套接字以及相关函数

accept connect listen 之间的关系

基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下:

连接详情:

 connect()函数

对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三次握手,而这个连接的过程是由内核完成,不是这个函数完成的,这个函数的作用仅仅是通知 Linux 内核,让 Linux 内核自动完成 TCP 三次握手连接三次握手详情,请看《浅谈 TCP 三次握手》),最后把连接的结果返回给这个函数的返回值(成功连接为0, 失败为-1)。

通常的情况,客户端的 connect() 函数默认会一直阻塞,直到三次握手成功或超时失败才返回(正常的情况,这个过程很快完成)。

listen()函数

对于服务器,它是被动连接的。举一个生活中的例子,通常的情况下,移动的客服(相当于服务器)是等待着客户(相当于客户端)电话的到来。而这个过程,需要调用listen()函数。

#include<sys/socket.h>

int listen(int sockfd, int backlog);

listen() 函数的主要作用就是将套接字( sockfd )变成被动的连接监听套接字(被动等待客户端的连接),至于参数 backlog 的作用是设置内核中连接队列的长度(这个长度有什么用,后面做详细的解释),TCP 三次握手也不是由这个函数完成,listen()的作用仅仅告诉内核一些信息。

这里需要注意的是listen()函数不会阻塞,它主要做的事情为,将该套接字和套接字对应的连接队列长度告诉 Linux 内核,然后,listen()函数就结束。

这样的话,当有一个客户端主动连接(connect()),Linux 内核就自动完成TCP 三次握手,将建立好的链接自动存储到队列中,如此重复。

所以,只要 TCP 服务器调用了 listen(),客户端就可以通过 connect() 和服务器建立连接,而这个连接的过程是由内核完成

三次握手的连接队列

这里详细的介绍一下 listen() 函数的第二个参数( backlog)的作用:告诉内核连接队列的长度

为了更好的理解 backlog 参数,我们必须认识到内核为任何一个给定的监听套接口维护两个队列:

1、未完成连接队列(incomplete connection queue),每个这样的 SYN 分节对应其中一项:已由某个客户发出并到达服务器,而服务器正在等待完成相应的 TCP 三次握手过程。这些套接口处于 SYN_RCVD 状态。

2、已完成连接队列(completed connection queue),每个已完成 TCP 三次握手过程的客户对应其中一项。这些套接口处于 ESTABLISHED 状态。

当来自客户的 SYN 到达时,TCP 在未完成连接队列中创建一个新项,然后响应以三次握手的第二个分节:服务器的 SYN 响应,其中稍带对客户 SYN 的 ACK(即SYN+ACK),这一项一直保留在未完成连接队列中,直到三次握手的第三个分节(客户对服务器 SYN 的 ACK )到达或者该项超时为止(曾经源自Berkeley的实现为这些未完成连接的项设置的超时值为75秒)。

如果三次握手正常完成,该项就从未完成连接队列移到已完成连接队列的队尾

backlog 参数历史上被定义为上面两个队列的大小之和,大多数实现默认值为 5,当服务器把这个完成连接队列的某个连接取走后,这个队列的位置又空出一个,这样来回实现动态平衡,但在高并发 web 服务器中此值显然不够。

accept()函数

accept()函数功能是,从处于 established 状态的连接队列头部取出一个已经完成的连接,如果这个队列没有已经完成的连接,accept()函数就会阻塞,直到取出队列中已完成的用户连接为止

如果,服务器不能及时调用 accept() 取走队列中已完成的连接,队列满掉后会怎样呢?UNP(《unix网络编程》)告诉我们,服务器的连接队列满掉后,服务器不会对再对建立新连接的syn进行应答,所以客户端的 connect 就会返回 ETIMEDOUT。但实际上Linux的并不是这样的!

 TCP 的连接队列满后,Linux 不会如书中所说的拒绝连接,只是有些会延时连接,而且accept()未必能把已经建立好的连接全部取出来(如:当队列的长度指定为 0 ),写程序时服务器的 listen() 的第二个参数最好还是根据需要填写,写太大不好(默认最大值限制是 128),浪费资源,写太小也不好,延时建立连接。

