套接字基础
C/S架构,即客户端/服务器架构,B/S架构(浏览器/服务器),也属于C/S架构
socket介绍
socket套接字就是为了完成C/S架构软件的开发。socket依赖于网络,所以骚年,网络基础不能忘了。
在Python中,socket子层位于TCP/IP协议栈的传输层和应用层的中间层,是一个提供向上向下接口的软件抽象层。socket封装了tcp和udp协议,所以遵循socket语法写出的程序遵循tcp和udp协议
注:socket = IP + port,ip用来标识网络中主机的位置,port用来标识主机的应用,所以ip + port能够标识互联网中的唯一一个应用,所以说socket其实就是IP和端口的组合
socket分类
网络编程只需要关注AF_INET,这种是应用最广泛的,如果是用于ipv6平台需要用AF_INET6。
其他:AF_UNIX,用于UNIX文件系统间通信、还有很多其他的平台使用的。
socket通信原理
上图为sockettcp通信过程:
1.服务器先初始化socket,然后与端口绑定(bind),对端口进行监听(listen)并调用accept阻塞等待
2.客户端连接先初始化一个socket,然后连接服务器(connect),如果正常访问到了服务器端,服务器端阻塞结束,连接成功,这时客户端与服务器端的连接建立。
3.客户端发送数据请求,服务器端接收请求并处理请求,然后服务器把回应数据发送给客户端,客户端读取数据,循环。
4.最后客户端或者服务端关闭连接,一次交互结束。
socket模块
如:基于本地环回地址的一次性套接字通信
服务端:
#导入socket模块
import socket
#创建socket,类似于买手机
skt=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定端口和ip,必须为元组类型,类似于手机插卡
#,如果元组为('',9000)表示本机所有的网卡,相当于0.0.0.0
skt.bind(('127.0.0.1',9000))
#侦听访问端口,类似于手机待机
#若括号中有值,则表示对TCP连接的优化
skt.listen()
#此处循环表示服务器持续提供服务
while True:
#conn表示接受的数据流,addr表示客户端的地址
conn,addr=skt.accept()
#接受客户端发送消息并打印
msg=conn.recv(1024)
print(msg.decode('utf-8'))
print(msg,type(msg))
#为客户端返回消息,表示接受成功
conn.send(msg.upper())
#关闭本次通信
conn.close()
#关闭链接
skt.close()
客户端
#导入socket模块
import socket
#创建socket
skt = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#与服务端建立链接
skt.connect(('127.0.0.1',9000))
#发送消息
msg = b'Hello'
skt.send(msg)
#接受服务端返回值并打印
res = skt.recv(100)
print(res.decode('utf-8'))
#关闭会话链接
skt.close()
#相关值说明
1 socket.socket(socket_family,socket_type,protocal=0) 2 socket_family可以是 AF_UNIX 或 AF_INET 3 socket_type 可以是 SOCK_STREAM(面向连接的可靠数据传输,即TCP协议)或 SOCK_DGRAM(面向无连接的不可靠数据传输,即UDP) 4 protocol 一般不填,默认值为 0
#相关方法说明
1 服务端套接字函数
2 s.bind() 绑定(主机,端口号)到套接字
3 s.listen() 开始TCP监听
4 s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来
5
6 客户端套接字函数
7 s.connect() 主动初始化TCP服务器连接
8 s.connect_ex() connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
9
10 公共用途的套接字函数
11 s.recv() 接收TCP数据
12 s.send() 发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
13 s.sendall() 发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
14 s.recvfrom() 接收UDP数据
15 s.sendto() 发送UDP数据
16 s.getpeername() 连接到当前套接字的远端的地址
17 s.getsockname() 当前套接字的地址
18 s.getsockopt() 返回指定套接字的参数
19 s.setsockopt() 设置指定套接字的参数
20 s.close() 关闭套接字
21
22 面向锁的套接字方法
23 s.setblocking() 设置套接字的阻塞与非阻塞模式
24 s.settimeout() 设置阻塞套接字操作的超时时间
25 s.gettimeout() 得到阻塞套接字操作的超时时间
26
27 面向文件的套接字的函数
28 s.fileno() 套接字的文件描述符
29 s.makefile() 创建一个与该套接字相关的文件
#常见错误处理
由于服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
最直接的解决方法,更改端口号
更多解决方法:
window解决方法
1 #加入一条socket配置,重用ip和端口
2 phone=socket(AF_INET,SOCK_STREAM)
3 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
4 phone.bind(('127.0.0.1',9000))
Linux解决方法
1 发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
2 vi /etc/sysctl.conf
3
4 编辑文件,加入以下内容:
5 net.ipv4.tcp_syncookies = 1
6 net.ipv4.tcp_tw_reuse = 1
7 net.ipv4.tcp_tw_recycle = 1
8 net.ipv4.tcp_fin_timeout = 30
9
10 然后执行 /sbin/sysctl -p 让参数生效。
11
12 net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
13
14 net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
15
16 net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
17
18 net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间