• 23、基于TCP的sorket编程、通信功能小代码、远程ssh连接(初识粘包问题)


    一、基于TCP协议的套接字通信(简单)

    • 服务端:
    import socket
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # socket.SOCK_DGRAM  ———>UDP
    
    
    # 2、绑定电话卡
    phone.bind(('172.0.0.1', 8080))
    
    # 3、开机
    phone.listen(5)
    
    print('start')
    # 4、接收链接请求
    conn, client_addr = phone.accept()
    print(client_addr)
    
    # 5、收发消息
    
    # 套接字收发消息都是二进制
    data = conn.recv(1024)  # 最大接收的字节数
    print(data)
    conn.send(data.upper())
    
    # 6、回收资源--挂电话
    conn.close()
    
    # 7、关机
    phone.close()
    
    • 客户端:
    import socket
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # socket.SOCK_DGRAM  ———>UDP
    
    # 2、打电话
    phone.connect(('127.0.0.1', 8080))
    
    # 3、发、收数据
    phone.send('hello'.encode('utf-8'))
    data = phone.recv(1024)
    print(data.decode('utf-8'))  # 收到的还是二进制
    
    # 4、关闭
    phone.close()
    

    二、加入通讯循环于链接循环(循环)

    • 服务端
    import socket
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # tcp称之为流式协议,udp称之为数据报协议SOCK_DGRAM
    # print(phone)
    
    # 2、插入/绑定手机卡
    phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    phone.bind(('127.0.0.1', 8080))
    
    # 3、开机
    phone.listen(5)  # 半连接池,限制的是请求数
    
    # 4、等待电话连接
    print('start...')
    while True:  # 连接循环
        conn, client_addr = phone.accept()  # 三次握手建立的双向连接(客户端的ip端口)
        # print(conn)
        print('已经有一个连接建立成功', client_addr)
    
        # 5、通信:收发消息
        while True:
            try:
                print('服务端正在收数据...')
                data = conn.recv(1024)
                if len(data) == 0:  # 在客户端单方面断开连接,服务端才会出现空数据的情况
                    break
                print('来自客户端的数据', data)
                conn.send(data.upper())
           except ConnectionAbortedError:
                break
        # 6、挂掉电话连接
        conn.close()
    # 7、关机
    phone.close()  # 写不写都无所谓,到不了这一步
    
    • 客户端
    import socket
    
    # 1、买手机
    phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    # 2、拨电话
    phone.connect(('127.0.0.1', 8080))  # 指定服务端ip和端口
    
    # 3、通信、发/收
    while True:
        msg = input('>>>:').strip()
        if len(msg) == 0:  # 如果输入的字节为空,服务端会一直处于收的状态
            # 为了解决这个bug
            continue
        phone.send(msg.encode('utf-8'))
        # print('has  send---->')
        data = phone.recv(1024)
        # print('has recv')
        print(data)
    
    phone.close()
    

    三、地址占用问题

    重启时如果遇到:OSERROR:Addres already in use

    这个是由于你的服务端仍然存四次挥手的time_wait状态在占用地址

    解决方法:

    • 方法一
    # 加入一条socket配置,重用ip和端口
    
    phone=socket(AF_INET,SOCK_STREAM)
    phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
    phone.bind(('127.0.0.1',8080))
    
    • 方法二(linux)
    发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
    vi /etc/sysctl.conf
    
    编辑文件,加入以下内容:
    net.ipv4.tcp_syncookies = 1
    net.ipv4.tcp_tw_reuse = 1
    net.ipv4.tcp_tw_recycle = 1
    net.ipv4.tcp_fin_timeout = 30
     
    然后执行 /sbin/sysctl -p 让参数生效。
     
    net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
    
    net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
    
    net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
    

    四、模拟ssh远程执行命令

    • 服务端
    from socket import *
    import subprocess
    
    server = socket(AF_INET, SOCK_STREAM)
    
    server.bind(('127.0.0.1', 8000))
    server.listen(5)
    
    print('start...')
    while True:
        conn, client_addr = server.accept()
    
        while True:
            print('from client:', client_addr)
            try:
                cmd = conn.recv(1024)
                if len(cmd) == 0: break
                print('cmd:', cmd)
    
                obj = subprocess.Popen(cmd.decode('utf8'),  # 输入的cmd命令
                                       shell=True,  # 通过shell运行
                                       stderr=subprocess.PIPE,  # 把错误输出放入管道,以便打印
                                       stdout=subprocess.PIPE)  # 把正确输出放入管道,以便打印
    
                res1 = obj.stdout.read()  # 打印正确输出
                res2 = obj.stderr.read()  # 打印错误输出
    
                conn.send(res1)
                conn.send(res2)
            except ConnectionAbortedError:
                break
    
        conn.close()
    
    
    • 客户端
    import socket
    
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    
    client.connect(('127.0.0.1', 8000))
    
    while True:
        msg = input('cmd:').strip()
        if len(msg) == 0:
            print('不能为空,请重新输入')
            continue
        client.send(msg.encode('utf8'))
        res = client.recv(1024)
        print(res.decode('gbk'))
    
    • 输入dir命令,由于服务端发送的字节少于1024字节,客户端可以接受
    • 输入tasklist命令,由于服务端发送字节多于1024字节,客户端只能接受部分数据,并且当你再次输入dir命令的时候,客户端会接收dir命令的结果,但是会打印上一次的剩余未发送完的数据,这个就是粘包问题(只有TCP有粘包,UDP不会)
  • 相关阅读:
    21. Merge Two Sorted Lists
    20. Valid Parentheses
    19. Remove Nth Node From End of List
    18. 4Sum
    17. Letter Combinations of a Phone Number
    16. 3Sum Closest
    15. 3Sum
    14. Longest Common Prefix
    js的Dom操作
    C/S和B/S两种软件体系结构
  • 原文地址:https://www.cnblogs.com/zhaokunhao/p/14283998.html
Copyright © 2020-2023  润新知