在进入我们的正题之前,再来复习一波编写服务器的函数流程吧
服务器端: socket()-->bind( )-->listen()-->accept()-->read()/write()--->close() socket()//创建套接字 bind()//分配套接字地址 listen()//等待连接请求状态 accept()//允许连接 read()/write()//进行数据交换 close()//断开连接
socket()//创建套接字
bind()//分配套接字地址
listen()//等待连接请求状态
相信大家都不陌生了。所以博主就接着讲解 listen()函数的那一篇博客讲起了。其实listen() 和 accept()联系是很紧密的。
请大家要明白这一点: socket三次握手是在listen中完成,accept只从完成连接的队列中拿出一个连接
好了,那我们就来介绍 accept()函数吧。
受理客户端连接请求
调用 listen()函数之后,若有新的连接请求,则应按序处理。受理请求意味着进入科技收数据的状态。
也许各位已经猜到进入这种状态所需要的部件-----当然是套接字了。大家可能认为可以使用服务器端套接字,单是正如我们前面所说·,服务器端套接字是做门卫的。如果在客户端的数据交换中是用门卫,那谁来守门呢???所以我们就需要另外一个套接字了。
那是不是意味我们要再去创建一个套接字呢??没有必要的,下面这个函数(accept())会自动创建套接字,并连接到发起请求的客户端。
所以我们就要考虑一下是否需要自己建立一个socket套接字去接收accept返回的客户端套接字了。事实上我们就是创建了一个有关客户端的套接字用来接收返回值,从而达到与客户端进行沟通的作用。
#include<sys/socket.h> int accept(int sockfd, struct spckaddr *addr, skcklen_t *sddlen); 参数: 第一个参数: int sockfd: --->服务器套接字的文件描述符 第二个参数: struct spckaddr *addr --->保存发起连接请求的客户端地址信息的变量地址值,也就是返回链接客户端地址信息,含IP地址和端口号 需要强制转换类型,之前讲过 第三个参数: skcklen_t *sddlen --->传入sizeof(addr)第二个参数的大小,函数返回时返回真正接收到地址结构体的大小 值得一提的是第二个参数和第三个参数: 第二个参数:传出参数,返回的连接成功的信息,所以我们不需要对这个套接字进行初始化。 第三个参数:传入传出参数,一开始传入一个参数防止溢出,调用完成之后长度会发生改变。 因为是类型不是int 所以我们一般会在前面定义一个 skcklen_t 变量来衡量大小。 第三个参数具体使用: skcklen_t = cliaddr_len; cliaddr_len = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);//因为是传入传出所以需要涉及修改值,所以传入地址。 返回值: 成功:成功返回一个新的socket文件描述符,用于和客户端通信。 失败:返回-1,设置errno
三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连接请求,就阻塞等待直到有客户端连接上来
accept()函数接受连接请求等待队列中待处理的客户端连接请求。函数调用成功时,accept()内部将产生用于数据I/O的套接字,并返回器文件描述符。需要强调的是,套接字是自动创建的,并自动与发起连接请求的客户端建立连接。如图展示了 accept()函数调用的全过程。
如图展示了“冲等待队列中取出一个连接请求,创建套接字并完成连接请求的”过程。
书中对于这些讲的已经很详细了,所以没啥好扩充的内容所以博主就把一般的服务器结构列出来:
...... skcklen_t = cliaddr_len; while (1) { cliaddr_len = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //connfd就是和客户端沟通的套接字。 n = read(connfd, buf, MAXLINE); ...... close(connfd); }
整个是一个while死循环,每次循环处理一个客户端连接。由于cliaddr_len是传入传出参数,每次调用accept()之前应该重新赋初值。
accept()的参数listenfd是先前的监听文件描述符,而accept()的返回值是另外一个文件描述符connfd,之后与客户端之间就通过这个connfd通讯,最后关闭connfd断开连接,而不关闭listenfd,再次回到循环开头listenfd仍然用作accept的参数。
这一篇博客基本上就要结束了,博主带大家分析一下服务器端代码吧。
好啦,谢谢大家地观看。