专业的编程技术博客社区

网站首页 > 博客文章 正文

详细解读网络io与线程/进程的苟且之事

baijin 2025-05-03 12:06:09 博客文章 4 ℃ 0 评论

简介;

本篇主要总结服务器端开发中的一些基本的框架。 最近在做数据库相关的事情,碰到了很多TCP相关的问题,新的场景新的挑战,有很多之前并没有掌握透彻的点,大大开了一把眼界,选了几个案例分享一下。

目录;

1. tcp的并发坑点

2. udp并发设计方案

3. 网络io与线程/进程

tcp的并发坑点

一. 关于半关闭和CLOSE_WAIT

客户端主动关闭,发送FIN包。服务端收到FIN,发出ACK,停留在CLOSE_WAIT状态。这个状态持续时间非常长,服务器如果积攒大量的CLOSE_WAIT状态socket,有可能耗尽资源。为什么会产生这样情况?有可能sockfd通过fork被子进程复制了一份,这样该sockfd引用计数为2。在关闭socket时使用close()函数并没有关掉该fd,仅仅是引用计数减1,所以服务端没有向客户端发送FIN,这样就造成了socket处于CLOSE_WAIT状态。解决的办法是:使用shutdown()函数。关于shutdown()函数用法参见

二. 关于FIN_WAIT2状态

前面说过,处于FIN_WAIT_2状态的客户端需要等待服务器发送结束报文段,才能转移至TIME_WAIT状态,否则它将一直停留在这个状态。如果不是为了在半关闭状态下继续接收数据,连接长时间地停留在FIN_WAIT_2状态并无益处。连接停留在FIN_WAIT_2状态的情况可能发生在:客户端执行半关闭后,未等服务器关闭连接就强行退出了。此时客户端连接由内核来接管,可称之为孤儿连接(和孤儿进程类似)。Linux为了防止孤儿连接长时间存留在内核中,定义了两个内核变量:
/proc/sys/net/ipv4/tcp_max_orphans和
/proc/sys/net/ipv4/tcp_fin_timeout。前者指定内核能接管的孤儿连接数目,后者指定孤儿连接在内核中生存的时间。

三. 关于TIME_WAIT状态

TIME_WAIT 状态存在的原因有两点:
口可靠地终止TCP连接。
口保证让迟来的TCP报文段有足够的时间被识别并丢弃。

使用UDP时,服务端通过同一个套接字和所有的客户端进行通信,当采用并发模式时,每一个子进程共享同一个UDP套接字,因此无法简单地绑定于一个客户并为其服务。

udp并发设计方案

有两种情况下可以使用并发的UDP服务器:

1 读入一个客户请求并发送一个应答后,与这个客户不再有任何联系。在这种情形下,当一个UDP请求到达时,阻塞在epoll_wait调用上的父进程被唤醒,然后fork一个子进程去调用recv_from读取一个请求(一个完整的数据报),处理完该请求后再调用send_to发送回去。

2 第二种UDP服务器与客户交互多个数据报。问题在于每个客户都是往服务器端的同一个的端口发送数据。并发服务器的每一个子进程如何正确区分每一个客户的数据报(涉及到进程的调度问题,如何避免一个子进程读取到不该它服务的客户发送来的数据报)。解决的方法是为每个客户创建一个的新的套接字,在其上bind一个临时端口,fork一个子进程使用该套接字发送对该客户的所有应答。这个办法要求客户查看服务器第一个应答的中的源端口号,并把本请求的后续数据报发送到该端口。

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表