网络编程

01基于tcp协议的socket通信

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 买电话
# socket.SOCK_STREAM 流式协议 就是TCP协议
phone.bind(('127.0.0.1', 8080))  # 买电话卡

phone.listen(5)  # 开机。
# 5 不是链接数,链接可以产生N个,同一时刻只能监听5个请求。
# print(111)
conn, addr = phone.accept() # 等待接电话  # 阻塞状态
# print(222)
print(conn, addr)  # conn 代表的是socket通信的对象,一个管道

client_data = conn.recv(1024)  # 交流过程
print(client_data)
conn.send(client_data.upper())

conn.close()
phone.close()
服务端
import socket

phone = socket.socket()  # 买电话

phone.connect(('127.0.0.1', 8080))  # 拨号

msg = input('>>>').strip()
phone.send(msg.encode('utf-8'))
server_data = phone.recv(1024)  # 限制的是最大接收字节数。
print(server_data)
phone.close()
客户端

但是我们发现这个只能执行一次,就断开了,于是有了下面的通信循环。

02通信循环

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)  #防止报OSError
phone.bind(('127.0.0.1', 8080))

phone.listen(5)
conn, addr = phone.accept()

while 1:
    try:
        client_data = conn.recv(1024)
        print(client_data)
        conn.send(client_data + b'SB')   #SB要转换成bytes类型
    except Exception:   #排除其他所有的错误
        break
    
conn.close()
phone.close()
服务端
import socket

phone = socket.socket()  # 买电话

phone.connect(('127.0.0.1', 8080))  # 拨号
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break    #选择正常退出
    phone.send(msg.encode('utf-8'))
    server_data = phone.recv(1024)  # 限制的是最大接收字节数。
    print(server_data.decode('utf-8'))
phone.close()
客户端

但是又发现当客户端输入q退出时,服务端就自动端口了,于是有了下面的链接循环。

03链接循环

import socket

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))

phone.listen(5)
while 1:
    conn, addr = phone.accept()
    print(addr)
    while 1:
        try:
            client_data = conn.recv(1024)
            print(client_data)
            print(222)
            conn.send(client_data + b'SB')
            print(333)
        except Exception:
            break

    conn.close()
phone.close()
服务端
import socket

phone = socket.socket()  # 买电话

phone.connect(('127.0.0.1', 8080))  # 拨号
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break
    phone.send(msg.encode('utf-8'))
    server_data = phone.recv(1024)  # 限制的是最大接收字节数。
    print(server_data.decode('utf-8'))
phone.close()
客户端1
import socket

phone = socket.socket()  # 买电话

phone.connect(('127.0.0.1', 8080))  # 拨号
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break
    phone.send(msg.encode('utf-8'))
    server_data = phone.recv(1024)  # 限制的是最大接收字节数。
    print(server_data.decode('utf-8'))
phone.close()
客户端2

...以此类推,客户端3.4.5.6...

当客户端1输入q退出时,客户端2开始聊,客户端2输入q退出时,3.4.5....依此聊。

 

04远程执行命令

 需要引入模块:subprocess

import subprocess

obj = subprocess.Popen('dir',
                       shell=True,
                       stdout=subprocess.PIPE,
                       stderr=subprocess.PIPE,
                       )

ret = obj.stdout.read().decode('gbk')
ret1 = obj.stderr.read().decode('gbk')
print(ret)
print(ret1)

 

import socket
import subprocess
#
# obj = subprocess.Popen('dir',
#                        shell=True,
#                        stdout=subprocess.PIPE,
#                        stderr=subprocess.PIPE,
#                        )
#
# ret = obj.stdout.read().decode('gbk')
# ret1 = obj.stderr.read().decode('gbk')
# print(ret)
# print(ret1)

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))

phone.listen(5)
conn, addr = phone.accept()
while 1:
    try:
        cmd = conn.recv(1024)
        obj = subprocess.Popen(cmd.decode('utf-8'),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               )

        ret = obj.stdout.read()
        ret1 = obj.stderr.read()
        
        conn.send(ret + ret1)
    except Exception:
        break

conn.close()
phone.close()
import socket

phone = socket.socket()

phone.connect(('127.0.0.1', 8080))
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break
    elif not msg: continue
    phone.send(msg.encode('utf-8'))
    server_data = phone.recv(1024)
    print(server_data.decode('gbk'))
phone.close()

 

但是我们发现输入ipconfig之后,最终展示的结果是不全的,这就是粘包现象,下面看来看下粘包现象

 05粘包现象

粘包现象分为两种:

 # 1,流式协议。数据全部都像水一样,连在一起。 一次性发送 1519个字节(全部在客户端操作系统的缓存区)但是客户端一次只取1024字节,所以缓存区还剩495字节,下次再取。
# 粘包现象一。

import socket
import subprocess

