1、linux环境下c语言书写socket服务器程序的步骤和注意事项在linux下用c语言编写socket服务器。首先分析需要解决的问题:1. Socket的处理机制。一般的处理方法是在服务端socket初始化和建立监听之后,开启一个while死循环。不断接受传递过来的socket链接请求,如果链接请求成功,开启一个子进程fork,在子进程中对socket进行处理。处理完毕之后,子进程结束退出。经本人测试研究发现了1中框架的几个问题,比较的突出。具体如下。2. 开启子进程容易产生僵尸进程。一开始的代码不完善,在子进程中exit后,父进程没有对其进行操作,导致了子进程成为僵尸进程,处理此问题有3种方
2、法,将在后面进行详细说明。3. Socket的处理容易不严谨,导致程序奔溃。Socket的处理要严谨,否则在程序的运行过程中会出现socket资源不能释放的问题。4. 日志文件的处理。在多进程(线程)处理中会面临一个日志文件不可共享写的问题。本文中的代码均为作者测试使用过,在本文的最后会附上所有代码,如有疑问或者需要可以联系作者,qq:398810897。具体问题分析:1. socket的基本框架(代码文件为server01.c)。在windows和linux下的socket编程基本上是一样的,没有多大的差别,都是遵循着 创建socket、定义socket(socket赋值)、绑定socket
3、、开启监听、socket收发、关闭socket的流程来的,在windows下一开始还需要初始化socket版本,在这里主要针对的linux环境下的socket,所以windows下的东西就不细说了。创建可以收发数据的socket的必须的步骤如下:/创建socket sock=socket(AF_INET,SOCK_STREAM,0); if(sock0) printf(socket errorn); return 0; printf(sock-ret=%dn,sock); /网络字节序 server.sin_family=AF_INET; server.sin_addr.s_addr=INAD
4、DR_ANY; server.sin_port=htons(6000);/.MYPORT bzero(&(server.sin_zero),8); setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int); /bind if(ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)0)/*bind() printf(bind errorn); return 0; printf(bind-ret=%dn,ret); ret=-9
5、; if(ret=listen(sock,100)0)/*listen() printf(listen errorn); return 0; printf(listen-ret=%dn,ret);在socket的创建以及初始化之后,做一个while(1)的死循环进行和客户端的链接。while(1) namelen=sizeof(struct sockaddr_in);/sockaddr_in:地址信息 if(msgsock=accept(sock,(struct sockaddr *)&client,&namelen)=(n=recv(sockettemp,data_buf,MAXLINE,0
6、) continue; else send(sockettemp,ch_send_back,strlen(ch_send_back),0); close(sockettemp); break; close(sockettemp); printf(SOCKET END =n); exit(0); 以上就是一个简单的socket服务器程序的框架了。2. 僵尸进程的处理。在程序运行之后,会出现如下的问题,就是fork了子进程之后,父进程便不再处理子进程,导致了僵尸进程的出现。如下图所示,会出现一个server01 的僵尸进程:为了避免这个问题的出现,现有四种方式可以处理。(1) 父进程通过wait和
7、waitpid等函数等待子进程结束,这会导致父进程挂起。执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。在这种情形下就不会产生defunct进程。(2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。(3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核
8、会回收,并不再给父进程发送信号(4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。不过子进程的回收还要自己做。 具体的测试结果:(1) 方法是可以避免僵尸进程的产生,但是效率很低,每一次fork了子进程,父进程都要等到子进程exit了才能继续运行,不适合在并发量很高的服务器程序上使用。(2) 在程序中定义一个signal的函数void proc_child(int SIGNO);然后在server监听了之后,和客户端链接之前,调用此函数signal(SIGCHLD, proc_child);函数的具
9、体实现如下:void proc_child(int SIGNO) int pid = -1; int stat; pid = waitpid(-1, &stat, WNOHANG);具体代码参考server02.c。试验结果还是比较满意,在单客户端访问时,效率几乎没什么影响,也不会产生僵尸进程,但是多个客户端,或者长期大量的链接的时候还是会产生僵尸进程。测试环境比较难以搭建。(3) 方法可以,而且简单容易实现,具体代码参考server03.c。(4) 没有测试,所以这里就只好不说了。3. 在测试的过程中发现了由于作者一开始对socket没有释放导致程序不能收发数据的现象。主要是在accept之
10、后,跳出了fork之后没有close造成的。4. 日志文件是一个比较麻烦的问题。一般情况下,日志要写到一起,只能一个一个的进程对文件进行操作,必然影响程序的并发性。这种情况下就需要建立一个缓冲区,将日志的数据保存到缓冲区中,然后在定时刷新,将缓冲区中的数据写到日志里面去。在windows下,作者比较喜欢使用一个动态链接库建立缓冲区的方法来实现,而在linux下,作者使用了一个比较简单的pipe(管道)的方法来实现这个。、具体的代码见server04.附录-代码Server01:/*/#include #include #include #include #include #include #i
11、nclude #include #include #include #include #define MAXLINE 4096int main() int sock;/server socket struct sockaddr_in server;/server socketaddr_in struct sockaddr_in client; char msgsock;/client socket int namelen;/socket addr length int bReuseaddr=1; int ret=-9; int client_id=0; /创建socket sock=socke
12、t(AF_INET,SOCK_STREAM,0); if(sock0) printf(socket errorn); return 0; printf(sock-ret=%dn,sock); /网络字节序 server.sin_family=AF_INET; server.sin_addr.s_addr=INADDR_ANY; server.sin_port=htons(6000);/.MYPORT bzero(&(server.sin_zero),8); setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,siz
13、eof(int); /bind if(ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)0)/*bind() printf(bind errorn); return 0; printf(bind-ret=%dn,ret); ret=-9; if(ret=listen(sock,100)0)/*listen() printf(listen errorn); return 0; printf(listen-ret=%dn,ret); while(1) namelen=sizeof(struct sockaddr_in);
14、/sockaddr_in:地址信息 if(msgsock=accept(sock,(struct sockaddr *)&client,&namelen)=(n=recv(sockettemp,data_buf,MAXLINE,0) continue; else send(sockettemp,ch_send_back,strlen(ch_send_back),0); close(sockettemp); break; close(sockettemp); printf(SOCKET END =n); exit(0); else/father close(msgsock); usleep(10
15、); return 0;Server02:/*/#include #include #include #include #include #include #include #include #include #include #include #define MAXLINE 4096void proc_child(int SIGNO) int pid = -1; int stat; pid = waitpid(-1, &stat, WNOHANG);int main() int sock;/server socket struct sockaddr_in server;/server soc
16、ketaddr_in struct sockaddr_in client; char msgsock;/client socket int namelen;/socket addr length int bReuseaddr=1; int ret=-9; int client_id=0; /创建socket sock=socket(AF_INET,SOCK_STREAM,0); if(sock0) printf(socket errorn); return 0; printf(sock-ret=%dn,sock); /网络字节序 server.sin_family=AF_INET; serve
17、r.sin_addr.s_addr=INADDR_ANY; server.sin_port=htons(6000);/.MYPORT bzero(&(server.sin_zero),8); setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int); /bind if(ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)0)/*bind() printf(bind errorn); return 0; printf(bin
18、d-ret=%dn,ret); ret=-9; if(ret=listen(sock,100)0)/*listen() printf(listen errorn); return 0; printf(listen-ret=%dn,ret); signal(SIGCHLD, proc_child); /signal(SIGCHLD, SIG_IGN); while(1) namelen=sizeof(struct sockaddr_in);/sockaddr_in:地址信息 if(msgsock=accept(sock,(struct sockaddr *)&client,&namelen)=(
19、n=recv(sockettemp,data_buf,MAXLINE,0) continue; else send(sockettemp,ch_send_back,strlen(ch_send_back),0); close(sockettemp); break; close(sockettemp); printf(SOCKET END =n); exit(0); else/father close(msgsock); usleep(10); return 0;void proc_child(int SIGNO) int pid = -1; int stat; pid = waitpid(-1
20、, &stat, WNOHANG);Server03:/*/#include #include #include #include #include #include #include #include #include #include #include #define MAXLINE 4096void proc_child(int SIGNO) int pid = -1; int stat; pid = waitpid(-1, &stat, WNOHANG);int main() int sock;/server socket struct sockaddr_in server;/serv
21、er socketaddr_in struct sockaddr_in client; char msgsock;/client socket int namelen;/socket addr length int bReuseaddr=1; int ret=-9; int client_id=0; /创建socket sock=socket(AF_INET,SOCK_STREAM,0); if(sock0) printf(socket errorn); return 0; printf(sock-ret=%dn,sock); /网络字节序 server.sin_family=AF_INET;
22、 server.sin_addr.s_addr=INADDR_ANY; server.sin_port=htons(6000);/.MYPORT bzero(&(server.sin_zero),8); setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int); /bind if(ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)0)/*bind() printf(bind errorn); return 0; printf(bind-ret=%