您的位置:首页 > 理论基础 > 计算机网络

Linux下TCP网络服务器实现源代码3

2011-02-09 17:00 375 查看
用gcctcpforwardport.c-oMyProxy编译此程序后运行效果如下:

当有用户访问本机的8000端口时,MyProxy程序将把此请求转发到172.16.100.218主机的80端口,即实现了一个http代理。

关于select函数:

其函数原型为:

intselect(intn,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,structtimeval*timeout);

此函数的功能是由内核检测在timeout时间内,是否有readfds,writefds,exceptfds三个句柄集(filedescriptors)里的某个句柄(filedescriptor)的状态符合寻求,即readfds句柄集里有句柄可读或writefds句柄集里有可写或exceptfds句柄集里有例外发生,任何一个有变化函数就立即返回,返回值为timeout发生状态变化的句柄个数。

n是所有readfds,writefds,exceptfds三个句柄集(filedescriptors)里编号最大值加1。比如:要检测两个socket句柄fd1和fd2在timeout时间内是否分别可读和可写就可以这样:

先把两个句柄集(filedescriptors)清零:

FD_ZERO(&readfds);

FD_ZERO(&writefds);

然后把fd1加入读检测集:

FD_SET(fd1,&readfds);

然后把fd2加入写检测集:

FD_SET(fd2,&writefds);

再给timeout设置值,timeout是这样的一个结构:

structtimeval{

longtv_sec;/*seconds*/

longtv_usec;/*microseconds*/

};

你可以这样赋值:

timeout.tv_sec=1;

timeout.tv_uec=0;

表示检测在1秒钟内是否有句柄状态发生变化。

如果有句柄发生变化,就可以用FD_ISSET检测各个句柄,比如:

FD_ISSET(fd1,&readfds);//检测是否fd1变成可读的了

FD_ISSET(fd2,&writefds);//检测是否fd2变成可写的了

示意程序代码如下:

/*----------------------示意代码开始--------------------------------------------*/

fd1=socket();//创建一个socket

fd2=socket();//创建一个socket

while(1){

FD_ZERO(&readfds);

FD_ZERO(&writefds);

FD_SET(fd1,&readfds);

FD_SET(fd2,&writefds);

timeout.tv_sec=1;

timeout.tv_uec=0;

ret=select(fd1>fd2?(fd1+1):(fd2+1),&readfds,&writefds,NULL,&timeout);

if(ret<0){printf("系统错误,select出错,错误代码:%d,错误信息:%s",errno,strerror(errno));}

elseif(ret==0){printf("select超时返回,没有任何句柄状态发生变化!");}

//有句柄状态发生了变化

if(FD_ISSET(fd1,&readfds)){

fd1有数据可读;

fd1里的数据被读出来;

}

if(FD_ISSET(fd2,&writefds)){

fd2可写;

fd2里发送数据给对方;

}

}

/*----------------------示意代码结束--------------------------------------------*/

经常用到的几个自定义函数:

1、开启监听的函数

/*----------------------源代码代码开始--------------------------------------------*/

int