#
# obj = subprocess.Popen('dir',
#                        shell=True,
#                        stdout=subprocess.PIPE,
#                        stderr=subprocess.PIPE,
#                        )
#
# ret = obj.stdout.read().decode('gbk')
# ret1 = obj.stderr.read().decode('gbk')
# print(ret)
# print(ret1)

phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8082))

phone.listen(5)
conn, addr = phone.accept()
while 1:
    try:
        cmd = conn.recv(1024)
        obj = subprocess.Popen(cmd.decode('utf-8'),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               )

        ret = obj.stdout.read()
        ret1 = obj.stderr.read()
        total_size = len(ret+ret1)  #len测试的是bytes类型的数据 示例见tes.py
        print(total_size)

        conn.send(ret + ret1)
    except Exception:
        break

conn.close()
phone.close()
服务端
import socket

phone = socket.socket()

phone.connect(('127.0.0.1', 8082))
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break
    elif not msg: continue  # 解决的是输入为空程序会夯住的问题。
    phone.send(msg.encode('utf-8'))
    server_data = phone.recv(1024)
    print(server_data.decode('gbk'))
phone.close()
客户端

服务端增加了一个字节:

 

 

 # 2,针对于(客户端/服务端)发送的连续的小的数据,对方会一次性接收。

 客户端给服务端发送两条数据,一条hello,一条world

#2 针对(客户端/服务端)发送的数据,他会一次性接收。

import socket
so1 = socket.socket()
so1.bind(('127.0.0.1',8888))
so1.listen(5)

conn,addr = so1.accept()
client_data1 = conn.recv(1024)
print('data1',client_data1.decode('utf-8'))
client_data2 = conn.recv(1024)
print('data2',client_data2.decode('utf-8'))
conn.send(client_data1)
conn.close()
so1.close()
服务端
#例子2:
import socket

phone = socket.socket()

phone.connect(('127.0.0.1', 8888))
phone.send(b'hello')
phone.send(b'world')

print(phone.recv(1024).decode('utf-8'))
phone.close()
客户端

发现,数据小,又连续,但是因为粘包现象都在data1上了。

 

解决方式一:让连续的数据变成不连续,不能这么做。

 但是不能这么用。

解决方式二:限定服务端获取的字节数等于发送的字节数,不行,写死了。

我们发现这两种都不行。我们要采用数据大小的具体数值+循环接收的方式

 

 

循环接受怎么办,来看一下:

# 循环接收
import socket

so1 = socket.socket()
so1.bind(('127.0.0.1', 8888))
so1.listen(5)

conn, addr = so1.accept()

client_data1 = conn.recv(5)
client_data2 = conn.recv(5)
client_data3 = conn.recv(5)
client_data4 = conn.recv(5)
print(client_data1,client_data2,client_data3,client_data4)
conn.send(client_data1)
conn.close()
so1.close()
服务端
import socket


phone = socket.socket()

phone.connect(('127.0.0.1', 8888))
phone.send(b'hellohellohellohellohello')
# phone.send(b'world')

print(phone.recv(1024).decode('utf-8'))
phone.close()
客户端

 

还有一个问题, len测试的数据大小返回的是int,你一个int怎么发送?int怎么转成bytes类型。 int 你会说先转成str,str再转成bytes。你的脸得标准,你有一个固定长度。

 

如何将一个固定的数字转成bytes类型呢,下面的来介绍。

06 解决粘包

# total_size: len(ret+ret1)  不固定的数字类型。 1519 518 10000
#咱们要发送一个: 固定的头部(数据的总大小) + 数据

引入一个模块:struct
struct  将一个数据,int,转化成固定的bytes
来看下用法

import struct
# 将一个数字转化成等长度的bytes类型。
ret = struct.pack('i', 183346)
print(ret, type(ret), len(ret))

# 通过unpack反解回来
ret1 = struct.unpack('i',ret)[0]
print(ret1, type(ret1), len(ret1))


# 但是通过struct 处理不能处理太大

ret = struct.pack('l', 4323241232132324)
print(ret, type(ret), len(ret))  # 报错

struct相关

import struct
#
# 将不固定长度的数字 ---> 固定的bytes
ret = struct.pack('i',151988)

# ret = struct.pack('q',1519324324424324324324324)  # 数据量大了就不可以
print(ret, type(ret), len(ret), sep='\n')

# 反解
ret1 = struct.unpack('i', ret)[0]
print(ret1)


# header: 可以包含多种对象:文件名,文件的MD5 文件的大小,等等
# 所以将文件头部构造成字典比较合适。
# import json
# head_dict = {
#     'filename': '二狗.avi',
#     'md5': '3243243254erew5rwef',
#     'total_size': 1343243243243243243244324324322324,
# }
#
# dict_json = json.dumps(head_dict)  #字典类型的头部转成了bytes类型
# # print(dict_json)
#
#
# header_total_size = len(dict_json)  #总长度
# print(header_total_size)
#
# ret = struct.pack('i', header_total_size)
# print(ret,len(ret))
# print(dict_dict)


