我的成长磨练,每天写博客,年轻人,卷起袖子,来把手弄脏吧! ------ 博客首页

socket套接字

一、用套接字实现简单通信

1.1服务端

import socket

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
conn,addr = soc.accept()
#接收数据,最大为1024字节
data= conn.recv(1024)
# 打印客户端发来的信息
print('客户端发来的数据:',data)
#发送信息
conn.send(b'sjdiuamjnd')
# 关闭通信
conn.close()
#关闭连接
soc.close()

1.2 客户端

import socket

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
soc.send(b'adndhbv')
#接收1024个字节
data = soc.recv(1024)
#打印接收的数据
print('服务端接受的数据:',data)
#关闭连接
soc.close()

二、用套接字实现通信循环


由于简单通信,客户端和服务器发一次数据就端来连接了,我们现在要用一种方法让他实现,客户端一直向服务器发消息,并且在服务器上显示内容而连接不断开。

2.1服务端

import socket

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
conn,addr = soc.accept()
#接收数据,最大为1024字节
while True:
    try:
        data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
        # 打印客户端发来的信息
        print('客户端发来的数据:',data)
    except Exception:  #异常处理
        break
        conn.close()        # 关闭通信
#关闭连接
soc.close()

2.2客户端

import socket

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data = input('请输入你要发送的数据>>>')
    data= bytes(data,encoding='utf8')  #将数据转化为bytes格式
    soc.send(data)
#关闭连接
soc.close()

三、用套接字实现连接循环


由于通信循环也只是一个客户端和一个服务器进行交互,如果有其他的客户端想要连接这个服务端,就连不进去,我们怎样去实现一台服务器可以和不仅是一个客户端建立连接呢?我们就需要连接循环,只要一个客户端和服务器断了,服务器还可以等待其他客户端来连接它。
### 3.1服务端

import socket

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)   #半连接池的大小
#准备接收数据
while True:  #加入循环
    print('等待客户端连接>>>>')
    conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
    print(f'客户端{addr}连接成功')
    #接收数据,最大为1024字节
    while True:
        try:
            data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
            # 打印客户端发来的信息
            print('客户端发来的数据:',data)
            # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
            if len(data) == 0:
                break
        except Exception:  #异常处理
            break
            conn.close()  # 关闭通信
#关闭连接
soc.close()
-------------------------------------------------------------------------------------
等待客户端连接>>>>
客户端('192.168.11.195', 51460)连接成功
客户端发来的数据: b'xcv'
客户端发来的数据: b'c'
等待客户端连接>>>>
客户端('192.168.11.195', 51467)连接成功
客户端发来的数据: b'zxcv'

### 3.2 客户端

import socket

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data = input('请输入你要发送的数据>>>')
    data= bytes(data,encoding='utf8')  #将数据转化为bytes格式
    soc.send(data)
#关闭连接
soc.close()

四、模拟SSH(远程执行命令)功能


4.1 subprocess模块

  • 什么是subprocess模块?

    执行系统命令的模块

import subprocess  #导入模块

