• python通过socket实现tcp/udp通信


    网络通信开发基础-脚本开发-第一关

    人生没有白走的路,你走的每一步都算数。

    任务目的

    建立 socket 连接通道,可以相互之间传输数据

    采用语言

    python

    基础原理

    OSI七层模型

    image-20220620162744236

    各层的基本作用

    image-20220620162831657

    socket通信主要实现于传输层

    传输层功能

    image-20220620163013574

    tcp和udp

    tcp

    tcp(传输控制协议),面向连接,也就是说tcp连接的建立和释放,需要经过三次握手和四次挥手。同时在建立连接后的数据传输过程中,tcp会有四个机制来保证数据可靠传输;

    1.确认应答和序列号
    2.超时重传
    3.流量控制
    4.拥塞控制

    通过这些机制,确保数据不会丢失。这很明显,是tcp面向连接的有点。因为这样,也导致存在了一些缺点。

    慢,效率低,占用系统资源高,易被攻击。

    udp

    udp(用户数据报协议),是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地把它扔到网络上。 在发送端,UDP传送数据的速度仅仅是受应用程序生成数据的速度、 计算机的能力和传输带宽的限制; 在接收端,UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息段。

    由于传输数据不建立连接,因此也就不需要维护连接状态,包括收发状态等, 因此一台服务机可同时向多个客户机传输相同的消息。

    udp的报头短,只有8个字节,相比tcp的最小20字节来说,开销更小

    udp吞吐量不受拥挤控制算法的调节,只受应用软件生成数据的速率、传输带宽、 源端和终端主机性能的限制

    因为无连接,所有udp不保证可靠交付,只保证把数据发出去就行。没有丢失重传机制。

    区别

    1、基于连接与无连接;

    2、对系统资源的要求(TCP较多,UDP少);

    3、UDP程序结构较简单;

    4、流模式与数据报模式 ;

    5、TCP保证数据正确性,UDP可能丢包;

    6、TCP保证数据顺序,UDP不保证。

    python实现socket通信

    socket介绍

    socket又称“套接字”,socket会通过udp/tcp协议来发送数据,用来实现两台机器的简单通信.socket是基于C/S架构的,所以socket网络编程,需要编写客户端程序和服务端程序。

    image-20220620164755170

    socket通信流程

    TCP通信

    image-20220620164859388

    socket关键函数介绍
    函数 描述
    socket() 获取socket类对象
    bind((hostname, port)) 在指定主机的端口绑定监听
    listen() 在绑定端口上开启监听,参数表示最大等待建立连接的个数
    accept() 等待客户端连接,连接后返回客户端地址
    send(data) 发送数据,data 是二进制数据
    recv(buffer) 表示接收数据, buffersize 是每次接收数据的长度
    close() 关闭套接字连接
    connect((hostname, port)) 设置要连接的主机名称与端口号
    代码及介绍

    server

    import socket
    # 创建一个socket对象,默认TCP套接字
    s = socket.socket()
    # 绑定端口
    s.bind(('127.0.0.1',9999))
    # 监听端口
    s.listen(10)
    print("正在连接中……")
    
    # 建立连接之后,持续等待连接
    while 1:
        # 阻塞等待连接
        sock,addr = s.accept()
        print(sock,addr)
        # 一直保持发送和接收数据的状态
        while 1:
            text = sock.recv(1024)
            # 客户端发送的数据为空的无效数据
            if len(text.strip()) == 0:
                print("服务端接收到客户端的数据为空")
            elif text.decode() == "exit":
                print("客户端下线")
                break
            else:
                print("收到客户端发送的数据为:{}".format(text.decode()))
                content = input("请输入发送给客户端的信息:")
                # 返回服务端发送的信息
                sock.send(content.encode())
        sock.close()
    

    client

    import socket
    # 创建一个socket对象
    s1 = socket.socket()
    s1.connect(('127.0.0.1',9999))
    # 不断发送和接收数据
    while 1:
        send_data = input("客户端要发送的信息:")
        # socket传递的都是bytes类型的数据,需要转换一下
        if send_data=="exit":
            info="exit"
            s1.send(info.encode())
            break
        else:
            s1.send(send_data.encode())
            # 接收数据,最大字节数1024,对返回的二进制数据进行解码
            text = s1.recv(1024).decode()
            print("服务端发送的数据:{}".format(text))
            print("------------------------------")
    

    注:服务端可以持续监听连接,客户端下线,服务端自动断开连接。客户端再次上线,服务端建立连接

    效果图

    image-20220620220755261

    UDP通信

    image-20220621103206421

    流程:

    1. 导入包socket
    2. 创建一个套接字
    3. 收发消息(优先发送)
    4. 关闭套接字
    client
    import socket
    
    
    def main():
    
       ip = "127.0.0.1"  # 对方ip和端口
       port = 8888
       other_addr = (ip, port)
       byte = 1024
       udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 创建socket
    
       while True:
           send_data = input("输入要发送的信息:").encode("utf-8")
           udp_socket.sendto(send_data, other_addr)
           """输入数据为空退出,否则进入接收状态"""
           if send_data:
               recv_data, other_addr = udp_socket.recvfrom(byte)
               print("收到来自%s的消息: %s" % (other_addr, recv_data.decode("utf-8")))
           else:
               break
       udp_socket.close() 
    
    
    if __name__ == '__main__':
       main()
    
    
    server

    流程:

    1. 导入包socket
    2. 创建一个套接字
    3. 绑定信息
    4. 收发消息(优先接收)
    5. 关闭套接字
    import socket
    
    
    def main():
        ip = ""
        port = 8888
        own_addr = (ip, port)  # 接收方端口信息
        byte = 1024
        udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        udp_socket.bind(own_addr)  # 绑定端口信息
        while True:
            recv_data, other_addr = udp_socket.recvfrom(byte)
            print("收到来自%s的消息: %s" % (other_addr, recv_data.decode("utf-8")))
            send_data = input("输入要发送的信息:").encode("utf-8")
            udp_socket.sendto(send_data, other_addr)
            """输入数据为空退出"""
            if send_data:
                pass
            else:
                break
        udp_socket.close()  # 关闭socket
    
    if __name__ == '__main__':
        main()
    
    效果图

    image-20220620222751159

    扩展-远程执行命令

    server

    from  socket import *
    import subprocess
    import struct
    
    # address=('127.0.0.1',10000)
    # back_log=5
    # buffer_size=1024
    
    tcp_server=socket(AF_INET,SOCK_STREAM)  ##定义tcp协议的socket接口
    tcp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) ##加入这条socket配置,重用IP和端口,还可以在操作系统上配置ip和端口重用
    tcp_server.bind(('127.0.0.1',10000))   ##为套接字绑定IP和端口,(IP和端口可以标示全网唯一)
    tcp_server.listen(5)  ## 可以接收的接收的连接数,并且监听端口
    
    while True:
        conn,addr=tcp_server.accept()  ##建立链接,并堵塞端口
        print(addr)
        while True:
            try:  ## 异常处理,防止客户端不正常断开
                data_cmd = conn.recv(1024)   ## 接收消息
                if not data_cmd:break   ##防止接收的消息为空
                print('收到客户端的命令'+str(data_cmd.decode()))
                ## 通过subprocess.Popen执行操作系统命令,并且把返回值重定向到subprocess.PIPE的管道零时存放
                res = subprocess.Popen(data_cmd.decode('utf-8'), shell=True,stderr=subprocess.PIPE,\
                                       stdout=subprocess.PIPE,stdin=subprocess.PIPE)
                err = res.stderr.read()  ##通过res.stderr.read()读取错误消息
                if err:
                    res_data=err
                else:
                    res_data=res.stdout.read()  ##通过res.stdout.read()读取标准输出消息
                if not res_data:
                    res_data = '执行成功'.encode('gbk')  ##防止操作系统执行命令后,返回值为空时,为客户端返回此消息
                length = len(res_data)  ## 计算返回值数据长度
                data_length = struct.pack('i', length)  ## 通过struct.pack处理返回值数据长度,并且也只能占用4个字节
                conn.send(data_length)   ## 发送返回值数据长度(4字节)作为返回给客户端消息的包头,因为是定长的,所\
                # 以客户端在接收时,第一次接收4个字节,就知道服务端返回的消息长度,就可以到自己的内存按次长度获取数据
                conn.send(res_data)  ## 发送数据
            except Exception as e:
                print(e)  ##处理方式
                break
    
    

    client

    from socket import *
    import subprocess
    import struct
    # ip_port=('127.0.0.1',10000)
    buff_size=1024
    
    tcp_client=socket(AF_INET,SOCK_STREAM)
    tcp_client.connect(('127.0.0.1',10000))   ## 链接服务端
    
    while True:
        cmd_data=input('请输入命令:')    ## 输入命令
        if not cmd_data:continue       ##判断是否输入空值,为空跳出本次循环,让用户重新输入
        if cmd_data == 'quit': break    ## 当用户输入quit时,断开链接(相当于不正常断开)
        tcp_client.send(cmd_data.encode('utf-8'))  ## 发送命令
    
        length_data = tcp_client.recv(4)    ## 第一次接收4字节,就是服务端用struct.pack封装为4字节的包头
        head_data=struct.unpack('i',length_data)[0]  ## 街封装,结果是列表类型
    
        # 处理数据
        res_length=0
        res_msg=b''
        while res_length < head_data:
            res_msg += tcp_client.recv(buff_size)
            res_length += len(res_msg)
            print(res_length)
        print('命令的执行结果是 ', res_msg.decode('gbk'))
    tcp_client.close()
    
    

    image-20220621104812312

  • 相关阅读:
    由u盘安装Ubuntu引出的事件
    初试Ubuntu
    从error 中学习
    快手一面:牛客:字符串左移
    快手一面:Leetcode:最小栈
    十三、线程池
    十二、windows临界区、其他各种mutex互斥量
    十一、std::async深入
    LeetCode(703):找出数据流中的第K大元素
    LeetCode(1003):检查替换后的字符串
  • 原文地址:https://www.cnblogs.com/liyu8/p/16396913.html
Copyright © 2020-2023  润新知