linuxc网络编程(linux c语言网络编程)
想在LINUX下学习C语言,该如何开始?
一、工具篇
“公欲善其事,必先利其器”。编程是一门实践性很强的工作,在你以后的学习或工作中,你将常常会与以下工具打交道, 下面列出学习C语言编程常常用到的软件和工具。
1、操作系统
在UNIX或Linux系统中学习C很方便,所以在开始您的学习旅程前请先选择一个UNIX或Linux操作系统,目前可供个人免费使用的UNIX或Linux系统有FreeBSD、RedHat Linux、SUSE Linux等,而且在安装包中还提供很多实用的工具,如:gcc, make等。
如果您一直使用Windows,身边又没有多余的机器安装UNIX,则可以使用VMware,通过VMware安装虚拟系统。
2、编译工具
目前绝大多数Unix或Lnux系统都提供CC或GCC编译器,最简单的cc命令格式如下:
cc -o hello hello.c
在unix shell环境中敲入上面的代码会将hello.c程序编译成可执行文件hello。
3、make工具
如 GNU make、System V make 和 Berkeley make 是用来组织应用程序编译过程的基本工具,但是每个 make 工具之间又有所不同。
大部分UNIX和Linux程序都是通过运行make来编译的。
make工具会读取一个包含指令的文件(这个文件的名字通常都是 makefile 或 Makefile,不过后文中我们统一称之为 “makefile”),并执行各种操作来编译程序。
4、调试工具
最简单的调试工具:为你的程序添加打印语句,在你对程序的运行机制有了一定的了解后,你可以实用一些工具帮助你进行调试,当然你得学习一下这些工具得使用,如:dbx,gdb等。
还有一些内存工具可以帮你查找内存泄漏或缓冲区溢出等一些问题,如:memwatch,yamd等。
5、其他工具
1)vi或vim
Unix下文本编辑器。主要靠一堆命令来编辑文本文件,学Unix编程最好熟悉并熟练使用vi编辑器。
当然在实际工作中,你可能需要一个集成编码环境或一个功能强大的图形化编辑工具。
提供一个中文的vim在线手册:
2)Secure shell/putty
一个支持ssh协议得客户端工具,多数情况下用来连接linux系统。
二、书籍篇
“书是人类进步得阶梯”。学习一门新的知识,当然要选择几本适合自己得书籍,下面介绍一些我自己学习C语言使用过的书籍:
1.《C primer plus》
推荐理由:适合作为入门书和基本函数查询得参考资料。本书最新版为第五版,以ANSI C99为标准详细介绍了C语言。
2.《The C programming_Language》
推荐理由:C语言之父得作品权威性毋庸置疑。虽然书籍出版时间比较老,好像也没更新,不过仍不失为经典书籍,网上有这本书得英文电子版提供下载。
3.《C 专家编程》
推荐理由:本书可以帮助有一定经验的C程序员成为C编程方面的专家,最关键的是本书寓教于乐,让你充分享受编程的乐趣。
4.《C缺陷与陷阱》
推荐理由:书中所揭示的知识能帮助您绕过C语言自身得陷阱和缺陷,减少代码中许多常见的Bug。
5.《unix环境高级编程》
推荐理由:既然是UNIX环境下C编程,就不得不说说UNIX编程书籍。
Stevens先生的《unix环境高级编程》是我竭力推荐的,也是我的案头必备(如果对网络编程有兴趣的,可以学习一下Stevens先生的《UNIX网络编程》两卷,如果觉得还不过瘾,可以再看看《TCP/IP详解》三卷)。
6.《计算机编程艺术》
推荐理由:算法大师得呕心沥血之作。计划出版五卷书,目前好像已出版3卷。对算法有兴趣得可以研究一下。
三、过程篇
1.学习C语法
语法的学习对于一个具有编程底子的来说,就很轻松了;即使你以前没有学习过其他编程语言,我相信有2个星期,你也能轻松搞定。
需要注意的是,不要太纠缠于语言的细节,比如:运算符优先级与结合性的问题等。
2.学习C标准库
ANSI C库把函数分为不同的组,每个组都具有与之相关的头文件。C语言标准库相对于其他语言,比如C++,Java来说是非常短小精悍的,但首先应着重对以下库进行学习:
ctype.h:字符处理
math.h:数学库
stdio.h:标准I/O库
stdlib.h:通用工具库
string.h:字符串处理
time.h:时间和日期
如果想了解完成的ANSI C库,你可以购买相关的书籍,这些书籍一般会详细介绍每个函数的用户和一些注意点;当然你也可以登陆 ... amp;page=index.html获取ANSI C库详细信息。
3.攻克C的难点
1)C语言声明:
C语言的声明确实让我觉得恐怖,比较晦涩难懂,而且声明的形式和使用的形式还类似。比如如下的声明恐怕就连很多熟悉C多年的程序员也不是一眼就能看出来的:
char * const * (*next)();
那么有没有一种好的记忆方法或规则来搞清楚呢,好像没有,如果有的话也不是这样折磨人了。不过可以看看《C专家编程》第三章的内容,或许你会有所收获。
也只能多学多练了,所谓熟能生巧嘛,希望这个问题不要在你的心灵上留下阴影。
2)数组与指针:
数组与指针的关系,在标准中并没有作很详细的规定,而且好多C入门的书籍在这个问题上并没有给出很详细的说明,所以会给人造成很多误解。
对于这个问题,你可以参考《C缺陷与陷阱》4.5节和《C专家编程》第4,9,10章,相信你这里面的内容搞透彻,以后就不会再被这个问题搞迷惑。
3)指针与内存:
如果你以后编写规模较大的程序,你可能发现这个问题可能会是你最大的烦恼,而且可能会是你消耗最多调试时间的事项。
4)C版本的问题:
你得特别小心该问题,最好不要在你的程序中混合使用不同版本C的特性,否则会给你带来很迷惑的问题。如果一定要用,你最好清楚自己在做什么。
还有一些其他C中的难点和容易错误的地方,可以学习前人的一些经验。以下是一个c FAQ的链接地址,相信在这篇文档中有你需要的大部分问题的解决方法。
4. UNIX环境编程
学习了以上内容之后,我相信,你就可以进行unix环境编程了。不过你可能需要对操作系统理论有一点点的了解,这样学起来会比较轻松一些。
Unix环境编程,你应该着重IO和进程两大块内容。
《Unix环境高级编程》中对Unix环境编程有着非常详细且深入的论述,而且书中有大量实用性例子程序,不过可能得花上几个月得时间,好好啃一啃了。
在扎实掌握以上内容,不代表你得C语言学习支路已经完成,相反,才刚刚开始。以后你需要用学到得知识去解决大量不同实际问题,在不断得实践过程中,你会近一步加深对C的理解。有了以上基础之后,你会发现,在实践过程中需要的其他知识,你会非常快速的掌握。
C语言中(void **)&a 是什么意思?其中 a定义为 指向结构体的指针,且初始化为NULL,LINUX网络编程中
a作用是取得a的地址,由于a已经是一个指针,所以a就是一个指针的指针,即是一个二维指针,然后用(void **)把这个二维指针转换为空类型二维指针,即void **类型的变量。
linux C网络编程调试出现错误,求大神指点
你可以检查一下allset初始化的地方,初始化没问题那就检查一下listenfd,如果listenfd指向不对比如为负数的时候,会在select轮询的时候发生段错误,调试过程可以把listenfd的值打印一下,gdb本身也是可以查看内存的值的
linux下C语言用socket网络编程怎么计算传输速度?
这要你的通信程序协商一个协议,比如定义一个通信结构体,传文件的时候,一开始发送结构体的信息过去,告诉对端你的文件总大小,然后,传输过程中,统计已经收到或者发送的数据,做个除法就得到速率了。
具体这类协商,你可以自己随便想,也可以借鉴现有的比较好的一些设计,有些考虑断点续传的技术,还有压缩的,看你代码也不需要考虑吧。
linux网络编程中如何实现服务器端多个read()和客户端write( )
TCP通信的模式如下图,比较固定,对着图编代码就可以了:
服务器的main函数:
int main(int argc, char **argv)
{
?int listenfd, connfd;
?pid_t childpid;
?socklen_t clilen;
?struct sockaddr_in cliaddr, servaddr; //IPv4 address
?/*socket*/
?listenfd = socket(AF_INET, SOCK_STREAM, 0);//创建一个TCP的socket
?if (-1 == listenfd) {
? perror("socket erro.");
? return -1;
?}
?/*bind*/
?//首先初始化server的IP地址和端口,然后再与刚刚创建的socket绑定
?bzero(servaddr, sizeof(servaddr));
?servaddr.sin_family = AF_INET;//设置协议簇
?servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的网卡
?servaddr.sin_port = htons(1234);//绑定端口号,端口号可以随便取,大于1024就可以了
?if (-1 == bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr))) {
? perror("bind error.");
? return -1;
?}
?/*listen*/
?//到这里已经有了一个绑定了IP地址和端口号的socket了,但是这个socket是个主动的socket,
?//而作为server需要的是一个等待别的接入的被动的socket,所以得调用listen将这个socket设置为监听状态
?//第二个参数表示服务器正在处理客户接入时的等待队列长度。
?if (-1 == listen(listenfd, 10)) {
? perror("listen error.");
? return -1;
?}
?while (1) {
? clilen = sizeof(cliaddr);
? //调用accept等待客户的接入,同时accept会用第二个参数返回客户的IP地址,
? //通过第三个参数返回IP地址的实际大小,同时这个参数也是个值-结构参数,也就是
? //在传递这个参数的时候,先给这个参数一个初始的值,然后函数中会根据具体的情况修改这个值,
? //所以这里传递的是指针。
? //当客户接入后,将返回一个成功和客服连接的socket描述符,通过读写这个socket即可实现和客户的通信了
? connfd = accept(listenfd, (struct sockaddr *)cliaddr, clilen);
? if (-1 == connfd) {
? ?if (EINTR == errno)
? ? continue;
? ?perror("accept error.");
? ?return -1;
? }
? //通过fock创建子进程来处理客户请求,这里只是显示客户的IP地址、端口号和发送的文字,
? //并将客户发送的文字回传给客户。
? if (0 == (childpid=fork())) {//fock返回0 说明是子进程
? ?//这里并没有关闭服务器的监听socket,只是将其引用计数减一,因为fork出来的子进程对父进程做了拷贝,
? ?//所以这个监听socket的引用计数将由1变成2,而内核只有在一个socket的引用计数变为0才回去关闭它
? ?close(listenfd);
? ?//通过和客户连接的socket和客户通信? ? ?
? ?str_echo(connfd, cliaddr);
? ?return 0;
? }
? //父进程将和客户连接的socket的引用计数减一,同样并没有关闭这个socket
? close(connfd);
?}
?return 0;
}
服务器处理客户请求:
#define BSIZE 100
void str_echo(int sockfd, struct sockaddr_in cliaddr)
{
?ssize_t n;
?char buf[BSIZE];
?
?while ((n=read(sockfd, buf, BSIZE))) {//读取客户发送的信息
? buf[n] = '\0';
? printf("IP: %s, PORT: %d: %s\n", \
? ? inet_ntoa(cliaddr), ntohs(cliaddr.sin_port), buf); //输出信息
? n_write(sockfd, buf, n);//将受到的信息发送回客户,n_write的实现下面给出
?}
}
客户端程序相对简单:
int main(int argc, char **argv)
{
?int sockfd;
?struct sockaddr_in servaddr;
?
?if (2 != argc) {
? printf("usage: ./client 127.0.0.1");//
? return -1;
?}
?/*socket*/
?sockfd = socket(AF_INET, SOCK_STREAM, 0);?//创建一个socket
?if (-1 == sockfd) {
? perror("socket error.");
? return -1;
?}
?/*connect*/
?//首先初始化要连接的服务器的IP地址和端口号,然后调用connect去连接这个服务器
?bzero(servaddr, sizeof(servaddr));
?servaddr.sin_family = AF_INET;
?servaddr.sin_port = htons(1234);
?inet_pton(AF_INET, argv[1], servaddr.sin_addr);
?if (-1 == connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr))) {
? perror("connect error.");
? return -1;
?}
?//连接成功后就可以通过这个socket来和服务器通信了
?str_cli(stdin, sockfd);
return 0;
}
n_write 和 readline
/*write n bytes to fd*/
ssize_t n_write (int fd, void *buf, size_t n)
{
?size_t nleft = n;
?ssize_t nwriten;
?char *bufp = buf;
while (nleft 0) {
? if ((nwriten = write (fd, bufp, nleft)) = 0) {
? ?if (EINTR == errno)
? ? nwriten = 0;
? ?else
? ? return -1;
? }
nleft -= nwriten;
? bufp += nwriten;
?}
return n;
}
/*read line from fd*/
ssize_t readline (int fd, void *buf, size_t maxlen)
{
?ssize_t n, rc;
?char c, *bufp;
bufp = buf;
?for (n = 1; n maxlen; n ++) {
again:
? if (1 == (rc = read (fd, c, 1))) {
? ?*bufp ++ = c;
? ?if ('\n' == c)
? ? break;? /*newline is stored*/
? } else if (rc == 0) {
? ?*bufp = 0;
? ?return (n - 1);? /*EOF, n-1 bytes were read*/
? } else {
? ?if (EINTR == errno)?/*interrupt*/
? ? goto again;
? ?return -1;? /*Erro, set the errno by read ()*/
? }
?}
?*bufp = 0;
?return n;
}
运行结果:
因为客户端没有指定IP地址和端口,所以其IP和端口都是内核随机分配的。