socket协议

一、socket

1、什么是socket?

socket是一个模块, 又称套接字,用来封装 互联网协议(应用层以下的层)。

2、为什么要有socket?

socket可以实现 互联网协议应用层以下的层的工作。
- 提高开发效率

3、怎么使用socket?

import socket
写socket套接字:
Client
Server

 

服务端:

import socket

sk = socket.socket()

sk.bind(('127.0.0.1', 9527))

sk.listen(5)

# 循环实现可接受多个用户访问
while True:
    conn, addr = sk.accept()
    print(addr)
  
  ## 循环实现循环通信
while True:
        # 监听代码块是否有异常出现
        try:
            data = conn.recv(1024)

            if len(data) == 0:
                break
            if data.decode('utf-8') == 'q':
                break
            print(data)

            send_data = input('服务端:')
            conn.send(send_data.encode('utf-8'))

        # 捕获异常信息,并打印  # e: 报错信息
        except Exception as e:
            print(e)
            break

    conn.close()

客户端:

import socket

sk = socket.socket()

sk.bind(('127.0.0.1', 9527))


while True:
    send_data = input('客户端:')
    sk.send(send_data.encode('utf-8'))

    data = sk.recv(1024)
    if len(data) == 0:
        break
    if data.decode('utf-8') == 'q':
        break
    print(data.decode('utf-8'))

sk.close()

 

二、粘包问题

  1. 问题: 无法确认对方发送过来数据的大小。
  2. 问题: 在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送。

解决: 确认对方数据的大小。

 

演示服务端:

# import socket
# import subprocess
#
# server = socket.socket()
#
# server.bind(
#     ('127.0.0.1', 9000)
# )
#
# server.listen(5)
#
# while True:
#     conn, addr = server.accept()
#     print(addr)
#
#     while True:
#         try:
#             # recv从内存中获取数据
#             cmd = conn.recv(10)
#
#             if len(cmd) == 0:
#                 continue
#             cmd = cmd.decode('utf-8')  # dir
#             if cmd == 'q':
#                 break
#
#             # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
#             obj = subprocess.Popen(
#                 # cmd接收解码后的字符串
#                 cmd, shell=True,
#                 stdout=subprocess.PIPE,
#                 stderr=subprocess.PIPE,
#             )
#
#             # 结果交给result变量名
#             result = obj.stdout.read() + obj.stderr.read()
#             print(len(result))
#             # gbk
#             print(result.decode('gbk'))
#
#             # 将结果返回给客户端
#             conn.send(result)
#
#         except Exception as e:
#             print(e)
#             break
#
#     conn.close()



# 问题2:
import socket

server = socket.socket()

server.bind(
    ('127.0.0.1', 9000)
)

server.listen(5)

conn, addr = server.accept()

data = conn.recv(10)
print(data)

data = conn.recv(1024)
print(data)

data = conn.recv(1024)
print(data)

客户端:

# import socket
#
# client = socket.socket()
#
# client.connect(
#     ('127.0.0.1', 9000)
# )
#
# while True:
#
#     cmd = input('客户端输入的内容: ')
#
#     client.send(cmd.encode('utf-8'))
#
#     data = client.recv(19190)
#     print(len(data))
#     print(data.decode('gbk'))
#     # print('*' * 100)
#     # data = client.recv(1024)
#     # print(data.decode('gbk'))
#     # print('*' * 100)
#     # data = client.recv(1024)
#     # print(data.decode('gbk'))
#     # print('*' * 100)
#     # data = client.recv(1024)
#     # print(data.decode('gbk'))
#     # print('*' * 100)
#     # data = client.recv(1024)
#     # print(data.decode('gbk'))
#     #
#     #

# 问题2:
import socket

client = socket.socket()

client.connect(
    ('127.0.0.1', 9000)
)

client.send(b'hello')
client.send(b'hello')
client.send(b'hello')

 

三、解决粘包问题

- 无论哪一端先发送数据
- 客户端

  1. 先制作报头,并发送 (struct)
  2. 发送真实数据

- 服务端:

  1.  接收报头,并解包获取 真实数据长度
  2. 根据真实数据长度 接收真实数据

recv(真实数据长度)

struct模块

