• python之路(12)网络编程


    前言

      基于网络通信(AF_INET)的socket(套接字)实现了TCP/UDP协议

    目录


    基于TCP协议的socket

    服务端

    #服务端
    from socket import *
    
    #AF_INIT(基于网络通信) SOCK_STREAM(TCP协议)(买手机)
    tcp_server= socket(AF_INET,SOCK_STREAM)
    
    #防止端口FIN_WAIT状态,重用ip和端口(4次挥手断开连接需要时间)
    #tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    
    #绑定ip和端口(插手机卡)
    tcp_server.bind(('127.0.0.1',8080))
    
    #可以挂起几个连接(开机)
    tcp_server.listen(5)
    
    while True:
        #服务端等待连接(等电话)
        conn, addr = tcp_server.accept()
    
        while True: #通话中
            try:
                # 接收buffer_size个字节的信息 (收消息)
                data = conn.recv(1024)
                # (发消息)
                conn.send(data.upper())
            except Exception as e: #客户端错误方式关闭连接,退出
                print(e,'客户端断开连接')
                break
    
        # 关闭连接 (挂电话)
        conn.close()
    
    # 关闭服务 (关机)
    tcp_server.close()

    客户端

    #客户端
    from socket import *
    
    #开机
    tcp_client= socket(AF_INET,SOCK_STREAM)
    #拨通电话
    tcp_client.connect(('127.0.0.1',8080))
    
    while True:
        msg = input('>>:').strip()
        #发消息
        tcp_client.send(msg.encode('utf-8'))
        #收消息
        data = tcp_client.recv(1024)
        print(data.decode('utf-8'))
    
    #关机
    tcp_client.close()
    #服务端
    from socket import *
    
    ip_port = ('127.0.0.1',8080)
    back_log = 5
    buffer_size = 1024
    
    tcp_server= socket(AF_INET,SOCK_STREAM) #AF_INIT(基于网络通信) SOCK_STREAM(TCP协议)(买手机)
    tcp_server.bind(ip_port) #绑定ip和端口(插手机卡)
    tcp_server.listen(back_log) #可以挂起几个连接(开机)
    while True:
        conn, addr = tcp_server.accept()  # 服务端等待连接(等电话)
        print('双向链接', conn)
        print('客户端地址', addr)
    
        while True:
            try:
                data = conn.recv(buffer_size)  # 接收buffer_size个字节的信息 (收消息)
                print("客户端发来的消息", data.decode('utf-8'))
                conn.send(data.upper())  # (发消息)
            except Exception:
                break
    
    conn.close() #关闭连接 (挂电话)
    tcp_server.close() #关闭服务 (关机)
    
    '''
    输出:
    双向链接 <socket.socket fd=516, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 8080), raddr=('127.0.0.1', 47586)>
    客户端地址 ('127.0.0.1', 47586)
    客户端发来的消息 chen
    '''
    服务端
    #客户端
    from socket import *
    
    ip_port = ('127.0.0.1',8080)
    back_log = 5
    buffer_size = 1024
    
    tcp_client= socket(AF_INET,SOCK_STREAM)
    tcp_client.connect(ip_port) #拨通电话
    while True:
        msg = input('>>:').strip()
        tcp_client.send(msg.encode('utf-8')) #发消息
        print('客户端已经发送消息')
        data = tcp_client.recv(buffer_size) #收消息
        print('收到服务端发送的消息',data.decode('utf-8'))
    
    tcp_client.close() #关机
    
    '''
    输出:
    >>:chen
    客户端已经发送消息
    收到服务端发送的消息 CHEN
    '''
    客户端

    基于UDP协议的socket 

    服务端  

    #服务端
    from socket import *
    
    #AF_INIT(基于网络通信) SOCK_DGRAM(数据报式)
    udp_server= socket(AF_INET,SOCK_DGRAM)
    
    #防止端口FIN_WAIT状态,重用ip和端口(4次挥手断开连接需要时间)
    #udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    
    #绑定ip和端口(插手机卡)
    udp_server.bind(('127.0.0.1',8080))
    
    while True:
        #接收消息
        data,addr = udp_server.recvfrom(1024)
    
        #按客户端ip和端口发送
        udp_server.sendto(data.upper(),addr)
    
    udp_server.close()  

    客户端

    #客户端
    from socket import *
    
    #开机
    udp_client= socket(AF_INET,SOCK_DGRAM)
    
    while True:
        msg = input('>>:').strip()
    
        #向指定ip和端口发消息
        udp_client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
        #收消息,返回数据和服务器ip
        data,addr = udp_client.recvfrom(1024)
    
    
    udp_client.close()
    

    TCP协议下粘包现象及处理  

    tcp协议下会产生粘包现象,即当前的命令结果与上一条的命令结果粘在了一起。

    产生粘包现象的原因有两个:

      1.  tcp是面向流的协议, tcp_client.recv() 设置的一次接收的size小于服务器传输过来的字节大小,当客户端从缓存的拿去接收的数据时,没有全部取走,而缓存中是以队列的形式存储,当再一次执行命令的时候会收到上一次命令结果的后半部分与当前命令结果连接在一起的结果   

      2.  tcp下使用Nagle算法优化,会将间隔较小且数据较小的数据,合成一个较大的数据块一起发送

    解决方法:粘包现象的根本是,不知道发送数据的大小 

    from socket import *
    import subprocess
    import struct
    ip_port=('127.0.0.1',8080)
    back_log=5
    buffer_size=1024
    
    tcp_server=socket(AF_INET,SOCK_STREAM)
    tcp_server.bind(ip_port)
    tcp_server.listen(back_log)
    
    while True:
        conn,addr=tcp_server.accept()
        print('新的客户端链接',addr)
        while True:
            #
            try:
                cmd=conn.recv(buffer_size)
                if not cmd:break
                print('收到客户端的命令',cmd)
    
                #执行命令,得到命令的运行结果cmd_res
                res=subprocess.Popen(cmd.decode('utf-8'),shell=True,
                                     stderr=subprocess.PIPE,
                                     stdout=subprocess.PIPE,
                                     stdin=subprocess.PIPE)
                err=res.stderr.read()
                if err:
                    cmd_res=err
                else:
                    cmd_res=res.stdout.read()
    
                #
                if not cmd_res:
                    cmd_res='执行成功'.encode('gbk')
    
                length=len(cmd_res)
                
                #给数据设置消息头表明数据的大小
                data_length=struct.pack('i',length)
                conn.send(data_length)
                conn.send(cmd_res)
                
            except Exception as e:
                print(e)
                break
    远程登录服务端
    from socket import *
    import struct
    from functools import partial
    ip_port=('127.0.0.1',8080)
    back_log=5
    buffer_size=1024
    
    tcp_client=socket(AF_INET,SOCK_STREAM)
    tcp_client.connect(ip_port)
    
    while True:
        cmd=input('>>: ').strip()
        if not cmd:continue
        if cmd == 'quit':break
    
        tcp_client.send(cmd.encode('utf-8'))
    
        #解决粘包:根据服务端传输的消息头表明数据大小,反复读取缓存缓存数据
        length_data=tcp_client.recv(4)
        length=struct.unpack('i',length_data)[0]
    
        recv_size = 0
        recv_msg = b''
        while recv_size < length:
            recv_msg += tcp_client.recv(buffer_size)
            recv_size = len(recv_msg)
    
        print('命令的执行结果是 ',recv_msg.decode('gbk'))
    
    tcp_client.close()
    远程登录客户端

    使用socketserver模块实现高并发

    tcp服务端  

    class MyServer(socketserver.BaseRequestHandler):
    
        #通信循环在在内部会调用handlle方法
        def handle(self): #在打电话中
            print('conn is: ',self.request)   #conn
            print('addr is: ',self.client_address) #addr
    
            while True:
                try:
                  #收消息
                    data=self.request.recv(1024)
                    if not data:break
                    print('收到客户端的消息是',data,self.client_address)
    
                    #发消息
                    self.request.sendall(data.upper())
    
                except Exception as e:
                    print(e)
                    break
    
    if __name__ == '__main__':
        s=socketserver.ThreadingTCPServer(('127.0.0.1',8080),MyServer) #多线程
        s.serve_forever()

    udp服务端

    import socketserver
    
    class MyServer(socketserver.BaseRequestHandler):
        def handle(self):
            print(self.request)
            print('收到客户端的消息是',self.request[0])
            self.request[1].sendto(self.request[0].upper(),self.client_address)
    
    
    if __name__ == '__main__':
        s=socketserver.ThreadingUDPServer(('127.0.0.1',8080),MyServer) 
        s.serve_forever()

      

  • 相关阅读:
    409. Longest Palindrome(计算一组字符集合可以组成的回文字符串的最大长度)
    242. Valid Anagram(两个字符串包含的字符是否完全相同)
    17. Letter Combinations of a Phone Number(电话号码的字母组合)
    模块XML真垃圾
    数据库是什么
    python项目开发规范
    面向对象之类的成员
    面向对象
    模块之 import os 模块一
    模块之序列化 import json
  • 原文地址:https://www.cnblogs.com/shuzhixia/p/10097955.html
Copyright © 2020-2023  润新知