1

基础

一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。
套接字是全双工的。

listen函数

只用于tcp,listen的原型是def listen(self, backlog),backlog一般默认为5,但是可以人为调整,一些web服务器都会高于此值。listen做两件事:

  1. 将未链接的套接字转换为被动套接字,指示内核接受对此套接字的连接请求

  2. 制定了内核为此套接口排队的最大连接个数

对于指定的套接字,内核要维护未连接队列和已连接队列两个队列,完成三路握手的连接将从未连接队列移至已连接队列。accept时会从已连接队列中取出一个给当前进程,若队列为空则睡眠(表征了其事件驱动的特性)
backlog为两个队列和的最大值,超过此值将忽略新来的连接并且不发RST,以使客户端重发SYN,让TCP的重传机制进行重传。
backlog为0表示不限制连接数,可能会导致SYN Flooding,不建议使用该值

socket.SO_REUSEADDR

socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

可以用于以下四种情况:

  • 当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

  • SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。

  • SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。

  • SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP

将监听的连接设置为可重用,这个选项有一些很实用的功能:

  1. 防止重启监听程序时出现无法bind端口错误

  2. 实现TCP的NAT穿透

TCP粘包处理

与UDP不同,TCP是流协议,而流是连续不断没有界限的,使用TCP时需要自己处理分包。由于网络情况或者发送问题,有可能接收到包的顺序和发送的顺序不同,这时候就需要用一些方法去进行粘包处理。

比如,为了防止缓冲区等待造成的粘包,可以将使用sendall函数将数据立即发送而不是等待缓冲区满,并将socket设置为NODELAY

sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

粘包问题其实是如何TCP数据消息边界的问题,总的来说有3种解决方案:

  1. 发送固定长度的消息

  2. 把消息的尺寸与消息一块发送

  3. 使用特殊标记来区分消息间隔

第二种情况应该是用得最普遍的,实现比较简单,也不会对性能造成大的影响。

http协议中基本三种方式都用到了,如图

图片描述

http头部使用CRLF,也就是"回车换行"来区分头部之间的不同字段,使用消息体尺寸来分割http消息体的内容,使用EOF(ascii为255)来关闭连接。头部和消息体之间呢?使用两个CRLF来区分


quietin
761 声望44 粉丝

兴趣在程序语言, 高性能, 分布式等方面