• 网络编程


    基于TCP协议通信套接字:

    服务端:

    import socket

    #1.买手机
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #SOCK_STEAM=>TCP流式协议,
    print(phone) #是用来接收链接请求,从而建立链接的

    #2.插手机卡
    phone.bind(('127.0.0.1',8081)) #0-65535

    #3.开机
    phone.listen(5) # 同一时刻最大请求数为5个

    # print('start....')
    #4.等待电话请求
    conn,client_addr=phone.accept() #(双向链接的套接字对象,存放客户端ip和端口的小元组)
    print(conn) # conn代表双向链接,用来收发消息
    print(client_addr)

    #5.收发消息
    data=conn.recv(1024) #1024接收的最大字节数bytes
    print('收到客户的数据',data)
    conn.send(data.upper())

    #6.挂电话链接
    conn.close()

    #7.关机
    phone.close()



    客户端:
    import socket

    #1.买手机
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #SOCK_STEAM=>TCP流式协议,

    #2.拨号
    phone.connect(('127.0.0.1',8081))

    #3.发收消息
    phone.send('hello'.encode('utf-8')) # 只能发bytes类型
    data=phone.recv(1024)
    print('收到服务端的消息: ',data)

    #4.挂电话链接
    phone.close()



    加上链接循环与通信循环:
    服务端:
    from socket import *

    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)

    # 链接循环
    while True:
    conn, client_addr = server.accept()
    print(client_addr)

    # 通信循环
    while True:
    try:
    data = conn.recv(1024)
    if len(data) == 0: break # 针对linux系统
    print('-->收到客户端的消息: ', data)
    conn.send(data.upper())
    except ConnectionResetError:
    break

    conn.close()

    server.close()


    客户端:
    from socket import *

    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8080))

    # 通信循环
    while True:
    msg=input('>>: ').strip()
    client.send(msg.encode('utf-8'))
    data=client.recv(1024)
    print(data)

    client.close()
    
    
    =========================================================================


    基于TCP协议通信套接字:
    服务端:
    import socket

    server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    server.bind(('127.0.0.1',8082))

    while True:
    data,client_addr=server.recvfrom(1024)
    print(data)
    server.sendto(data.upper(),client_addr)

    server.close()

    客户端:
    import socket

    client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

    while True:
    msg=input('>>: ').strip()
    client.sendto(msg.encode('utf-8'),('127.0.0.1',8082))
    data,server_addr=client.recvfrom(1024)
    print(data)
    
    
    =========================================================================

    粘包问题:

    须知:只有TCP有粘包现象,UDP永远不会粘包

    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

    
    

       此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都           很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

    
    
    1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
    2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
    3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略


    解决粘包终极版:
    服务端:
    from socket import *
    import subprocess
    import struct
    import json

    server = socket(AF_INET, SOCK_STREAM)
    server.bind(('127.0.0.1', 8081))
    server.listen(5)

    # 链接循环
    while True:
    conn, client_addr = server.accept()
    print(client_addr)

    # 通信循环
    while True:
    try:
    cmd = conn.recv(1024) # cmd=b'dir'
    if len(cmd) == 0: break # 针对linux系统
    obj = subprocess.Popen(cmd.decode('utf-8'),
    shell=True,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE
    )
    stdout = obj.stdout.read()
    stderr = obj.stderr.read()
    # 1. 先制作报头
    header_dic = {
    'filename': 'a.txt',
    'md5': 'asdfasdf123123x1',
    'total_size': len(stdout) + len(stderr)
    }
    header_json = json.dumps(header_dic)
    header_bytes = header_json.encode('utf-8')

    # 2. 先发送4个bytes(包含报头的长度)
    conn.send(struct.pack('i', len(header_bytes)))
    # 3 再发送报头
    conn.send(header_bytes)

    # 4. 最后发送真实的数据
    conn.send(stdout)
    conn.send(stderr)
    except ConnectionResetError:
    break

    conn.close()
    server.close()
    客户端:
    from socket import *
    import struct
    import json

    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))

    # 通信循环
    while True:
    cmd=input('>>: ').strip()
    if len(cmd) == 0:continue
    client.send(cmd.encode('utf-8'))
    #1. 先收4bytes,解出报头的长度
    header_size=struct.unpack('i',client.recv(4))[0]

    #2. 再接收报头,拿到header_dic
    header_bytes=client.recv(header_size)
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    print(header_dic)
    total_size=header_dic['total_size']

    #3. 接收真正的数据
    cmd_res=b''
    recv_size=0
    while recv_size < total_size:
    data=client.recv(1024)
    recv_size+=len(data)
    cmd_res+=data

    print(cmd_res.decode('gbk'))
    client.close()
    ===========================================================
    socketserver模块使用方法:
    基于TCP协议:
    服务端:
    import socketserver

    # 自定义类用来处理通信循环
    class MyTCPhanler(socketserver.BaseRequestHandler):
    def handle(self):
    while True:
    try:
    data = self.request.recv(1024)
    if len(data) == 0: break # 针对linux系统
    print('-->收到客户端的消息: ', data)
    self.request.send(data.upper())
    except ConnectionResetError:
    break

    self.request.close()


    if __name__ == '__main__':
    server=socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyTCPhanler)
    server.serve_forever() # 链接循环

    客户端:
    from socket import *

    client = socket(AF_INET, SOCK_STREAM)
    client.connect(('127.0.0.1', 8081))

    # 通信循环
    while True:
    # msg=input('>>: ').strip() #msg=''
    # if len(msg) == 0:continue
    # client.send(msg.encode('utf-8')) #client.send(b'')
    client.send('hello'.encode('utf-8')) #client.send(b'')
    # print('has send')
    data=client.recv(1024)
    # print('has recv')
    print(data)

    client.close()

    基于UDP协议:
    服务端:
    import socketserver

    class MyUdphandler(socketserver.BaseRequestHandler):
    def handle(self):
    data,sock=self.request #self.request相当于conn
    sock.sendto(data.upper(),self.client_address)

    if __name__ == '__main__':
    server=socketserver.ThreadingUDPServer(('127.0.0.1',8081),MyUdphandler)
    server.serve_forever()

    客户端:
    from socket import *

    client=socket(AF_INET,SOCK_DGRAM)

    while True:
    client.sendto(b'hello',('127.0.0.1',8081))
    data,server_addr=client.recvfrom(1024)
    print(data)


     
  • 相关阅读:
    PHP运行出现Notice : Use of undefined constant 的解决办法
    Winfrom 设置Panel添加滚动条
    Unable to find the wrapper ”https”
    winfrom 控件的显示隐藏方法
    winfrom 窗体控件实现二级联动
    【最小生成树】Bzoj1601[Usaco2008 Oct]灌水
    【强连通分量】Bzoj1051 HAOI2006 受欢迎的牛
    【Homework】LCA&RMQ
    【建图+最短路】Bzoj1001 狼抓兔子
    【组合数学】Bzoj2916 [Poi1997]Monochromatic Triangles
  • 原文地址:https://www.cnblogs.com/zhangpang/p/9594911.html
Copyright © 2020-2023  润新知