基础
一个套接字由相关五元组构成,协议、本地地址、本地端口、远程地址、远程端口。
套接字是全双工的。
listen函数
只用于tcp,listen的原型是def listen(self, backlog)
,backlog一般默认为5,但是可以人为调整,一些web服务器都会高于此值。listen
做两件事:
将未链接的套接字转换为被动套接字,指示内核接受对此套接字的连接请求
制定了内核为此套接口排队的最大连接个数
对于指定的套接字,内核要维护未连接队列和已连接队列两个队列,完成三路握手的连接将从未连接队列移至已连接队列。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
将监听的连接设置为可重用,这个选项有一些很实用的功能:
防止重启监听程序时出现无法
bind
端口错误实现TCP的NAT穿透
TCP粘包处理
与UDP不同,TCP是流协议,而流是连续不断没有界限的,使用TCP时需要自己处理分包。由于网络情况或者发送问题,有可能接收到包的顺序和发送的顺序不同,这时候就需要用一些方法去进行粘包处理。
比如,为了防止缓冲区等待造成的粘包,可以将使用sendall
函数将数据立即发送而不是等待缓冲区满,并将socket设置为NODELAY
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
粘包问题其实是如何TCP数据消息边界的问题,总的来说有3种解决方案:
发送固定长度的消息
把消息的尺寸与消息一块发送
使用特殊标记来区分消息间隔
第二种情况应该是用得最普遍的,实现比较简单,也不会对性能造成大的影响。
http协议中基本三种方式都用到了,如图
http头部使用CRLF
,也就是"回车换行"来区分头部之间的不同字段,使用消息体尺寸来分割http消息体的内容,使用EOF
(ascii为255)来关闭连接。头部和消息体之间呢?使用两个CRLF
来区分
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。