监听套接字与连接套接字

监听套接字(listening socket)和已连接套接字(connected socket)之间的区别常会使很多人感到迷惑。本文简要描述一下这两者的区别。

为了说明监听套接字与已连接套接字的区别,我们先来看一下套接字在连接中的含义。
从内核的角度来看,一个套接字就是通信的一个端点。一个连接由它两端的套接了地址唯一确定,这对套接字地址叫做套接字对(socket pair),由下列4元组来表示:

(clientip:clientport, serverip:serverport)

其中,clientip 是客户端的IP地址,clientport 是客户端的端口,serverip 是服务器的IP地址,而 serverport 是服务器的端口。

在这里插入图片描述
图1:套接字对4元组示意图

 上图1展示了一个套接字对4元组,即一个客户端与一个服务器之间的连接。在这个示例中,客户端套接字为  128.2.194.242:51234    服务器套接字地址为   114.113.200.133:80

给定客户端和服务器地址,客户和服务器之间的连接就由下列套接字对唯一确定了:

(128.2.194.242:51234, 114.113.200.133:80)

在上面的例子中,客户端是发起连接请求的主动实体,服务器是等待来自客户端连接请求的被动实体。我们知道,socket函数可以创建一个套接字。默认情况,内核会认为socket函数创建的套接字是主动套接字(active socket),它存在于一个连接的客户端。而服务器调用listen函数告诉内核,该套接字是被服务器而不是客户端使用的,即listen函数将一个主动套接字转化为监听套接字(下文以 listenfd 表示)。监听套接字可以接受来自客户端的连接请求。
服务器通过accept函数等待来自客户端的连接请求到达监听套接字 listenfd,并返回一个已连接套接字(下文以 connfd 表示)。利用 I/O 函数,这个 connfd 可以被用来与客户端进行通信。

上面就是监听套接字与已连接套接字的基本区别了。具体来说,监听套接字,是服务器作为客户端连接请求的一个端点,它被创建一次,并存在于服务器的整个生命周期。已连接套接字是客户端与服务器之间已经建立起来了的连接的一个端点,服务器每次接受连接请求时都会创建一次已连接套接字,它只存在于服务器为一个客户端服务的过程中。
值得指出的是,无论是监听套接字,还是已连接套接字,都是只存在于服务器端。

在这里插入图片描述
图2:监听套接字与已连接套接字的角色

 图2描绘了监听套接字和已连接套接字的角色。在第一步中,服务器调用accept,等待连接请求到达监听套接字 listenfd,假设该监听套接字的文字描述符为3(0,1,2已预留给标准文件使用)。在第二步中,客户端调用connect函数,发送一个连接请求到 listenfd。第三步,accept函数打开一个新的已连接套接字 connfd (假设套接字的文件描述符为4),在 clientfd 和 connfd 之间建立连接,并且随后返回给服务器应用程序。客户端也从connect函数返回。此时,客户端和服务器就可以分别通过读写 clientfd 和 connfd 来回传送数据了。

利用套接字建立通讯原理

客户端地址(ip+端口)服务器地址(ip+端口)
客户端由内核默认分配需要预先知晓,以传入connect()中
服务器

当收到客户端的connect请求并

三次握手建立连接后知晓

需要设定,以传入bind()函数中

服务器的listen函数调用结束后,会产生一个监听套接字(listenfd),当客户端的connect函数调用后,对应的套接字信息会写入到listenfd中,服务器会尝试与客户端进行三次握手连接,连接成功后,accept选取一个已经建立连接的套接字进行通讯。而select,epoll等监听工具也是利用了这一点,它们负责监听套接字的读写状态,当监听到listenfd套接字产生读操作时,代表有客户端想要与服务器建立连接,那么此时启动accept()函数,对于连接套接字也是如此,当监听到连接套接字发生读操作,代表客户端此时在写入数据。
 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值