python网络编程 - socket

1,socket通信

 

2,socket对象的参数

socket families:网络层

    socket.AF_INETIPV4

    socket.AF_INET6IPV6

    socket.AF_UNIX unix本机进程间通信

socket types:传输层

    socket.SOCK_STREAM #TCP

    socket.SOCK_DGRAM #UDP    

    socket.SOCK_RAW  #原始套接字,可以伪造IP头

    socket.SOCK_RDM #UDP,保证传到,但不保证顺序

3,黏包

场景:

单次接收设置1024,但是实际数据大于1024,多于1024的内容会再下次接收到

服务器连续两次send,可能会被放进缓冲区形成黏包

 

解决方法

计算发送内容大小,先把大小发送给接收方,发送完大小后服务器必须加阻塞确认,防止黏包

拿到内容大小后,后续如果还有多次send多问题,就可以改用精确接收内容大小防止黏包了,无需每次阻塞确认

特别注意中文str长度1,变成bytes后长度是3

4,模拟SSH

服务器端:

import socket
import os

server = socket.socket()
server.bind(('localhost', 19999))  # 绑定端口
server.listen(3)  # 监听端口,这里不是并发
while True:
    conn, addr = server.accept()  # 等待连接,进入阻塞状态
    while True:
        cmd = conn.recv(1024)  # 接收大小是1024
        if not cmd:  # linux会陷入recv死循环,建议都加上防止反复接收死循环
            break
        cmd = cmd.decode()
        cmd_res = os.popen(cmd).read()

        cmd_lens = len(cmd_res)
        conn.send(str(cmd_lens).encode())
        client_ack = conn.recv(1024)   # 加个确认防止黏包
        conn.send(cmd_res.encode()) 

        # server.close()  # 这里如果加close,server不会立即关闭,会在当前client结束后再关

客户端:

import socket

client = socket.socket()
client.connect(('localhost', 19999))
while True:
    msg = input('>>: ').strip()
    if not msg:
        continue
    elif msg == 'exit':  # 退出循环,关闭连接
        break

    client.send(msg.encode())  # 只能send bytes,默认utf-8
    cmd_res_size = client.recv(1024)
    cmd_res_size = int(cmd_res_size.decode())  # bytes -> str -> int
    client.send(b'ack')  # 发送确认,解决长度与实际data之间的黏包问题

    received_size = 0
    received_data = b''
    while received_size < cmd_res_size:
        data = client.recv(1024)
        received_size += len(data)
        received_data += data

    print(received_data.decode())

client.close()

猜你喜欢

转载自www.cnblogs.com/guxh/p/10687003.html