# 将字典转化成json格式,---> bytes-->发送。

 

low版:

import socket
import subprocess
import struct
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8085))

phone.listen(5)
conn, addr = phone.accept()
while 1:
    try:
        cmd = conn.recv(1024)
        obj = subprocess.Popen(cmd.decode('utf-8'),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               )
        
        # 1,获取执行命令返回的结果
        ret = obj.stdout.read()
        ret1 = obj.stderr.read()
        
        # 2,计算head(总数据)大小
        total_size = len(ret+ret1)
        print(total_size)
        
        # 3,将head转换成固定长度的bytes
        head = struct.pack('i', total_size)
        
        # 4, 发送head
        conn.send(head)
        
        # 5,发送数据部分。
        conn.send(ret)
        conn.send(ret1)
        
        
    except Exception:
        break

conn.close()
phone.close()

# total_size: len(ret+ret1)  不固定的数字类型。 1519 518 10000
#咱们要发送一个:    固定的头部(数据的总大小) + 数据
# struct  将一个数据,int,转化成固定的bytes
服务端
import socket
import struct
phone = socket.socket()

phone.connect(('127.0.0.1', 8085))
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break
    elif not msg: continue
    phone.send(msg.encode('utf-8'))
    
    # 1,接收报头。
    header_bytes = phone.recv(4)
    
    # 2,反解报头。
    total_size = struct.unpack('i', header_bytes)[0] # int
    
    # 3,接收数据部分。

    data_size = 0
    res = b''
    while data_size < total_size:
        data = phone.recv(1024)  #每次接收1024个数据
        res = res + data   #b'5000个内容'  每次加1024给res,最后加到5000
        # print(res)  #"b'\r\nWindows IP \xc5\xe4\xd6\xc3\r\n\r\n\r\n\xce\xde\xcf\xdf\xbe\xd6\xd3\xf2\xcd\xf8\xca\xca\xc5\xe4\xc6\xf7 \xb1\xbe\xb5\xd8\xc1\xac\xbd\xd3* 1:\r\n\r\n "
        # print(len(res))
        data_size = data_size + len(data)  #数据大小+最后的数据大小

    print(res.decode('gbk'))
phone.close()
客户端

 

upper版:

import socket
import subprocess
import struct
import json
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)
phone.bind(('127.0.0.1', 8080))

phone.listen(5)
conn, addr = phone.accept()
while 1:
    try:
        cmd = conn.recv(1024)
        obj = subprocess.Popen(cmd.decode('utf-8'),
                               shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               )
        
        # 1,获取执行命令返回的结果,计算结果的总大小
        ret = obj.stdout.read()
        ret1 = obj.stderr.read()
        total_size = len(ret + ret1)
        
        # 2,构建header字典
        head_dict = {
            'filename': '二狗.avi',
            'md5': '3243243254erew5rwef',
            'total_size': total_size,
        }

        
        # 3,将字典转换json格式
        dict_json = json.dumps(head_dict)

        # 4, 将字典转化成bytes
        dict_bytes = dict_json.encode('utf-8')
        
        
        # 5, 计算字典bytes类型的大小
        dict_len = len(dict_bytes)
        
        # 6将bytes类型的 字典的长度转化成固定长度的bytes
        head_dict_len = struct.pack('i', dict_len)
        
        # 7,发送字典的总大小
        conn.send(head_dict_len)
        
        # 8,发送 bytes类型的字典
        conn.send(dict_bytes)
    
        # 9,发送数据部分。
        conn.send(ret)
        conn.send(ret1)
        
        
    except Exception:
        break

conn.close()
phone.close()

# total_size: len(ret+ret1)  不固定的数字类型。 1519 518 10000
#咱们要发送一个:    固定的头部(数据的总大小) + 数据
# struct  将一个数据,int,转化成固定的bytes
服务端
import socket
import struct
import json
phone = socket.socket()

phone.connect(('127.0.0.1', 8080))
while 1:
    msg = input('>>>').strip()
    if msg.upper() == 'Q': break
    elif not msg: continue
    phone.send(msg.encode('utf-8'))
    
    # 1,接收字典的大小。
    dict_size = struct.unpack('i', phone.recv(4))[0]
    
    # 2,获取报头字典json格式。
    header_dict_json = phone.recv(dict_size).decode('utf-8')
    
    # 3, 通过json 反解成 字典
    header_dict = json.loads(header_dict_json)
    
    # 4,接收数据部分。
    
    data_size = 0
    res = b''
    while data_size < header_dict['total_size']:
        data = phone.recv(1024)
        res = res + data
        data_size = data_size + len(data)

    print(res.decode('gbk'))
phone.close()
客户端

 

 





 

 

posted @ 2018-12-13 18:05  小菜鸟111  阅读(168)  评论(0编辑  收藏  举报