• 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("客户端:
    	",data.decode("utf-8"))  #解码
    27             conn.send(data.upper())
    28         except Exception:
    29             break             #退出信息循环层
    30 
    31     conn.close()               #结束与client的连接,等待下一次连接
    32 
    33 tcp_server.close()
    server
     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

    注意:

    1. 最大连接数指的是,最多可以支持多人挂起,并非同时进行连接
    2. 当client断开连接时,会发生异常,需要做异常处理
    3. 对于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)
    server
     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"))
    client

    注意:

    1. 使用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("客户端:
    	",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()                   #启动
    server

    client端不变

    注意:

    1. 即使,client断开连接,发生异常,server也会继续跟其他client通信
    2. 但是,也具有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"))
    server
     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"))
    client

    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"))
    server
     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"))
    client

    四、粘包的解决方法

    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("命令的结果是:
    	",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端(核心代码)(bug):

    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("命令的结果是:
    	", recv_msg.decode("gbk"))

    需要导入两个包

    1 import struct
    2 from functools import partial
  • 相关阅读:
    uniApp 实现微信小程序和app视频播放flv格式视频监控
    uniapp 给子组件传值不及时显示
    uni-app 中$refs 在app中无法使用
    使用甘特图
    背景图片加蒙版,里面内容不受影响
    MyBatis 多对一操作
    在Maven项目中使用lombok
    MyBatis使用分页
    Log4j打印日志
    paramiko 下载文件
  • 原文地址:https://www.cnblogs.com/lilong74/p/11294570.html
Copyright © 2020-2023  润新知