网络编程笔记(2)——socket长连接、黏包问题、subprocess模块

内容目录:

  • tcp协议的socket长连接
  • 基于udp协议的socket连接
  • 黏包问题
  • subprocess模块

内容详细

1.tcp协议的socket长连接

  • 长连接只能保持和一个client端连接,第一个断开连接后才能和后面的一个客户端连接
#Server端
import socket
sk = socket.socket()        #拿到socket的句柄
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind(('127.0.0.1',8080))
sk.listen()                 #监听是否有通信
while True:
    conn,addr = sk.accept()     #接收到client:连接:connection,地址:address

    print(addr)
    while True:
        ret = conn.recv(1024).decode('utf-8')       #接收到1024个字节,如果更改,必须为1024的倍数
        print(ret)
        if ret == 'bye':
            break
        info = input(">>>:")
        if info == 'bye':
            conn.send(b'bye')
            break
        conn.send(bytes(info,encoding='utf-8'))     #和client传信息,必须传一个byte类型

    conn.close()                #连接断开
sk.close()

#Client端--第一个客户端
import socket
sk = socket.socket()
sk.connect(('127.0.0.1',8080))
while True:
    info = input('client2>>>:')
    if info == 'bye':
        sk.send(b'bye')
        break
    sk.send(bytes(info.encode('utf-8')))
    ret = sk.recv(1024).decode('utf-8')
    print(ret)
    if ret == 'bye':
        sk.send(b'bye')
        break
sk.close()

#多个client端--代码如第一个客户端

2.基于udp协议的socket连接

  • UDP的server 不需要进行监听也不需要建立连接

  • 在启动服务之后只能被动的等待客户端发送信息过来

  • 客户端发送消息的同时还会自带地址信息

  • 消息回复的时候,不仅需要发送消息,还需要把目标的地址带上

    #Srever端
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)        #DGRAM datagram基于UDP协议
    sk.bind(('127.0.0.1',8080))
    
    msg,addr = sk.recvfrom(1024)
    print(msg.decode('utf-8'))
    sk.sendto(b'bye',addr)
    
    sk.close()
    
    #Client端
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    ip_port = ('127.0.0.1',8080)
    
    sk.sendto(b'hello',ip_port)
    ret,addr = sk.recvfrom(1024)
    print(ret.decode('utf-8'))
    
    sk.close()
    
  • 同时多个客户端聊天

    #Server:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)        #DGRAM datagram基于UDP协议
    sk.bind(('127.0.0.1',8080))
    
    while True:
        msg,addr = sk.recvfrom(1024)
        print(msg.decode('utf-8'))
        if msg == 'bye':
            break
        info = input('Server:')
        info = ('33[31mServer:%s33[0m'%info).encode('utf-8')
        sk.sendto(info,addr)
    sk.close()
    
    #Client1:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    ip_port = ('127.0.0.1',8080)
    while True:
        info = input('Client1:')
        info = ('33[32m来自Client1的消息:%s33[0m'%info).encode('utf-8')
        sk.sendto(info,ip_port)
        ret,addr = sk.recvfrom(1024)
        print(ret.decode('utf-8'))
        if ret == 'bye':
            break
    
    sk.close()
    
    #Client:
    import socket
    sk = socket.socket(type=socket.SOCK_DGRAM)
    ip_port = ('127.0.0.1',8080)
    while True:
        info = input('Client2:')
        info = ('33[34m来自Client2的消息:%s33[0m'%info).encode('utf-8')
        sk.sendto(info,ip_port)
        ret,addr = sk.recvfrom(1024)
        print(ret.decode('utf-8'))
        if ret == 'bye':
            break
    
    sk.close()
    

3.黏包问题

  • 使用TCP协议连接时:

    • 会出现黏包现象:信息过长,接收的不完整,如果没接完上一条信息则执行下一命令时会继续打印上一条信息
    • 不会丢包,信息可靠
  • 使用UDP协议连接时:

    • 信息接收不可靠,如果信息过长,则执行完命令后,多余信息直接抛弃
    • 会丢包
  • 所有的客户端执行server端下发的指令

  • 将所有的执行结果反馈回来,我来接收

    #例子:设置时间服务器
    # server端:
    # 需求
        # 写一个时间同步的服务器
        # 服务端接收请求
        # 按照client端发送的时间格式,将服务器时间转换成对应格式
        # 发送给客户端
    import time
    import socket
    
    sk = socket.socket(type=socket.SOCK_DGRAM)
    sk.bind(('127.0.0.1',9000))
    while True:
        msg,addr = sk.recvfrom(1024)
        # msg 客户端发送给server端的时间格式 "%Y-%m-%d %H:%M-%S"
        time_format = msg.decode('utf-8')
        time_str = time.strftime(time_format)
        sk.sendto(time_str.encode('utf-8'),addr)
    sk.close()
    
    #Client端:
    # client端每隔一段时间发送请求到服务端
    # 发送时间的格式
    import time
    import socket
    sk = socket.socket(type = socket.SOCK_DGRAM)
    sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9000))
    msg,addr = sk.recvfrom(1024)
    print(msg.decode('utf-8'))
    sk.close()
    
    # 方式一
    # 操作系统的定时任务 + python代码的形式
    # 方式二
    # while True + time.sleep的形式
    

4.subprocess模块

  • 读取系统命令的模块

    #Client端:
    import socket
    import subprocess
    
    sk = socket.socket()
    sk.connect(('127.0.0.1',8080))
    
    cmd = sk.recv(1024).decode('gbk')   #因为Windows传输都为GBK编码的
    res = subprocess.Popen(cmd,shell=True,          #PIPE为管道/队列,只能取一次,取完就结束了
                           stdout=subprocess.PIPE,  #stdout为命令的输出
                           stderr=subprocess.PIPE)  #stderr为命令错误的提示信息
    std_out = res.stdout.read() #从队列中取出命令
    std_err = res.stderr.read() #从队列中取出错误命令信息
    sk.send(std_out)
    sk.send(std_err)
    
    sk.close()
    
原文地址:https://www.cnblogs.com/lynlearnde/p/13471634.html