Python Socket网络编程详解与实践

一:基本概念

  • 服务器:提供服务的一方,负责监听特定端口,等待客户端的连接请求并提供相应服务。
  • 客户端:请求服务的一方,向服务器发起连接请求,与服务器进行数据通信以获取所需服务。
  • 套接字(Socket):网络通信的端点,可理解为网络通信中的一个接口或端口,用于在不同计算机之间发送和接收数据。
  • 三次握手

            TCP 三次握手是 TCP/IP 协议族中,建立 TCP 连接时使用的一种机制,用于确保通信双方都准备好进行数据传输,并且能够正常接收和发送数据。以下是对 TCP 三次握手的详细介绍:

    第一次握手:客户端发送 SYN 包

  • 客户端向服务器发送一个带有 SYN(Synchronize Sequence Numbers)标志位的 TCP 数据包,该数据包中还会包含一个随机生成的序列号seq=x。这个数据包的目的是向服务器表明客户端想要建立一个 TCP 连接,并初始化自己的序列号。

  • 第二次握手:服务器发送 SYN+ACK 包

  • 服务器接收到客户端的 SYN 包后,会确认客户端的请求。服务器会向客户端发送一个带有 SYN 和 ACK(Acknowledgment)标志位的 TCP 数据包。其中,SYN 标志位用于初始化服务器到客户端的连接,服务器也会随机生成一个序列号seq=y。同时,ACK 标志位用于确认客户端的 SYN 包,确认号ack=x+1,表示服务器已经成功收到客户端序列号为x的 SYN 包,期望客户端下一次发送的数据从序列号x + 1开始。

  • 第三次握手:客户端发送 ACK 包

  • 客户端收到服务器的 SYN+ACK 包后,会检查确认号是否正确以及 SYN 标志位是否正确等。如果确认无误,客户端会向服务器发送一个带有 ACK 标志位的 TCP 数据包,确认号ack=y+1,表示客户端已经成功收到服务器序列号为y的 SYN 包,期望服务器下一次发送的数据从序列号y + 1开始,而客户端自己的序列号则为seq=x+1。服务器收到这个 ACK 包后,连接就正式建立成功,双方可以开始进行数据传输了。

  • 通过这三次握手,客户端和服务器就能够确认彼此的接收和发送能力,从而建立起可靠的 TCP 连接,为后续的数据传输提供了基础。TCP 三次握手的核心目的就是实现通信双方的同步,确保连接的可靠性和稳定性,防止出现数据丢失或混乱等问题。

    二.Python 中的 socket 模块

    Python 标准库中的socket模块用于进行 Socket 编程,常用函数和方法如下:

     

  • socket.socket():创建一个新的 Socket 对象。
  • socket.bind(address):将 Socket 绑定到指定的地址(IP 地址和端口号)。
  • socket.listen(backlog):使 Socket 处于监听状态,等待连接请求,backlog指定了连接队列的最大长度。
  • socket.accept():接受连接请求,返回一个新的 Socket 对象和客户端地址。
  • socket.connect(address):连接到指定地址的服务器。
  • socket.send(bytes):发送数据,参数bytes是要发送的字节数据。
  • socket.recv(bufsize):接收数据,bufsize指定了接收数据的缓冲区大小。
  • 三.socket编程

    3.1 TCP编程

    tcp服务器端

    import socket
    
    def start_server():
        # 创建一个TCP/IP Socket
        server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 绑定Socket到地址(IP地址和端口号)
        server_address = ('localhost', 65432)
        server_socket.bind(server_address)
        # 监听连接请求
        server_socket.listen(1)
        print('服务器启动,等待连接...')
    
        while True:
            # 接受连接请求
            client_socket, client_address = server_socket.accept()
            try:
                print('连接自:', client_address)
                # 接收数据
                data = client_socket.recv(1024)
                print('接收到的数据:', data.decode())
                # 发送响应数据
                response = 'HTTP/1.0 200 OK\r\n\r\nHello, World!'
                client_socket.sendall(response.encode())
            finally:
                # 关闭连接
                client_socket.close()
    
    if __name__ == '__main__':
        start_server()

    tcp客户端

    import socket
    
    def start_client():
        # 创建一个TCP/IP Socket
        client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # 连接到服务器
        server_address = ('localhost', 65432)
        print('连接到服务器:', server_address)
        client_socket.connect(server_address)
        try:
            # 发送数据
            message = 'GET / HTTP/1.0\r\n\r\n'
            print('发送数据:', message)
            client_socket.sendall(message.encode())
            # 接收响应数据
            data = client_socket.recv(1024)
            print('接收到的数据:', data.decode())
        finally:
            # 关闭连接
            client_socket.close()
    
    if __name__ == '__main__':
        start_client()

    3.2 UDP编程

    udp服务器端

    import socket
    
    udp_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udp_server.bind(('127.0.0.1', 9090))
    print("UDP服务器启动中...")
    
    while True:
        data, addr = udp_server.recvfrom(1024)
        print(f"来自 {addr} 的消息:{data.decode()}")
        udp_server.sendto(b"消息已收到", addr)

    udp客户端

    import socket
    
    udp_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server_address = ('127.0.0.1', 9090)
    message = "你好,服务器!"
    udp_client.sendto(message.encode(), server_address)
    response, _ = udp_client.recvfrom(1024)
    print(f"服务器响应:{response.decode()}")
    udp_client.close()

    四.模拟ssh协议实现命令执行并返回执行结果

    客户端代码

    # -*- coding: utf-8 -*-
    # Author: s0rin
    import socket
    import time
    
    def main(target,port):
        sk = socket.socket()
        sk.connect((target,port))
        while True:
            date = input("输入命令>>>")
            sk.send((date.encode()))
    
            #版本1
            res = sk.recv(1024)
            print("字节长度:",len(res))
            print("执行命令结果:{}".format(res.decode()))
    
            #版本2
            cmd_ret_bytes_len=sk.recv(1024)
            cmd_res_len = int(cmd_ret_bytes_len.decode())
            recv_num=0
            while recv_num<cmd_res_len:
                data = sk.recv(1024)
                print(data.decode())
                recv_num+=len(data)
            print("data的长度:",recv_num,cmd_res_len)
    
    if __name__ == '__main__':
        target = "127.0.0.1"
        port = 6666
        main(target,port)

    服务端代码

    # -*- coding: utf-8 -*-
    # Author: s0rin
    import socket
    import subprocess
    import time
    import struct
    
    def main(ip_port):
        sock = socket.socket()
        sock.bind((ip_port))
        sock.listen(10)
        while True:
            conn ,addr = sock.accept()
            print("客户端{}建立连接".format(addr))
            while True:
                cmd = conn.recv(1024)
                if not cmd:
                    print(f"{conn.getpeername()}客户端推出")
                print("执行命令",cmd.decode("gbk"))
    
                #版本1
                cmd_ret_bytes = subprocess.getoutput(cmd).encode()
                conn.send(cmd_ret_bytes)
    
                #版本2
                cmd_ret_bytes = subprocess.getoutput(cmd).encode()
                print("响应字节数",len(cmd_ret_bytes))
                cmd_ret_bytes_len = str(len(cmd_ret_bytes)).encode()
                conn.send(cmd_ret_bytes_len)
                conn.send(cmd_ret_bytes)
    
    if __name__ == '__main__':
        ip_port = ("127.0.0.1",6666)
        main(ip_port)

    作者:S_Sorin

    物联沃分享整理
    物联沃-IOTWORD物联网 » Python Socket网络编程详解与实践

    发表回复