Python——socket&socketserver(网络编程)
socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)
socket和file的区别:
- file模块是针对某个指定文件进行【打开】【读写】【关闭】
- socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】
在Scoket里的表达方式:
Socket Families(地址簇) IP层
- socket.AF_UNIX unix本机进程间通信
- socket.AF_INET IPV4
- socket.AF_INET6 IPV6
Socket Types 传输层
- socket.SOCK_STREAM #for tcp
- socket.SOCK_DGRAM #for udp
socket.SOCK_RAW #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_RDM #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
特点:
1. 默认socket.socket()为:IPv4地址与TCP的传输
2. 2.X版本能发字符串,比特,字节。但在3.X中只能发bit类型。
3. 需要注意的是粘包问题(就是两个发送的数据全部在一起)
4. DUP传输时,需要注意防火墙是否允许通过。
粘包:
发生在发送端的粘包:
由于两个数据的发送时间间隔短+数据的长度小,再加上TCP的优化机制(将两个较小数据和发送时间间隔很短的合并在一起发送,这样会减少TCP对数据的确认包)
发送在接收端的粘包:
由于TCP协议中所传输的数据无边界,所以来不及接收的多条数据会在接收方的内核缓存并粘起来。
可以通过struct模块来解决。
1 2 3 4 5 | import struct value = 345223123 value_struct = struct.pack( 'i' ,value) #通过pack来将字节数控制到4个字节,那么接收方先进行4字节的接收,再接收其他的就不存在粘包问题了。 print (value_struct) #b'\xd3\xaf\x93\x14' |
import struct client_data = {'client_info':'login',#用户信息 'client_status':False,#用户状态 'client_name':None,#用户名 'client_pwd':None,#密码 'server_con':None,#服务器回复信息 'data_name':None,#如有数据,将写数据的名字 'data_size':None,#如有数据,将写数据的大小 } data_len = len(client_data) print(data_len) data_len_struct = struct.pack('i',data_len) print(data_len_struct) print(struct.unpack('i',data_len_struct)[0])
标准配置:
1. 可以允许多个用户接入,但只有一个能进行数据传输,只有一个断了,下一个才能进行数据传输。
2. 服务器一直在监听状态。
3. 用户无法输入空的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 | #Server import socket server = socket.socket() server.bind(( '127.0.0.1' , 8888 )) server.listen( 2 ) #需要监听用户的个数 while True : conn,addr = server.accept() #接收一个新的连接。conn是每进来一个连接,都为其创建一个实例,addr为连接进来的IP地址。 while True : data = conn.recv( 8192 ) #监听应该为连接进来的实例,多大为10M左右,官方建议最多不超过8192K。 if not data: break #为了解决客户端一断开就进入死循环。因为客户端一断开就会发送空数据。 print ( 'recv:' ,data.decode()) conn.send(data.upper()) server.close() |
1 2 3 4 5 6 7 8 9 10 11 | #Client import socket client = socket.socket() client.connect(( '127.0.0.1' , 8888 )) while True : choice = input ( '==>:' ) if len (choice) = = 0 : continue client.send(choice.encode()) #把目前的unicode字符编码转换为utf-8字符编码 data = client.recv( 1024 ) print (data.decode()) #将utf-8字符编码进行转换 client.close() |
一个简单的SSH:
存在的问题:
1. 在检查字符长度时,可能存在中文和英文长度上的差异,就是server发过来的要比实际发送数据过来的要小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | __author__ = "XinB" #Server import socket,os server = socket.socket() #将socket模块导入并赋值 server.bind(( 'localhost' , 9999 )) #写服务器的Ip地址和接收的端口号 server.listen( 2 ) #开始监听,并设置同时进入的客户端数量。 while True : conn,addr = server.accept() #开始接收客户端(会卡到该过程,等待用户接入) while True : data = conn.recv( 1024 ) #如果用户接入,接收发过来的信息。 if not data: break #如果用户发过来的信息是空,那么将结束用户并断开该用户。 cmd_recv = os.popen(data.decode()).read() #如果不为空,那么将用户传输过来的,应用到dos系统命令中。 if len (cmd_recv) = = 0 : #判断如果返回的值为空(用户输入错误将会返回为空),那么将打印。 cmd_recv = '无效输出' conn.send( str ( len (cmd_recv.encode( 'utf-8' ))).encode( 'utf-8' )) #将系统返回的值,计算值的大小,将大小发送给用户。 conn.send(cmd_recv.encode( 'utf-8' )) #再将值发送给客户端。 server.close() |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | __author__ = "XinB" #Client import socket client = socket.socket() #将socket模块导入并赋值 client.connect(( 'localhost' , 9999 )) #客户端填写服务器端Ip地址和端口号 while True : choice = input ( '>>>:' ).strip() #循环用用户输入字符串 if len (choice) = = 0 : continue #如果用户输入的为空将返回重新输入。 client.send(choice.encode()) #如果不为空,将发送给服务器端 cmd_res = client.recv( 1024 ) #第一次接收的是将要发过来数据的长度。 resved_size = 0 #创建一个初始为0的变量,将判断接收的数据是否已经满足要求。 resved_data = '' #创建一个空字符,将显示所返回的结果。 while resved_size < int (cmd_res.decode()): #循环在resved_size 小于服务器返回值的时候。 data = client.recv( 1024 ) #开始接收数据 resved_size + = len (data) #将接收数据的大小添加到空变量 resved_data + = data #将接收到的数据添加到空字符中。 else : print (resved_data.decode()) #如果全部接收完毕,将所有的数据打印出来。 client.close() |
socket-server
特点:
- socket的再封装版本。使其更加简化
- 可以实现多用户接入,socket-server主要用于server端,client端使用socket写即可。
- 必须继承socket.BaseRequestHandler的父类。
- 所有的客户端请求,全部由handle方法处理。
- 每一个客户端请求,都会实例化一个server classes。
- 3.Xpython 由于客户端断开后会报连接错误,所以需要try一下。
- 请求过来之前可以使用setup,请求处理中可以用handle,请求处理之后可以使用finish。
socket-server分类:
1. class socketserver.TCPServer()
使用与TCP的连接使用
2. class socketserver.UDPServer()
使用与UDP的连接使用,所使用的语法和TCPserver是一样的。
3. class socketserver.UnixStreamServer()
在本机,用于两个不同进程之间的通信使用。
初始化案例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | import socketserver class Myserver(socketserver.BaseRequestHandler): #必须要继承该类 def handle( self ): #必须填写方法 ''' 1. handle方法针对的是每一个客户端都会独立出来一个。而不是公用这一个handle方法。 2. handle必须的原因是父类有相同方法,如果要自定义就要自己写此方法。 3. self.client_address为客户端的Ip地址加端口。 4. self.server为服务器的IP地址。 5. self.request为客户端连接信息。(跟socket的conn一样) :return: ''' print ( self .request) self .request.send() #发送 self .request.recv() #接收 server = socketserver.ThreadingTCPServer(( '127.0.0.1' , 9000 ),Myserver) server.serve_forever() |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
· .NET开发智能桌面机器人:用.NET IoT库编写驱动控制两个屏幕
· 用纯.NET开发并制作一个智能桌面机器人:从.NET IoT入门开始
· 一个超经典 WinForm,WPF 卡死问题的终极反思
· ASP.NET Core - 日志记录系统(二)
· 在外漂泊的这几年总结和感悟,展望未来
· 博客园 & 1Panel 联合终身会员上线
· 支付宝事故这事儿,凭什么又是程序员背锅?有没有可能是这样的...
· https证书一键自动续期,帮你解放90天限制
· 在 ASP.NET Core WebAPI如何实现版本控制?