'''
1.struct是什么?
    是一个python内置的模块, 它可以将固定长度的数据,打包成固定格式的长度。

    # 模式
    i: 4

    # 其他模式


2.作用:
    可以将真实数据,做成一个固定长度多的报头,客户端发送给服务端,服务端可以接收报头,
    然后对报头进行解包,获取真实数据的长度,进行接收即可。
'''

import struct

data = b'11111111111111'
print(len(data))
# 打包制作报头
header = struct.pack('i', len(data))
print(header)
print(len(header))


# 解包获取真实数据长度 ---> 得到一个元组,元组中第一个值是真实数据的长度
res = struct.unpack('i', header)[0]
print(res)

客户端:

import socket
import struct

client = socket.socket()

client.connect(
    ('127.0.0.1', 9000)
)

while True:

    cmd = input('客户端输入的内容: ')

    cmd_bytes = cmd.encode('utf-8')

    # 做一个报头
    header = struct.pack('i', len(cmd_bytes))
    print(len(header))
    client.send(header)

    # 待服务端确认长度后,发送真实数据长度
    client.send(cmd_bytes)

    # 接收服务端返回的报头
    headers = client.recv(4)

    # 解包,接收服务端返回的真实数据
    data_len = struct.unpack('i', headers)[0]
    result = client.recv(data_len)

    print('接收服务端返回的真实数据长度', len(result))
    print(result.decode('gbk'))

服务端:

import socket
import subprocess
import struct

server = socket.socket()

server.bind(
    ('127.0.0.1', 9000)
)

server.listen(5)

while True:
    conn, addr = server.accept()
    print(addr)

    while True:
        try:
            # 获取客户端传过来的报头
            header = conn.recv(4)
            # 解包获取真实数据长度
            data_len = struct.unpack('i', header)[0]

            # 准备接收真实数据
            cmd = conn.recv(data_len)

            if len(cmd) == 0:
                continue

            cmd = cmd.decode('utf-8')  # dir
            if cmd == 'q':
                break

            # 调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
            obj = subprocess.Popen(
                # cmd接收解码后的字符串
                cmd, shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
            )

            # 结果交给result变量名
            result = obj.stdout.read() + obj.stderr.read()

            print('发给客户端端的真实数据长度', len(result))

            # gbk
            # print(result.decode('gbk'))

            # 将结果返回给客户端
            # 做一个报头, 返回给客户端
            header = struct.pack('i', len(result))
            print(len(header))
            conn.send(header)
            conn.send(result)

        except Exception as e:
            print(e)
            break

    conn.close()

 

客户端:

import socket
import struct
import json

client = socket.socket()

client.connect(
    ('127.0.0.1', 9000)
)

while True:

    movie_name = input('请输入上传电影的名字: ')

    # 伪装电影真实数据
    movie_len = 1000000

    send_dic = {
        'movie_name': movie_name,
        'movie_len': movie_len
    }

    # 序列化
    json = json.dumps(send_dic)
    print(json)
    print(json.encode('utf-8'))
    print(len(json.encode('utf-8')))

    json_bytes = json.encode('utf-8')

    # 做一个报头
    header = struct.pack('i', len(json_bytes))

    # 先发送报头
    client.send(header)

    # 后发送真实数据
    client.send(json_bytes)

服务端:

import socket
import json
import struct

server = socket.socket()

server.bind(
    ('127.0.0.1', 9000)
)

server.listen(5)

while True:
    conn, addr = server.accept()
    print(addr)

    while True:
        try:
            # 获取客户端传过来的报头
            header = conn.recv(4)

            # 解包获取真实数据长度
            json_len = struct.unpack('i', header)[0]

            # 接收json(dict)的真实长度
            json_bytes_data = conn.recv(json_len)

            # 将bytes类型数据转为json数据
            json_data = json_bytes_data.decode('utf-8')

            # 反序列化  json ---> dict
            back_dic = json.loads(json_data)
            print(back_dic)
            print(back_dic.get('movie_len'))

            # 准备接收真实数据
            # movie_data = conn.recv(back_dic.get('movie_len'))
            # print(movie_data)

        except Exception as e:
            print(e)
            break


    conn.close()
posted @ 2019-12-05 18:00  小猪皮蛋  阅读(874)  评论(0编辑  收藏  举报