网络编程之socket
socket 是应用层tcp和ip协议的通信,是一种中间的抽象层,它就像是一个接口,里面封装了很多的协议,(也可以自己重写socket)
socket中的套接字分为两种
1.AF_UNIX 是一个套接字
AF_UNIX 是一种基于文件的套接字
unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,
两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信
2,AF_INET是一种套接字
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,
要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中, AF_INET是使用最广泛的一个,
python支持很多种地址家族,但是由于大部通讯都是网络通讯,所以大部分时候使用AF_INET)
基于tcp的socket 就是客服端和服务端,使用socket中的tcp协议来完成客服端和服务端的连接
什么是客服端:
客服端就是用户 使用的程序通过用户使用程序来传输信息的这个介子,这个就是客服端
什么是服务端:
服务端就是给客户端提供需要的数据的,就相当于是一个库
如何使用socket来连接客户端和服务端
tcp 的通讯流程
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket ip_port=('127.0.0.1',9000) #电话卡 BUFSIZE=1024 #收发消息的尺寸 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机 conn,addr=s.accept() #手机接电话 print('接到来自%s的电话' %addr[0]) msg=conn.recv(BUFSIZE) #听消息,听话 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机 服务端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话 s.send('linhaifeng nb'.encode('utf-8')) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话 print(feedback.decode('utf-8')) s.close() #挂电话
这样的客户端和服务端不是完善的所以就优化了客户端和服务端(不完善是因为有太多的错误)
客户端和服务端的常见的错误
在调试过程中,可能会遇见以下错误:
问题发生原因:
2.三次握手或四次挥手时,发生了异常导致对方程序已经结束而服务器任然处于time_wait状态导致!
3.在高并发的场景下,由于链接的客户端太多,也会产生大量处于time_wait状态的链接
解决的方案:
第1种原因,很简单关闭之前运行的服务器即可
第2,3中原因导致的问题,有两种解决方案:
1.设置服务器重用端口
#加入一条socket配置,重用ip和端口 phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,必须在bind前加 phone.bind(('127.0.0.1',8081))
2.通过调整linux内核参数解决(了解)
发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决, vi /etc/sysctl.conf 编辑文件,加入以下内容: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30 然后执行 /sbin/sysctl -p 让参数生效。 net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
当客服端与服务器链接成功后,如果一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止),都会导致另一方发送问题,
在windows下,接收数据的一方在recv函数处将抛出异常
Traceback (most recent call last): File "C:/Users/jerry/PycharmProjects/untitled/TCP/server.py", line 9, in <module> conn.recv(1024) ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。
linux下,不会抛出异常会导致接收数据的一方,recv方法不断的收到空消息,造成死循环
要使应用程序能够在不同平台正常工作,那需要分别处理这两个问题
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(ip_port) s.listen(5) while True: conn,addr=s.accept() while True: try: msg=conn.recv(BUFSIZE) #linux不会抛出异常,会接收到空消息,这里加以判断 if not msg: conn.close() break print(msg,type(msg)) conn.send(msg.upper()) except ConnectionResetError: #只要异常发生则意味着对方以及关闭了,服务器也相应的关闭该链接 conn.close() break conn.close() s.close()
至此TCP通讯模板程序就完成了,可以不断的接收新的链接,不断的收发消息,并且不会因为客户端强制关闭而异常退出!
然后就优化客户端和服务端
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket ip_port=('127.0.0.1',8081)#电话卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机 while True: #新增接收链接循环,可以不停的接电话 conn,addr=s.accept() #手机接电话 # print(conn) # print(addr) print('接到来自%s的电话' %addr[0]) while True: #新增通信循环,可以不断的通信,收发消息 msg=conn.recv(BUFSIZE) #听消息,听话 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话 while True: #新增通信循环,客户端可以不断发收消息 msg=input('>>: ').strip() if len(msg) == 0:continue s.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话 print(feedback.decode('utf-8')) s.close() #挂电话