Daya Jin's Blog Python and Machine Leaning

Python Network Programming

2019-11-22


Basic

一个简单的TCP回显服务器与客户端程序。

 server.py

import socket

HOST = socket.gethostname()
PORT = 50007

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(1)    # 积压连接数
    conn, addr = s.accept()    # 返回新sock对象与连接地址
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)    # 发送所有数据

 client.py

import socket

HOST = '192.168.10.128'  # The remote host
PORT = 50007  # The same port as used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello, world')    # 发送所有数据(字节流)
    data = s.recv(1024)    # 字节流
    print(type(data))
print('Received', repr(data))    # 字节流转字串

黏包

因为TCP是面向连接的协议,数据流是以packet的形式发送与接收的,如果发送消息过长会被分成多个包,同样接收方也会分多次接收。问题的根源在于,接收方不知道发送方要发送多长的数据,若发送方连续连续发送两条数据,接收方不知道数据之间的分割,因此将两条本应分开数据(的某部分)一起接收了,于是产生了黏包现象。UDP没有黏包问题。

黏包问题的解决方式也很简单,既然问题的根源在于接收方不知道发送数据的长度,那么在发送正式数据前发送方通知接收方的数据长度即可。

Struct

Python的struct模块是一个字节流解释器跟翻译器,一个最简单的示例,现有三个数1、2、3,如果要将其分别按不同的类型转成(翻译)字节流,可以使用下述代码:

import struct

print(struct.pack('ihb', 1, 2, 3))    # b'\x01\x00\x00\x00\x02\x00\x03'
print(struct.pack('!ihb', 1, 2, 3))    # b'\x00\x00\x00\x01\x00\x02\x03'

这里解释一下程序与输出。pack的第一个参数是格式字串,其后跟的全是需要转换的数据,格式参数必须跟数据一一对应。上述程序中的!表示网络字节序,i表示int(4Byte),h表示short(2Byte),b表示signed char(1Byte)。根据ihb的解释格式,1、2、3会被分别转换成:0x000000010x00020x03,然后按照不同的字节序会得到不同的字节流。

又比如,在ping程序的ICMP报文中,前$8$个字节即为ICMP的头部,如下图所示,ICMP的头部字节码为:\x08\x00\x4c\x60\x00\x01\x00\xfb

这里对ICMP的首部进行解码,由人工计算易得首部各字段的值应该为(8, 0, 19552, 1, 251),由struct模块解码得到的值与之一致:

import struct

data = b'\x08\x00\x4c\x60\x00\x01\x00\xfb'
print(struct.unpack('!BBHHH', data))    # (8, 0, 19552, 1, 251)

Similar Posts

上一篇 QT Basic

下一篇 Python Ping

Content