connect accept listen 与三次握手的关系

首先明确三次握手发生在什么时候。

经过我的验证,在客户端执行connect的时候,便是向已经listen的服务器发出三次握手,等connect返回的时候,三次握手已经完成,和accept没有任何关系。验证过程如下。

客户端程序只执行到connect,服务器端程序只执行到listen,然后用tcpdump对本地进行抓包。这里要注意tcpdump想抓本地还回的包需要监听 lo 也就是tcpdump -i lo port 12346 

再用netstat观察端口状态 发现两边都是establish

int main ()
{
	int sock;
	sock=socket(AF_INET,SOCK_STREAM,0);
	assert(sock>=0);
	struct sockaddr_in addr;
	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(12346);
	inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
	int err=connect(sock,(sockaddr*)&addr,sizeof(addr));
	assert(err==0);
	sleep(10000);
}
int main ()
{
	int listenfd;
	listenfd=socket(AF_INET,SOCK_STREAM,0);
	assert(listenfd>=0);
	int opt=1;
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	struct sockaddr_in addr;
	memset(&addr,0,sizeof(addr));
	addr.sin_family=AF_INET;
	addr.sin_port=htons(12346);
	inet_pton(AF_INET,"127.0.0.1",&addr.sin_addr);
	int err=bind(listenfd,(sockaddr*)&addr,sizeof(addr));
	assert(err==0);
	err=listen(listenfd,10);
	assert(err==0);
	sleep(10000);
}

再来看listen函数的参数。它的作用是告诉内核设置连接队列的长度。

内核为每一个listen状态的套接字设置两个队列,未完成连接队列和已完成连接队列,这两个队列共用listen设置的连接长度。

当客户端发送syn报文的时候,服务器检测连接队列是否已满,如果满了就丢弃这个syn,如果没满就把这个链接放入未完成队列,发送ack和syn,当客户端收到ack和syn之后,会发送ack报文,并且从connect返回,服务器收到这个ack后把连接从未完成队列中取出放入已完成队列,等待accept把这个链接取走。此时三次握手已经全部完成,两端连接都是establish状态。

实际上连接在未完成队列中的时间是很短的,这段时间代表服务器发送syn和ack并且从客户端收到ack的时间,没有特殊情况的话,一般很快就会完成,但是如果服务器发送syn+ack后超过一定时间都没有收到客户端的ack,服务器会把这个链接丢弃掉,超时时限一般被设为75秒。

以下是我把listen的参数设为1,开了三个客户端向同一个服务器发起连接,并且服务器没有accept的抓包情况。

可见在等待队列里最多有两个链接,第三个client一直在发送syn,因为没有收到ack,所以执行超时重传,超时时间每次*2。

我打印了每个client从connect返回后的errno。

errno110表示  Connection timed out

最后来说accept。这个函数只是把已完成队列中的链接取出来,如果已完成连接队列里没有连接,accept就会阻塞。

这里要说一点,就算是队列满了,新的client在发起连接之后,服务器也并没有拒绝他的syn,只是把它丢弃了,这样client还是会向服务器发送syn,只不过因为超时重传机制,每次发送的间隔时间会变长,如果在这个阶段服务器accept了已完成的连接,那么连接队列就会空出来,这样受到syn后,服务器还是会正常的和客户端完成三次握手。

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
三次握手是指在TCP连接建立过程,客户端和服务器之间需要进行三次交互来确认连接的建立。下面是一个用Python实现的简单示例: ```python import socket # 客户端代码 def client(): # 创建socket对象 client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置服务器的主机名和端口号 server_address = ('localhost', 8888) # 进行第一次握手,向服务器发送连接请求 client_socket.connect(server_address) print("第一次握手:客户端发送连接请求") # 进行第二次握手,接收服务器的确认消息 server_message = client_socket.recv(1024) print("第二次握手:客户端接收到服务器的确认消息:", server_message.decode()) # 进行第三次握手,向服务器发送确认消息 client_socket.send(b'ACK') print("第三次握手:客户端发送确认消息") # 关闭socket连接 client_socket.close() # 服务器端代码 def server(): # 创建socket对象 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 设置服务器的主机名和端口号 server_address = ('localhost', 8888) # 绑定地址和端口号 server_socket.bind(server_address) # 监听连接请求 server_socket.listen(1) print("等待客户端连接...") # 接受客户端的连接请求 client_socket, client_address = server_socket.accept() print("第一次握手:服务器接受到客户端连接请求") # 进行第二次握手,向客户端发送确认消息 server_socket.sendto(b'ACK', client_address) print("第二次握手:服务器发送确认消息") # 接收客户端的确认消息 client_message = client_socket.recv(1024) print("第三次握手:服务器接收到客户端的确认消息:", client_message.decode()) # 关闭socket连接 server_socket.close() # 启动服务器和客户端 if __name__ == '__main__': server() client() ``` 上述代码在客户端和服务器端分别通过socket进行三次握手完成TCP连接的建立。请注意,这只是一个简单示例,实际场景可能涉及到更多的错误处理和状态判断。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值