socket编程
Posted lilong74
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了socket编程相关的知识,希望对你有一定的参考价值。
一、TCP、UDP协议的简单应用
1、使用TCP协的通信的基本格式
1 # 服务端 2 from socket import * #导入socket模块 3 4 ip_port = ("127.0.0.1",8080) 5 back_log = 5 #最大连接数(挂起) 6 buffer_size = 1024 #最大接受字节 7 8 tcp_server = socket(AF_INET,SOCK_STREAM) #注意与UDP的区别 9 tcp_server.bind(ip_port) #绑定IP地址 10 tcp_server.listen(back_log) #设置最大监听数(挂起) 11 12 print("服务端开始运行...") 13 14 #循环接受链接(未连接) 15 while True: 16 conn,addr = tcp_server.accept() #接受数据,为一个元组 17 print("双向链接是:",conn) 18 print("客户端地址:",addr) 19 20 # 循环接受信息(已连接) 21 while True: 22 try: #防止接受的数据为空(None),导致异常 23 data = conn.recv(buffer_size) 24 if not data: 25 break 26 print("客户端:\n\t",data.decode("utf-8")) #解码 27 conn.send(data.upper()) 28 except Exception: 29 break #退出信息循环层 30 31 conn.close() #结束与client的连接,等待下一次连接 32 33 tcp_server.close()
1 # 客户端 2 from socket import * 3 4 ip_port = (‘127.0.0.1‘,8080) 5 back_log = 5 6 buffer_size = 1024 7 8 tcp_client = socket(AF_INET,SOCK_STREAM) 9 tcp_client.connect(ip_port) #连接服务端 10 11 # 循环接受信息 12 while True: 13 msg = input(">>:") 14 if not msg: #发送信息为空时 15 continue 16 17 tcp_client.send(msg.encode("utf-8")) 18 print("客户端已发送消息") 19 20 data = tcp_client.recv(buffer_size) 21 print("客户端已收到消息",data.decode("utf-8")) 22 23 tcp_client.close()
注意:
- 最大连接数指的是,最多可以支持多人挂起,并非同时进行连接
- 当client断开连接时,会发生异常,需要做异常处理
- 对于TCP协议,当accept接受到的数据为空时,server端会堵塞
2、使用UDP协的通信的基本格式
1 # 服务器 2 from socket import * 3 ip_port = ("127.0.0.1",8080) 4 buffer_size = 1024 5 6 udp_server = socket(AF_INET,SOCK_DGRAM) 7 udp_server.bind(ip_port) 8 9 print("服务器启动...") 10 11 while True: 12 data,addr = udp_server.recvfrom(buffer_size) 13 print(data.decode("utf-8")) 14 15 udp_server.sendto(data.upper(),addr)
1 # 客户端 2 from socket import * 3 4 ip_port = ("127.0.0.1",8080) 5 buffer_size = 1024 6 7 udp_client = socket(AF_INET,SOCK_DGRAM) 8 9 10 while True: 11 msg = input(">>:") 12 if not msg: 13 continue 14 udp_client.sendto(msg.encode("utf-8"),ip_port) 15 16 data,addr = udp_client.recvfrom(buffer_size) 17 print(data.decode("utf-8"))
注意:
- 使用UDP通信时,并不需要连接,而是在发送数据时,才指定IP地址和端口,所有server接受为空时,并不报错
二、socketserver的使用
利用socketserver达到并发的效果(针对TCP)
1 import socketserver 2 3 class MyServer(socketserver.BaseRequestHandler): #必须继承这个类 4 def handle(self): #必须重写这个方法 5 print("conn is:",self.request) 6 print("addr is:",self.client_address) 7 8 while True: 9 try: 10 data = self.request.recv(1024) 11 print("客户端:\n\t",data) 12 13 self.request.sendall(data.upper()) 14 except Exception as e: 15 print(e) 16 break 17 18 if __name__ == ‘__main__‘: 19 s = socketserver.ThreadingTCPServer(("127.0.0.1",8080),MyServer) 20 s.serve_forever() #启动
client端不变
注意:
- 即使,client断开连接,发生异常,server也会继续跟其他client通信
- 但是,也具有socket的通行,server接受数据为空时,会发生堵塞
三、粘包的发生
1、发送的多条数据远小于缓存区的大小且时间间隔短(为了减少延时,tcp通过算法会把数据合并发送)
1 from socket import * 2 import time 3 4 ip_port = ("127.0.0.1",8080) 5 back_log = 5 6 buffer_size = 1024 7 8 tcp_client = socket(AF_INET,SOCK_STREAM) 9 tcp_client.connect(ip_port) 10 11 # 第一种粘包 12 tcp_client.send("Hello".encode("utf-8")) 13 tcp_client.send("world".encode("utf-8")) 14 tcp_client.send("lilong".encode("utf-8"))
1 from socket import * 2 import time 3 4 ip_port = ("127.0.0.1",8080) 5 back_log = 5 6 buffer_size = 1024 7 8 tcp_client = socket(AF_INET,SOCK_STREAM) 9 tcp_client.connect(ip_port) 10 11 # 第一种粘包 12 tcp_client.send("Hello".encode("utf-8")) 13 14 time.sleep(1) 15 16 tcp_client.send("world".encode("utf-8")) 17 18 time.sleep(1) 19 20 tcp_client.send("lilong".encode("utf-8"))
2、发送的数据大于缓存区的大小
1 from socket import * 2 3 ip_port = ("127.0.0.1",8080) 4 back_log = 5 5 buffer_size = 1024 6 7 tcp_server = socket(AF_INET,SOCK_STREAM) 8 tcp_server.bind(ip_port) 9 tcp_server.listen(back_log) 10 11 conn,addr = tcp_server.accept() 12 13 data1 = conn.recv(5) 14 print("第1个数据",data1.decode("utf-8")) 15 16 data2 = conn.recv(5) 17 print("第1个数据",data2.decode("utf-8")) 18 19 data3 = conn.recv(5) 20 print("第1个数据",data3.decode("utf-8"))
1 from socket import * 2 import time 3 4 ip_port = ("127.0.0.1",8080) 5 back_log = 5 6 buffer_size = 1024 7 8 tcp_client = socket(AF_INET,SOCK_STREAM) 9 tcp_client.connect(ip_port) 10 11 tcp_client.send("Helloworld!lilong".encode("utf-8"))
四、粘包的解决方法
1、直接告诉客户端发送数据的字节
server端(核心代码):
1 length = len(data) 2 conn.send(str(length).encode("utf-8")) 3 4 client_ready = conn.recv(1024)#卡住 5 if client_ready == b"ready": #同步 6 conn.send(data)
client端(核心代码):
1 length = tcp_client.recv(buffer_size) 2 tcp_client.send(b"ready") 3 4 length = int(length.decode("utf-8")) 5 6 recv_size = 0 7 recv_msg = b"" 8 while recv_size < length: 9 recv_msg += tcp_client.recv(buffer_size) 10 recv_size = len(recv_msg) 11 12 print("命令的结果是:\n\t",recv_msg.decode("gbk"))
2、以4个字节为最大发送字节,发送数据
server端(核心代码):
1 length = len(cmd_res) 2 3 data_length = struct.pack(‘i‘,length) 4 conn.send(data_length) 5 conn.send(cmd_res)
client端(核心代码):
1 length_data = tcp_client.recv(4) 2 length = struct.unpack("i",length_data)[0] 3 4 recv_msg = ‘‘.join(iter(partial(tcp_client.recv,1024),b"")) 5 6 print("命令的结果是:\n\t", recv_msg.decode("gbk"))
需要导入两个包
1 import struct 2 from functools import partial
以上是关于socket编程的主要内容,如果未能解决你的问题,请参考以下文章