新客网WWW.XKER.COM:致力做中国最专业的网络学院!
学院: 操作系统 - 网络应用 - 服务器 - 网络安全 - 工具软件 - 办公软件 - Web开发 - 数据库 - 网页设计 - 图形图像 - 媒体动画 - 硬件学堂 - 存储频道 - QQ专区
您的位置:首页 > 软件开发 > 系统编程 > Linux编程 > 正文:socket编程原理 [精华]

socket编程原理 [精华]

新客网 XKER.COM 2008-04-18 来源:chinaunix lhp1210 收藏本文
在程序结束前关闭主套接字是一个好习惯 */ 



close(Mysock.DaemonSock); 

FD_CLR(Mysock.DaemonSock, &Mysock.readfds); 

FD_CLR(Mysock.DaemonSock, &Mysock.exceptfds); 




int CreateConnection(struct in_addr *sin_addr) 

/* Create a Connection to remote host which IP address is in sin_addr. 

Param: sin_addr indicates the IP address in Network Byte Order. 

if succeed return the socket number which indicates this connection, 

else return error code (<0) */ 



struct sockaddr_in server; /* server address */ 

int tmpsock, flag=1, i; 


if ((tmpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) 

return(-1); 


server.sin_family = AF_INET; 

server.sin_port = Mysock.Port; 

server.sin_addr.s_addr = sin_addr->;s_addr; 


/* Set this socket as a Non-blocking socket. */ 

if (ioctl(tmpsock, FIONBIO, &flag) == -1) { 

close(tmpsock); 

return(-2); 




/* Connect to the server. */ 

if (connect(tmpsock, (struct sockaddr *)&server, sizeof(server)) < 0) { 

if ((errno != EWOULDBLOCK) && (errno != EINPROGRESS)) { 

/* 如果错误代码是EWOULDBLOCK和EINPROGRESS,则不用关闭套接字,因为系统将在之后继续为套接字建立连接,连接是否建立成功可用select()函数来检测套接字是否“可写”来确定。*/ 

close(tmpsock); 

return(-3); /* Connect error. */ 






FD_SET(tmpsock, &Mysock.readfds); 

FD_SET(tmpsock, &Mysock.writefds); 

FD_SET(tmpsock, &Mysock.exceptfds); 


i = 0; 

while (Mysock.Sockets != 0) i++; /* look for a blank sockets position */ 

if (i >;= 64) { 

close(tmpsock); 

return(-4); /* too many connections */ 




Mysock.Sockets = tmpsock; 

Mysock.SockNum++; 

return(i); 




int AcceptConnection(struct in_addr *IPaddr) 

/* Accept a connection. If succeed, return the data sockets number, else return -1. */ 



int newsock, len, flag=1, i; 

struct sockaddr_in addr; 


len = sizeof(addr); 

bzero((char *)&addr, len); 

if ((newsock = accept(Mysock.DaemonSock, &addr, &len)) == -1) 

return(-1); /* Accept error. */ 


/* Set this socket as a Non-blocking socket. */ 

ioctl(newsock, FIONBIO, &flag); 


FD_SET(newsock, &Mysock.readfds); 

FD_SET(newsock, &Mysock.writefds); 

FD_SET(newsock, &Mysock.exceptfds); 


/* Return IP address in the Parameter. */ 

IPaddr->;s_addr = addr.sin_addr.s_addr; 


i = 0; 

while (Mysock.Sockets != 0) i++; /* look for a blank sockets position */ 

if (i >;= 64) { 

close(newsock); 

return(-4); /* too many connections */ 




Mysock.Sockets = newsock; 

Mysock.SockNum++; 

return(i); 




int CloseConnection(int Sockno) 

/* Close a connection indicated by Sockno. */ 



int retcode; 


if ((Sockno >;= 64) || (Sockno < 0) || (Mysock.Sockets[Sockno] == 0)) 

return(0); 


retcode = close(Mysock.Sockets[Sockno]); 

FD_CLR(Mysock.Sockets[Sockno], &Mysock.readfds); 

FD_CLR(Mysock.Sockets[Sockno], &Mysock.writefds); 

FD_CLR(Mysock.Sockets[Sockno], &Mysock.exceptfds); 


Mysock.Sockets[Sockno] = 0; 

Mysock.SockNum--; 

return(retcode); 




int QuerySocketsMsg() 

/* Query Sockets Message. If succeed return message number, else return -1. 

The message information stored in struct SockMsg. */ 



fd_set rfds, wfds, efds; 

int retcode, i; 

struct timeval TimeOut; 


rfds = Mysock.readfds; 

wfds = Mysock.writefds; 

efds = Mysock.exceptfds; 

TimeOut.tv_sec = 0; /* 立即返回,不阻塞。*/ 

TimeOut.tv_usec = 0; 


bzero((char *)&SockMsg, sizeof(SockMsg)); 

if ((retcode = select(64, &rfds, &wfds, &efds, &TimeOut)) == 0) 

return(0); 


if (FD_ISSET(Mysock.DaemonSock, &rfds)) 

SockMsg.AcceptNum = 1; /* some client call server. */ 


for (i=0; i<64; i++) /* Data in message */ 



if ((Mysock.Sockets >; 0) && (FD_ISSET(Mysock.Sockets, &rfds))) 

SockMsg.ReadQueue[SockMsg.ReadNum++] = i; 




for (i=0; i<64; i++) /* Data out ready message */ 



if ((Mysock.Sockets >; 0) && (FD_ISSET(Mysock.Sockets, &wfds))) 

SockMsg.WriteQueue[SockMsg.WriteNum++] = i; 




if (FD_ISSET(Mysock.DaemonSock, &efds)) 

SockMsg.AcceptNum = -1; /* server socket error. */ 


for (i=0; i<64; i++) /* Error message */ 



if ((Mysock.Sockets >; 0) && (FD_ISSET(Mysock.Sockets, &efds))) 

SockMsg.ExceptQueue[SockMsg.ExceptNum++] = i; 



return(retcode); 




int SendPacket(int Sockno, void *buf, int len) 

/* Send a packet. If succeed return the number of send data, else return -1 */ 



int actlen; 


if ((Sockno >;= 64) || (Sockno < 0) || (Mysock.Sockets[Sockno] == 0)) 

return(0); 


if ((actlen = send(Mysock.Sockets[Sockno], buf, len, 0)) < 0) 

return(-1); 

return(actlen); 




int RecvPacket(int Sockno, void *buf, int size) 

/* Receive a packet. If succeed return the number of receive data, else if the connection 

is shutdown by peer then return 0, otherwise return 0-errno */ 



int actlen; 

if ((Sockno >;= 64) || (Sockno < 0) || (Mysock.Sockets[Sockno] == 0)) 

return(0); 

if ((actlen = recv(Mysock.Sockets[Sockno], buf, size, 0)) < 0) 

return(0-errno); 

return(actlen); /* actlen是接收的数据长度,如果为零,指示连接被对方关闭。*/ 




2.5.3 简单服务器程序示例 
/* File Name: server.c */ 

/* 这是一个很简单的重复服务器程序,它初始化好被动套接字后,循环等待接收连接。如果接收到连接,它显示数据套接字序号和客户端的IP地址;如果数据套接字上有数据到来,它接收数据并显示该连接的数据套接字序号和接收到的字符串。*/ 

#include "tcpsock.h" 

main(argc, argv) 

int argc; 

char **argv; 



struct in_addr sin_addr; 

int retcode, i; 

char buf[32];
/* 对于服务器程序,它经常是处于无限循环状态,只有在用户主动kill该进程或系统关机时,它才结束。对于使用kill强行终止的服务器程序,由于主套接字没有关闭,资源没有主动释放,可能会给随后的服务器程序重新启动产生影响。因此,主动关闭主套接字是一个良好的变成习惯。下面的语句使程序在接收到SIGINT、SIGQUIT和SIGTERM等信号时先执行CloseMainSock()函数关闭主套接字,然后再结束程序。因此,在使用kill强行终止服务器进程时,应该先使用kill -2 PID给服务器程序一个消息使其关闭主套接字,然后在用kill -9 PID强行结束该进程。*/ 

(void) signal(SIGINT, CloseMainSock); 

(void) signal(SIGQUIT, CloseMainSock); 

(void) signal(SIGTERM, CloseMainSock); 


if ((retcode = InitPassiveSock("TestService")) < 0) { 

printf("InitPassiveSock: error code = %d\n", retcode); 

exit(-1); 




while (1) { 

retcode = QuerySocketsMsg(); /* 查询网络消息 */ 

if (SockMsg.AcceptNum == 1) { /* 有外来连接等待接收?*/ 

retcode = AcceptConnection(&sin_addr); 

printf("retcode = %d, IP = %s \n", retcode, inet_ntoa(sin_addr.s_addr)); 



else if (SockMsg.AcceptNum == -1) /* 主套接字错误?*/ 

printf("Daemon Sockets error.\n"); 

for (i=0; i 
if ((retcode = RecvPacket(SockMsg.ReadQueue, buf, 32)) >; 0) 

printf("sockno %d Recv string = %s \n", SockMsg.ReadQueue, buf); 

else /* 返回数据长度为零,指示连接中断,关闭套接字。*/ 

CloseConnection(SockMsg.ReadQueue); 



} /* end while */ 



2.5.4 简单客户程序示例 
/* File Name: client.c */ 

/* 客户程序在执行时,先初始化数据结构,然后等待用户输入命令。它识别四个命令: 

conn(ect): 和服务器建立连接; 

send: 给指定连接发送数据; 

clos(e): 关闭指定连接; 

quit: 退出客户程序。 

*/ 

#include "tcpsock.h" 


main(argc, argv) 

int argc; 

char **argv; 



char cmd_buf[16]; 

struct in_addr sin_addr; 

int sockno1, retcode; 

char *buf = "This is a string for test."; 


sin_addr.s_addr = inet_addr("166.111.5.249"); /* 运行服务器程序的主机的IP地址 */ 


if ((retcode = InitSocketsStruct("TestService")) < 0) { /* 初始化数据结构 */ 

printf("InitSocketsStruct: error code = %d\n", retcode); 

exit(1); 




while (1) { 

printf(">;"); 

gets(cmd_buf); 

if (!strncmp(cmd_buf, "conn", 4)) { 

retcode = CreateConnection(&sin_addr); /* 建立连接 */ 

printf("return code: %d\n", retcode); 



else if(!strncmp(cmd_buf, "send", 4)) { 

printf("Sockets Number:"); 

scanf("%d", &sockno1); 

retcode = SendPacket(sockno1, buf, 26); /* 发送数据 */ 

printf("return code: %d\n", retcode, sizeof(buf)); 



else if (!strncmp(cmd_buf, "close", 4)) { 

printf("Sockets Number:"); 

scanf("%d", &sockno1); 

retcode = CloseConnection(sockno1); /* 关闭连接 */ 

printf("return code: %d\n", retcode); 



else if (!strncmp(cmd_buf, "quit", 4)) 

exit(0); 

else 

putchar('\007'); 

} /* end while */ 





 

----------------------------------------------
收藏】 【评论】 【推荐】 【投稿】 【打印】 【关闭
发表评论
要记得去论坛讨论,点击注册新会员匿名评论
评论内容:不能超过250字,需审核后才会公布,请自觉遵守互联网相关政策法规。