AF_INET与套接字
创建套接字的函数是socket(),函数原型为:
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
其中 “int domain”参数表示套接字要使用的协议簇,协议簇的在“linux/socket.h”里有详细定义,常用的协议簇:
- AF_UNIX(本机通信)
- AF_INET(TCP/IP-IPv4)
- AF_INET6(TCP/IP-IPv6)
其中 “type”参数指的是套接字类型,常用的类型有:
- SOCK_STREAM(TCP流)
- SOCK_DGRAM(UDP数据报)
- SOCK_RAW(原始套接字)
最后一个 “protocol”一般设置为“0”,也就是当确定套接字使用的协议簇和类型时,这个参数的值就为0,但是有时候创建原始套接字时,并不知道要使用的协议簇和类型,也就是domain参数未知情况下,这时protocol这个参数就起作用了,它可以确定协议的种类。
socket是一个函数,那么它也有返回值,当套接字创建成功时,返回套接字,失败返回“-1”,错误代码则写入“errno”中。
创建套接字:
python中socket默认为Ipv4,tcp模式。
服务端
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((ip,port))#绑定ip和端口号 sock.listen(5)#开启监听,允许5个连接, while True: conn,addr = sock.accept() buf = conn.recv(1024) if len(buf): print(buf)
conn.send(buf) sock.close()
listen参数的问题
可以表示可建立socket连接的排队的个数
windows,mac此连接参数有效
Linux此连接参数无效,默认最大
客户端
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((ip,port))#监听ip和端口号 sock.send(messages) while True: buf = sock.recv(1024)#一次接受1024字节数据 if len(buf): print(buf) sock.close()
socket编程中主动关闭和被动关闭
tcp中server,client都可能是主动关闭方或者被动关闭方,现阐述下两者之间的关系:
客户端(client) 服务器(server)
close() Fin x -> 读通道关闭(close_wait)
写通道关闭 <- Ack x+1
读通道关闭(time_wait) <- Fin y close()
ack y+1 -> 写通道关闭
2x msl closed
closed
- 客户端调用函数close(),这时,客户端会发送一个FIN信号给服务器
- 服务器收到FIN信号,关闭套接字的读通道,并将自己的状态设置为CLOSE_WAIT(表示被动关闭),并返回一个ACK给客户端
- 客户端收到ACK,关闭套接字写通道
- 接下来服务器会调用close():
- 服务器close(),发送一个FIN到客户端。
- 客户端收到FIN,关闭读通道,并将自己的状态设置成TIME_WAIT,发送一个ACK给服务器
- 服务器收到ACK,关闭写通道,并将自己的状态设置为CLOSE。
- 客户端等待两个最大数据传输时间,然后将自己的状态设置成CLOSED。
有了上面的背景知识,对于我们系统线上一个case分析就很简单了!
首先是主动关闭日志很多,后来是被动关闭的日志
由于server端发现了大量闲置的没有Io的socket连接,有监听器在监听是否存在闲置的socket连接,就释放并关闭这些连接,time_wait就出现了,这个时候应用方客户端重启应用,释放了资源包括一些客户端连接,这个时候close_wait出现了,正好是以上日志所反映的
同时time_wait状态的连接是不会释放内核资源,所以服务端不要轻易close!
socket是全双工管道,虽然只有一对socket,但实际上是两个通道,读写是不会冲突的。
python 解决close_wait过多问题
close_wait产生的原因:
首先要知道客户端和服务端的连接是使用套接字通信的,TCP/IP协议建立连接需要三次握手,而关闭client与server的连接需要进行四步,如图:
建立连接后常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关闭。
通过上图,我们来分析,什么情况下,连接处于CLOSE_WAIT状态呢?
就是服务端在被动关闭收到FIN,未发出自己FIN的情况下就处于CLOSE_WAIT状态了,通常CLOSE_WAIT的持续时间很
短,但是在某些特殊状态下就可能长时间存在,如果出现大量close_wait的现象,主要原因可能是一方关闭了socket链接,但是另一方
忙与读或者写,没有关闭连接。代码需要判断socket,一旦读到0,断开连接,read返回负,检查一下errno( errno 是记录系统的最后
一次错误代码。),如果不是AGAIN,就断开连接。