OpenSCPServer(intport,inttotal,intsendbuflen,intrecvbuflen,intblockORnot,intreuseORnot){

/*************************关于本函数************************************

*function_name:OpenSCPServer

*参数说明:port整数型监听端口号,total整数型监听个数,sendbuflen整数型发送缓冲区大小

*recvbuflen整数型接收缓冲区大小,blockORnot整数型是否阻塞,reuseORnot整数型是否端口重用

*purpose:用来建立一个tcp服务端socket

*tidiedby:zhoulifa(zhoulifa@163.com)周立发(http://zhoulifa.bokee.com)

Linux爱好者Linux知识传播者SOHO族开发者最擅长C语言

*datetime:2006-07-0520:00:00

*Note:任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

*但请遵循GPL

*Thanksto:PaulSheer感谢PaulSheer在select_tut的man手册里提供了这份源代码

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

*Note:要使用此函数需要自定义一个全局变量charerrorMessage[1024];并包含GetCurrentTime.h头文件

*********************************************************************/

intsockfd=0,ret=0,opt=0,flags=1;

structsockaddr_inladdr;

ret=sockfd=socket(PF_INET,SOCK_STREAM,0);

if(ret<0){

sprintf(errorMessage,"OpenTCPServersocket()error!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));

return-1;

}

ret=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&reuseORnot,sizeof(int));

if(ret<0){

sprintf(errorMessage,"OpenTCPServersetsockopt()reuseerror!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));

return-2;

}

ret=setsockopt(sockfd,SOL_SOCKET,SO_RCVBUF,&recvbuflen,sizeof(int));

if(ret<0){

sprintf(errorMessage,"OpenTCPServersetsockopt()recvbuferror!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));

return-3;

}

ret=setsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,&sendbuflen,sizeof(int));

if(ret<0){

sprintf(errorMessage,"OpenTCPServersetsockopt()sendbuferror!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));

return-4;

}

ioctl(sockfd,FIONBIO,&blockORnot);/*blockornot*/

laddr.sin_family=PF_INET;

laddr.sin_port=htons(port);

laddr.sin_addr.s_addr=INADDR_ANY;

bzero(&(laddr.sin_zero),8);

ret=bind(sockfd,(structsockaddr*)&laddr,sizeof(structsockaddr));

if(ret<0){

sprintf(errorMessage,"OpenTCPServerbind()error!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));

close(sockfd);

return-5;

}

ret=listen(sockfd,total);

if(ret<0){

sprintf(errorMessage,"OpenTCPServerlisten()error!return:%d,errno=%d,errortext:'%s'%s",ret,errno,strerror(errno),GetCurrentTime(0,0));

close(sockfd);

return-6;

}

sprintf(errorMessage,"OpenTCPServeropenedonport.%d(%d)OK,socket(%d),buf(%d:%d)!%s",port,total,sockfd,sendbuflen,recvbuflen,GetCurrentTime(0,0));

returnsockfd;

}

/*----------------------源代码代码结束--------------------------------------------*/

2、连接服务器的函数

/*----------------------源代码代码开始--------------------------------------------*/

int

ConnectSCPServer(char*serverip,intserverport,intblockORnot){

/*************************关于本函数************************************

*function_name:ConnectSCPServer

*参数说明:serverip服务器IP地址或主机名,serverport服务器端口,blockORnot整数型是否阻塞

*purpose:用来建立一个tcp客户端socket

*tidiedby:zhoulifa(zhoulifa@163.com)周立发(http://zhoulifa.bokee.com)

Linux爱好者Linux知识传播者SOHO族开发者最擅长C语言

*datetime:2006-07-0520:40:00

*Note:任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

*但请遵循GPL

*Thanksto:PaulSheer感谢PaulSheer在select_tut的man手册里提供了这份源代码

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

*Note:要使用此函数需要自定义一个全局变量charerrorMessage[1024];并包含自己编写的GetCurrentTime.h头文件

*********************************************************************/

intserversock=0,ret=0;

unsignedlongaddr;

structsockaddr_insin;

structhostent*he;

if((he=gethostbyname(serverip))==0){

sprintf(errorMessage,"ConnectSCPServerIPaddress'%s'error!return:-1%s",serverip,GetCurrentTime(0,0));

return-1;

}

serversock=socket(PF_INET,SOCK_STREAM,0);

if(serversock==-1){

sprintf(errorMessage,"ConnectSCPServersocket()error!return:-2,errno=%d,errortext:'%s'%s",errno,strerror(errno),GetCurrentTime(0,0));

return-2;

}

ioctl(serversock,FIONBIO,&blockORnot);//blockornot

memset((char*)&sin,0,sizeof(structsockaddr_in));

sin.sin_family=PF_INET;

sin.sin_port=htons(serverport);

sin.sin_addr=*((structin_addr*)he->h_addr);

ret=connect(serversock,(structsockaddr*)&sin,sizeof(sin));

if(ret==-1){

sprintf(errorMessage,"ConnectSCPServerconnect()error!return:-3,errno=%d,errortext:'%s'%s",errno,strerror(errno),GetCurrentTime(0,0));

close(serversock);

return-3;

}

returnserversock;

}

/*----------------------源代码代码结束--------------------------------------------*/

3、发送数据函数Send

/*----------------------源代码代码开始--------------------------------------------*/

int

Send(intsock,char*buf,size_tsize,intflag,inttimeout){

/*************************关于本函数************************************

*function_name:Send

*参数说明:sock整数型socket,buf待发送的内容,size要发送的大小,flag发送选项,timeout超时时间值

*purpose:用来通过一个socket在指定时间内发送数据

*tidiedby:zhoulifa(zhoulifa@163.com)周立发(http://zhoulifa.bokee.com)

Linux爱好者Linux知识传播者SOHO族开发者最擅长C语言

*datetime:2006-07-0520:58:00

*Note:任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

*但请遵循GPL

*Thanksto:PaulSheer感谢PaulSheer在select_tut的man手册里提供了这份源代码

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

*Note:要使用此函数需要自定义一个全局变量charerrorMessage[1024];并包含自己编写的GetCurrentTime.h头文件

*********************************************************************/

inti=0,ret=0,intretry=0;

structtimevaltival;

fd_setwritefds;

intmaxfds=0;

tival.tv_sec=timeout;

tival.tv_usec=0;

FD_ZERO(&writefds);

if(sock>0){

FD_SET(sock,&writefds);

maxfds=((sock>maxfds)?sock:maxfds);

}

else{

sprintf(errorMessage,"Sendsocket:%derror!return:-2%s",sock,GetCurrentTime(0,0));

return-2;

}

ret=select(maxfds+1,NULL,&writefds,NULL,&tival);

if(ret<=0){

if(ret<0)sprintf(errorMessage,"Sendsocket:%dselect()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));

elsesprintf(errorMessage,"Sendsocket:%dselecttimeout(%d)!%s",sock,timeout,GetCurrentTime(0,0));

close(sock);

return-3;

}

if(!(FD_ISSET(sock,&writefds))){

sprintf(errorMessage,"Sendsocket:%dnotinwritefds!%s",sock,GetCurrentTime(0,0));

close(sock);

return-4;

}

while(i<size){

ret=send(sock,buf+i,size-i,flag);

if(ret<=0){

sprintf(errorMessage,"Sendsocket:%dsend()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));

if(EINTR==errno)

if(intretry<10){intretry++;continue;}

elsesprintf(errorMessage,"Sendsocket:%dsend()error!EINTR10times!%s",sock,GetCurrentTime(0,0));

close(sock);

return-1;

}

elsei+=ret;

}

sprintf(errorMessage,"Sendsocket:%dsend()OK!%d/%dbytessent!%s",sock,i,size,GetCurrentTime(0,0));

returni;

}

/*----------------------源代码代码结束--------------------------------------------*/

4、接收数据函数Recv

/*----------------------源代码代码开始--------------------------------------------*/

int

Recv(intsock,char*buf,size_tsize,intflag,inttimeout){

/*************************关于本函数************************************

*function_name:Recv

*参数说明:sock整数型socket,buf接收数据的缓冲区,size要接收数据的大小,flag接收选项,timeout超时时间值

*purpose:用来从一个socket在指定时间内读取数据

*tidiedby:zhoulifa(zhoulifa@163.com)周立发(http://zhoulifa.bokee.com)

Linux爱好者Linux知识传播者SOHO族开发者最擅长C语言

*datetime:2006-07-0521:10:00

*Note:任何人可以任意复制代码并运用这些文档,当然包括你的商业用途

*但请遵循GPL

*Thanksto:PaulSheer感谢PaulSheer在select_tut的man手册里提供了这份源代码

*Hope:希望越来越多的人贡献自己的力量,为科学技术发展出力

*Note:要使用此函数需要自定义一个全局变量charerrorMessage[1024];并包含自己编写的GetCurrentTime.h头文件

*********************************************************************/

inti=0,ret=0,intretry=0;

structtimevaltival;

fd_setreadfds;

intmaxfds=0;

tival.tv_sec=timeout;

tival.tv_usec=0;

FD_ZERO(&readfds);

if(sock>0){

FD_SET(sock,&readfds);

maxfds=((sock>maxfds)?sock:maxfds);

}

else{

sprintf(errorMessage,"Recvsocket:%derror!return:-2%s",sock,GetCurrentTime(0,0));

return-2;

}

ret=select(maxfds+1,&readfds,NULL,NULL,&tival);

if(ret<=0){

if(ret<0)sprintf(errorMessage,"Recvsocket:%dselect()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));

elsesprintf(errorMessage,"Recvsocket:%dselecttimeout(%d)!%s",sock,timeout,GetCurrentTime(0,0));

close(sock);

return-3;

}

if(!(FD_ISSET(sock,&readfds))){

sprintf(errorMessage,"Recvsocket:%dnotinreadfds!%s",sock,GetCurrentTime(0,0));

close(sock);

return-4;

}

while(i<size){

ret=recv(sock,buf+i,size-i,flag);

if(ret<=0){

sprintf(errorMessage,"Recvsocket:%drecv()error!return:%d,errno=%d,errortext:'%s'%s",sock,ret,errno,strerror(errno),GetCurrentTime(0,0));

if(errno==EINTR)

if(intretry<10){intretry++;continue;}

elsesprintf(errorMessage,"Recvsocket:%drecv()error!EINTR10times!%s",sock,GetCurrentTime(0,0));

close(sock);

return-1;

}

elsei+=ret;

}

sprintf(errorMessage,"Recvsocket:%drecv()OK!%d/%dbytesreceived!%s",sock,i,size,GetCurrentTime(0,0));

returni;

}

最后需要说明的是:我这里讲到的源程序并不能实际地作为一个产品程序来用,实际情况下可能会有其它许多工作要做,比如可能要建立共享队列来存放socket里读到的消息,也可能把发送消息先进行排队然后再调用Send函数。还有,如果不是全数字,在发送前一定要htonl转换为网络字节序,同理接收到后一定要先ntohl由网络字节序转换为主机字节序,否则对方发送过来的0x00000001在你这里可能是0x00010000,因为高低位顺序不同。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: