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不会)
原文地址:https://www.cnblogs.com/zhaokunhao/p/14283998.html