#执行系统dir命令,把执行的正确结果放到stdout管道中,把错误的命令放在stderr管道中(dir:查看当前文件夹下的所有文件,tasklist:查看当前正在运行的系统任务)
obj=subprocess.Popen('dir',shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
#拿到正确结果的管道,读出里面的内容
yes = obj.stdout.read()
no = obj.stdeer.read()
#windows的命令是gbk编码,所以用gbk解码
print('错误信息',str(yes,encoding= 'gbk'))   
print('错误信息',str(no,encoding= 'gbk'))   

4.2服务端

import socket
import subprocess

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True:  #加入循环
    print('等待客户端连接>>>>')
    conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
    print(f'客户端{addr}连接成功')
    #接收数据,最大为1024字节
    while True:
        try:
            data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
            # 打印客户端发来的信息
            print('客户端发来的数据:',data)
            # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
            if len(data) == 0:
                break
            print(data)
            obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
            msg = obj.stdout.read()   #将数据读出来
            conn.send(msg)   #将读取的数据通过网络发送给c端
            print(111)
        except Exception:  #异常处理
            break
            conn.close()  # 关闭通信
#关闭连接
soc.close()

4.3客户端

import socket

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
    data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
    soc.send(data_inp)  #将转化好的数据发送到服务端
    data_rec = soc.recv(1024)  #接收来自服务端的数据数据
    print(str(data_rec,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码
#关闭连接
soc.close()

五、粘包问题


我们先来看看一个粘包的例子

服务器
---------------------------------------------------------------------------
import socket

soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True:  #加入循环
    print('等待客户端连接>>>>')
    conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
    print(f'客户端{addr}连接成功')
    #接收数据,最大为1024字节
    while True:
        try:
            data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
            # 打印客户端发来的信息
            print('客户端发来的数据:',data)
            # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
            if len(data) == 0:
                break
        except Exception:  #异常处理
            break
            conn.close()  # 关闭通信
#关闭连接
soc.close()
-----------------------------------------------------------------
等待客户端连接>>>>
客户端('192.168.11.195', 51569)连接成功
客户端发来的数据: b'aaa'
客户端发来的数据: b'bbbccc'
客户端
---------------------------------------------------------------------------------------
import socket

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data = input('请输入你要发送的数据>>>')
    data= bytes(data,encoding='utf8')  #将数据转化为bytes格式
    soc.send(b'aaa')
    soc.send(b'bbb')
    soc.send(b'ccc')
#关闭连接
soc.close()

5.1 发生粘包的两种情况

  • 发送端需要等缓冲区满才发送出去,造成粘包(发送时间间隔很短,数据又很小,会合到一起,产生粘包)
  • 接收端不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次手的时候还是从缓冲区呐上次一流的数据,产生粘包)

5.2 解决粘包的处理办法

1)发送端在发送数据前让接收端知道字节流(数据)的大小,为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。

服务器
-----------------------------------------------------------------------------------------
import socket
import subprocess
import struct
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True:  #加入循环
    print('等待客户端连接>>>>')
    conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
    print(f'客户端{addr}连接成功')
    #接收数据,最大为1024字节
    while True:
        try:
            data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
            # 打印客户端发来的信息
            print('客户端发来的数据:',data)
            # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
            if len(data) == 0:
                break
                print(data)
            obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
            msg = obj.stdout.read()   #将数据读出来

            # 先取出要发送数据长度length     #接下来是给客户端数据
            length = len(msg)
            # head 是固定四个字节
            data_head = struct.pack('i', length)   #将数据的长度的信息打包压缩成4个字节,作为数据头,先发过去
            # 发了数据头
            conn.send(data_head)
            # 发送真正的内容
            conn.send(msg)   #将读取的数据通过网络发送给c端
        except Exception:  #异常处理
            break
    conn.close()  # 关闭通信
#关闭连接
soc.close()
客户端
-------------------------------------------------------------------------------------
import socket
import struct  ##把一个数字打包成固定长度的4字节

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
    data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
    soc.send(data_inp)  #将转化好的数据发送到服务端
    #接下来是收服务器发来的数据
    data_head = soc.recv(4)        #先收四个字节,作为数据头部

    # 对数据头解压缩,得到数据的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
    length= struct.unpack('i',data_head)[0]

    count = 0
    data_total = b''   #数据先定义为空
    while count<length:
        if length<1024:  #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
            data = soc.recv(length)
            break
        else:#如果接收的数据小于1024
            if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024  ,再收1024
                data= soc.recv(1024)
            else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
                data= soc.recv(length-count)

        data_total += data
        count +=len(data)
    print(str(data_total,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码


#关闭连接
soc.close()import socket
import struct  ##把一个数字打包成固定长度的4字节

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
    data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
    soc.send(data_inp)  #将转化好的数据发送到服务端
    #接下来是收服务器发来的数据
    data_head = soc.recv(4)        #先收四个字节,作为数据头部

    # 对数据头解压缩,得到数据的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
    length= struct.unpack('i',data_head)[0]

    count = 0
    data_total = b''   #数据先定义为空
    while count<length:
        if length<1024:  #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
            data = soc.recv(length)
            break
        else:#如果接收的数据小于1024
            if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024  ,再收1024
                data= soc.recv(1024)
            else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
                data= soc.recv(length-count)

        data_total += data
        count +=len(data)
    print(str(data_total,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码


#关闭连接
soc.close()

2)我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个字节足够用了)

  • 发送时:

    先发报头长度

    再编码报头内容然后发送

    最后发真实内容

  • 接收时:

    先手报头长度,用struct取出来

    根据取出的长度收取报头内容,然后解码,反序列化

    从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

服务端
--------------------------------------------------------------------------------
import socket
import subprocess
import struct
import json
soc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP地址和端口号
soc.bind(('192.168.11.195',8080))
#处于监听状态
soc.listen(5)
#准备接收数据
while True:  #加入循环
    print('等待客户端连接>>>>')
    conn,addr = soc.accept()  #每次都会卡到这里,有客户端连接才能执行下一步
    print(f'客户端{addr}连接成功')
    #接收数据,最大为1024字节
    while True:
        try:
            data= conn.recv(1024)  #每次都会卡到这里,有数据发过来的时候才会执行
            # 打印客户端发来的信息
            print('客户端发来的数据:',data)
            # 处理linux客户端断开,如果在window下这段代码根本不会执行(即便是客服端发了空,这个地方也不会走到)
            if len(data) == 0:
                break
                print(data)
            obj = subprocess.Popen(str(data,encoding='utf8'),shell = True,stdout = subprocess.PIPE,stderr = subprocess.PIPE)
            msg = obj.stdout.read()   #将数据读出来

            # 先取出要发送数据长度length     #接下来是给客户端传数据)
            dic= {'size':len(msg)}   #定义一个字典,把数据长度放到字典里面
            dic_bytes = (json.dumps(dic).encode('utf8'))   #将字典序列化,转为bytes格式
            # head 字典的长度是固定四个字节的长度
            data_head = struct.pack('i', len(dic_bytes))   #将数据字典的长度的信息打包压缩成4个字节,作为数据头,先发过去
            # print(data_head)   #b'\x0f\x00\x00\x00'
            # 发送数据头
            conn.send(data_head)   #发送4个字节的长度
            # 发送真正的数据头部
            conn.send(dic_bytes)
            #发送真正的数据内容
            conn.send(msg)   #将读取的数据通过网络发送给c端

        except Exception:  #异常处理
            break
    conn.close()  # 关闭通信
#关闭连接
soc.close()
客户端
-------------------------------------------------------------------------------------
import socket
import struct  ##把一个数字打包成固定长度的4字节
import json

soc = socket.socket()
#连接IP地址和端口号
soc.connect(('192.168.11.195',8080))
#发送消息
while True:  #加入循环
    data_inp = input('请输入你要发送的数据>>>')  #输入需要执行的命令
    data_inp= bytes(data_inp,encoding='utf8')  #将数据转化为bytes格式
    soc.send(data_inp)  #将转化好的数据发送到服务端(接下来其实就是把这一句做一个改造,让他更牛x)
    #接下来是收服务器发来的数据
    head_dic_len = soc.recv(4)        #先收四个字节,这四个字节是头部字典的长度
    # 对头部字典解压缩,得到字典的长度有多长,因为解压缩出来的数据是一个元组,所以要取出第一个数据才知道数据的长度
    dic_len= struct.unpack('i',head_dic_len)[0]
    dic_byte = soc.recv(dic_len)  # byte 字典的长度,#收真正的头部字典
    data_head = json.loads(dic_byte)  #反序列化
    print(data_head)    #是一个字典{'size': 17396}
    length = data_head['size']

    count = 0
    data_total = b''   #数据先定义为空
    print(length)    #17396
    while count<length:
        if length<1024:  #如果接收的数据小于1024,直接接收数据大小,也就是把所有数据都接收到
            data = soc.recv(length)
            break
        else:#如果接收的数据小于1024
            if length-count>=1024:#总数据长度-count(目前收到多少,count就是多少) 如果还大于1024  ,再收1024
                data= soc.recv(1024)
            else:#总数据长度-count(目前收到多少,count就是多少) 如果小于1024,只收剩下的部分就可以
                data= soc.recv(length-count)

        data_total += data
        count +=len(data)
    print(str(data_total,encoding='gbk'))   #因为Windows是gbk编码,所以用gbk解码

#关闭连接
soc.close()
 
posted @ 2019-09-16 19:02  不喜  阅读(209)  评论(0编辑  收藏  举报