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不会)

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号