linux环境下c语言书写socket服务器程序的步骤和注意事项.docx
- 文档编号:30700395
- 上传时间:2023-08-19
- 格式:DOCX
- 页数:28
- 大小:31.46KB
linux环境下c语言书写socket服务器程序的步骤和注意事项.docx
《linux环境下c语言书写socket服务器程序的步骤和注意事项.docx》由会员分享,可在线阅读,更多相关《linux环境下c语言书写socket服务器程序的步骤和注意事项.docx(28页珍藏版)》请在冰豆网上搜索。
linux环境下c语言书写socket服务器程序的步骤和注意事项
在linux下用c语言编写socket服务器。
首先分析需要解决的问题:
1.Socket的处理机制。
一般的处理方法是在服务端socket初始化和建立监听之后,开启一个while死循环。
不断接受传递过来的socket链接请求,如果链接请求成功,开启一个子进程fork,在子进程中对socket进行处理。
处理完毕之后,子进程结束退出。
经本人测试研究发现了1中框架的几个问题,比较的突出。
具体如下。
2.开启子进程容易产生僵尸进程。
一开始的代码不完善,在子进程中exit后,父进程没有对其进行操作,导致了子进程成为僵尸进程,处理此问题有3种方法,将在后面进行详细说明。
3.Socket的处理容易不严谨,导致程序奔溃。
Socket的处理要严谨,否则在程序的运行过程中会出现socket资源不能释放的问题。
4.日志文件的处理。
在多进程(线程)处理中会面临一个日志文件不可共享写的问题。
本文中的代码均为作者测试使用过,在本文的最后会附上所有代码,如有疑问或者需要可以联系作者,qq:
398810897。
具体问题分析:
1.socket的基本框架(代码文件为server01.c)。
在windows和linux下的socket编程基本上是一样的,没有多大的差别,都是遵循着创建socket、定义socket(socket赋值)、绑定socket、开启监听、socket收发、关闭socket的流程来的,在windows下一开始还需要初始化socket版本,在这里主要针对的linux环境下的socket,所以windows下的东西就不细说了。
创建可以收发数据的socket的必须的步骤如下:
//创建socket
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socketerror\n");
return0;
}
printf("sock-ret=[%d]\n",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,(constchar*)&bReuseaddr,sizeof(int));
//bind
if((ret=bind(sock,(structsockaddr*)&server,sizeof(structsockaddr)))<0)////****bind()
{
printf("binderror\n");
return0;
}
printf("bind-ret=[%d]\n",ret);
ret=-9;
if((ret=listen(sock,100)<0))////****listen()
{
printf("listenerror\n");
return0;
}
printf("listen-ret=[%d]\n",ret);
在socket的创建以及初始化之后,做一个while
(1)的死循环进行和客户端的链接。
while
(1)
{
namelen=sizeof(structsockaddr_in);//sockaddr_in:
地址信息
if((msgsock=accept(sock,(structsockaddr*)&client,&namelen))<0)////****accept()
{
printf("accepterror\n");
continue;
}
else
{。
。
。
。
。
。
。
。
。
。
。
。
}
。
。
。
。
。
。
。
。
。
}
如果有客户端链接成功,即开启一个子进程来处理相关的流程。
在else中处理这个问题,在这里把else里面的代码贴出来。
else
{
printf("socketacceptfromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);
printf("SOCKETBEGIN===================\n");
if(!
fork())//child
{
intsockettemp=msgsock;
while
(1)
{
chardata_buf[MAXLINE];//socketrecv
charch_send_back[300];//socketback
intn=0;
memset(data_buf,0,MAXLINE);
if(0>=(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("SOCKETEND===================\n");
exit(0);
}
以上就是一个简单的socket服务器程序的框架了。
2.僵尸进程的处理。
在程序运行之后,会出现如下的问题,就是fork了子进程之后,父进程便不再处理子进程,导致了僵尸进程的出现。
如下图所示,会出现一个[server01]
为了避免这个问题的出现,现有四种方式可以处理。
(1)父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。
执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。
在这种情形下就不会产生defunct进程。
(2)如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。
在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。
(3)如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD,SIG_IGN)或signal(SIGCHLD,SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号
(4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。
不过子进程的回收还要自己做。
具体的测试结果:
(1)方法是可以避免僵尸进程的产生,但是效率很低,每一次fork了子进程,父进程都要等到子进程exit了才能继续运行,不适合在并发量很高的服务器程序上使用。
(2)在程序中定义一个signal的函数voidproc_child(intSIGNO);
然后在server监听了之后,和客户端链接之前,调用此函数signal(SIGCHLD,proc_child);函数的具体实现如下:
voidproc_child(intSIGNO)
{
intpid=-1;
intstat;
pid=waitpid(-1,&stat,WNOHANG);
}
具体代码参考server02.c。
试验结果还是比较满意,在单客户端访问时,效率几乎没什么影响,也不会产生僵尸进程,但是多个客户端,或者长期大量的链接的时候还是会产生僵尸进程。
测试环境比较难以搭建。
(3)方法可以,而且简单容易实现,具体代码参考server03.c。
(4)没有测试,所以这里就只好不说了。
3.在测试的过程中发现了由于作者一开始对socket没有释放导致程序不能收发数据的现象。
主要是在accept之后,跳出了fork之后没有close造成的。
4.日志文件是一个比较麻烦的问题。
一般情况下,日志要写到一起,只能一个一个的进程对文件进行操作,必然影响程序的并发性。
这种情况下就需要建立一个缓冲区,将日志的数据保存到缓冲区中,然后在定时刷新,将缓冲区中的数据写到日志里面去。
在windows下,作者比较喜欢使用一个动态链接库建立缓冲区的方法来实现,而在linux下,作者使用了一个比较简单的pipe(管道)的方法来实现这个。
、
具体的代码见server04.
附录-代码
Server01:
/*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineMAXLINE4096
intmain()
{
intsock;//serversocket
structsockaddr_inserver;//serversocketaddr_in
structsockaddr_inclient;
charmsgsock;//clientsocket
intnamelen;//socketaddrlength
intbReuseaddr=1;
intret=-9;
intclient_id=0;
//创建socket
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socketerror\n");
return0;
}
printf("sock-ret=[%d]\n",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,(constchar*)&bReuseaddr,sizeof(int));
//bind
if((ret=bind(sock,(structsockaddr*)&server,sizeof(structsockaddr)))<0)////****bind()
{
printf("binderror\n");
return0;
}
printf("bind-ret=[%d]\n",ret);
ret=-9;
if((ret=listen(sock,100)<0))////****listen()
{
printf("listenerror\n");
return0;
}
printf("listen-ret=[%d]\n",ret);
while
(1)
{
namelen=sizeof(structsockaddr_in);//sockaddr_in:
地址信息
if((msgsock=accept(sock,(structsockaddr*)&client,&namelen))<0)////****accept()
{
printf("accepterror\n");
continue;
}
else
{
printf("socketacceptfromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);
printf("SOCKETBEGIN===================\n");
if(!
fork())//child
{
intsockettemp=msgsock;
while
(1)
{
chardata_buf[MAXLINE];//socketrecv
charch_send_back[300];//socketback
intn=0;
memset(data_buf,0,MAXLINE);
if(0>=(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("SOCKETEND===================\n");
exit(0);
}
else//father
{
close(msgsock);
}
}
usleep(10);
}
return0;
}
Server02:
/*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineMAXLINE4096
voidproc_child(intSIGNO)
{
intpid=-1;
intstat;
pid=waitpid(-1,&stat,WNOHANG);
}
intmain()
{
intsock;//serversocket
structsockaddr_inserver;//serversocketaddr_in
structsockaddr_inclient;
charmsgsock;//clientsocket
intnamelen;//socketaddrlength
intbReuseaddr=1;
intret=-9;
intclient_id=0;
//创建socket
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socketerror\n");
return0;
}
printf("sock-ret=[%d]\n",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,(constchar*)&bReuseaddr,sizeof(int));
//bind
if((ret=bind(sock,(structsockaddr*)&server,sizeof(structsockaddr)))<0)////****bind()
{
printf("binderror\n");
return0;
}
printf("bind-ret=[%d]\n",ret);
ret=-9;
if((ret=listen(sock,100)<0))////****listen()
{
printf("listenerror\n");
return0;
}
printf("listen-ret=[%d]\n",ret);
signal(SIGCHLD,proc_child);
//signal(SIGCHLD,SIG_IGN);
while
(1)
{
namelen=sizeof(structsockaddr_in);//sockaddr_in:
地址信息
if((msgsock=accept(sock,(structsockaddr*)&client,&namelen))<0)////****accept()
{
printf("accepterror\n");
continue;
}
else
{
printf("socketacceptfromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);
printf("SOCKETBEGIN===================\n");
if(!
fork())//child
{
intsockettemp=msgsock;
while
(1)
{
chardata_buf[MAXLINE];//socketrecv
charch_send_back[300];//socketback
intn=0;
memset(data_buf,0,MAXLINE);
if(0>=(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("SOCKETEND===================\n");
exit(0);
}
else//father
{
close(msgsock);
}
}
usleep(10);
}
return0;
}
voidproc_child(intSIGNO)
{
intpid=-1;
intstat;
pid=waitpid(-1,&stat,WNOHANG);
}
Server03:
/*
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#defineMAXLINE4096
voidproc_child(intSIGNO)
{
intpid=-1;
intstat;
pid=waitpid(-1,&stat,WNOHANG);
}
intmain()
{
intsock;//serversocket
structsockaddr_inserver;//serversocketaddr_in
structsockaddr_inclient;
charmsgsock;//clientsocket
intnamelen;//socketaddrlength
intbReuseaddr=1;
intret=-9;
intclient_id=0;
//创建socket
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
printf("socketerror\n");
return0;
}
printf("sock-ret=[%d]\n",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,(constchar*)&bReuseaddr,sizeof(int));
//bind
if((ret=bind(sock,(structsockaddr*)&server,sizeof(structsockaddr)))<0)////****bind()
{
printf("binderror\n");
return0;
}
printf("bind-ret=[%
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- linux 环境 语言 书写 socket 服务器 程序 步骤 注意事项