套接字基础
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.0skt.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#创建socketskt = 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_INET3 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 时间