Linux TCP协议的基础编程模型(2)

附:在TCP通信模型中,服务器端进程需要完成创建套接字(socket)、命名套接字(bind)、侦听接收(listen)等一系列操作后才能够调用accept接收客户端连接请求,因此在服务器端这三个函数常常连续调用。为了简化编程,提高代码重用度,本处设计了一个封装以上三种操作的函数CreateSock。

int CreateSock(int* pSock, int nPort, int nMax)

{

struct sockaddr_in addrin;

struct sockaddr *paddr = (struct sockaddr *)&addrin;

assert(pSock != NULL && nPort > 0 && nMax > 0);

memset(&addrin, 0, sizeof(addrin));

/*协议地址组包*/

addrin.sin_family = AF_INET;                         /*协议名*/

addrin.sin_addr.s_addr = htonl(INADDR_ANY);  /*自动分配地址*/

addrin.sin_port = htons(nPort);                        /*端口号*/

/*创建TCP套接字描述符*/

assert((*pSock = socket(AF_INET, SOCK_STREAM, 0)) > 0);  /*命名套接字*/

/*套接字进入侦听状态*/

if ((bind(*pSock, paddr, sizeof(addrin)) >= 0) && (listen(*pSock, nMax) >= 0))    

return 0;                                            /*准备成功,正确返回*/

(close(*pSock) == 0);          /* 准备失败,关闭套接字描述符 */

return 1;

}

说明:pSock回传创建的侦听套接字描述符;nPort指定套接字的侦听端口;nMax指定侦听套接字的最大连接数。本函数调用成功时完成套接字的创建、命名和侦听等准备工作,并返回0,否则关闭套接字返回其它值。

4.Socket的连接处理---系统调用accept

#include<sys/types.h>

#include<sys/socket.h>

int accept(int s, struct sockaddr *addr, int *addrlen);

返回值:成功返回新创建的套接字描述符,失败返回-1,阻塞返回错误errno==EINTR。

服务器端套接字在进入侦听状态后,必须通过函数accept接收客户进程提交的连接请求,才能完成一个套接字的完整连接。

s 是一个由函数socket创建,由函数bind命名,并调用函数listen进入侦听的套接字描述符。

addr回传连接成功的客户端地址结构。

Addrlen回传该结构占用的字节空间大小。

如果不在乎客户端套接字的地址信息,可能把参数addr和addrlen设置为NULL。

也可以通过函数getpeername随时获取客户端套接字的地址信息。

附:本处设计一个封装了系统调用accept的函数AcceptSock。

int AcceptSock(int * pSock, int nSock)

{

struct sockaddr_in addrin;

int lSize;

assert(pSock != NULL && nSock > 0);

while (1){

lSize = sizeof(addrin);

memset(&addrin, 0, sizeof(addrin));

/* 阻塞接收客户端连接请求,并创建新的套接字描述符 */

if ((*pSock = accept(nSock, (struct sockaddr *)&addrin, &lSize)) > 0)  return 0;

/* 调用accept过程中接收到到信号,调用中断 */

else if (errno == EINTR)       continue;

else       ASSERT(0);

}

}

说明:函数AcceptSock接收客户端的套接字连接申请,它在TCP通信服务器端成功调用函数CreateSock后使用,整型nSock指定用于侦听的套接字描述符,参数pSock回传新创建的与客户端相连接的套接字描述符,成功时返回0。

5.Socket的连接申请---系统调用connect

#include<sys/types.h>

#include<sys/socket.h>

int connect(int s, const struct sockaddr *name, int namelen);

返回值:成功返回0,否则返回-1。

函数connect连接两个指定的套接字。参数s是本地套接字描述符,指针name指定对方套接字地址结构。参数namelen指定该数据结构的长度。

在流套接字中调用connect,函数必须等待服务器端的应答结果或者连接超时后才能返回。而在数据报套接字中调用connect,函数仅仅记录对方套接字地址而不建立真正的连接。

函数调用成功返回0,否则返回-1。如果连接超时,函数返回并且取消连接申请。如果进程在函数调用期间接收到信号,则函数中断,但继续申请连接。

附:本处设计一个封装了系统调用connect的函数ConnectSock。

int ConnectSock(int *pSock, int nPort, char * pAddr)

{

struct sockaddr_in addrin;

long lAddr;

int nSock;

assert(pSock != NULL && nPort > 0 && pAddr != NULL);

assert((nSock = socket(AF_INET, SOCK_STREAM, 0)) > 0);

/*创建TCP套接字描述符*/

/* 协议地址组包 */

memset(&addrin, 0, sizeof(addrin));

addrin.sin_family = AF_INET;                    /*协议地址类型*/

addrin.sin_addr.s_addr = inet_addr(pAddr);  /*字符串IP地址转换为网络字节顺序*/

addrin.sin_port = htons(nPort);                   /* 端口号转网络字节顺序*/

if ((connect(nSock, (struct sockaddr *)&addrin, sizeof(addrin)) >= 0))

{     /*连接成功,返回套接字描述符*/

*pSock = nSock;

return 0;

}

/*连接失败,关闭套接字描述符*/

close(nSock);

return 1;

}

客户端在调用connect连接服务器端时,必然先创建套接字描述符,填充协议地址结构,并做好网络字节顺序转换和IP地址格式转换等工作。函数ConnectSock封装了这一系列操作,参数pSock回传了新创建的与服务器端相连接的套接字描述符,字符串pAddr指定了服务器端主机的IP地址,整型nPort指定了服务器端套接字的端口号。函数调用成功时返回0。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wwpjdf.html