网络编程2()

  • 一、socket套接字1.基于文件类型的套接字家族2.基于网络类型的套接字家族3.socket模块之服务端4.socket模块之客户端
  • 1.基于文件类型的套接字家族
  • 2.基于网络类型的套接字家族
  • 3.socket模块之服务端
  • 4.socket模块之客户端
  • 二、代码优化1.send与recv2.消息自定义3.循环通信4.服务端能够持续提供服务5.消息不能为空6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)7.客户端异常退出会发送空消息(针对mac、linux)
  • 1.send与recv
  • 2.消息自定义
  • 3.循环通信
  • 4.服务端能够持续提供服务
  • 5.消息不能为空
  • 6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)
  • 7.客户端异常退出会发送空消息(针对mac、linux)
  • 三、半连接池
  • 四、黏包问题1.TCP特性2.recv3.struct模块4.解决黏包问题的终极方案:4.1.服务端:4.2.客户端:
  • 1.TCP特性
  • 2.recv
  • 3.struct模块
  • 4.解决黏包问题的终极方案:4.1.服务端:4.2.客户端:
  • 4.1.服务端:
  • 4.2.客户端:

一、socket套接字

1.基于文件类型的套接字家族

套接字家族名字:AF_UNIX

2.基于网络类型的套接字家族

套接字家族名字:AF_INET

3.socket模块之服务端

import socket
 
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address)  # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~')  # 朝客户端发送数据
data = sock.recv(1024)  # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close()  # 断链接
server.close()  # 关机

4.socket模块之客户端

import socket
 
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024)  # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server')  # 朝服务端发送数据
# 4.关闭
client.close()

二、代码优化

1.send与recv

客户端与服务端不能同时执行同一个,不能同时收或发

  • 有一个收,另外一个就是发
  • 有一个发,另外一个就是收

2.消息自定义

input获取用户数据(主要是编码解吗)

3.循环通信

给数据交互环节添加循环

4.服务端能够持续提供服务

需求:不会因为客户端断开连接而报错

方法:异常捕获

  • 一旦客户端断开连接,服务端结束通信循环调到连接处等待

5.消息不能为空

判断是否为空,是则重新输入(主要针对客户端)

6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)

from socket import SOL_SOCKET, SO_REUSEADDR
server.aetsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 加在bind前面

7.客户端异常退出会发送空消息(针对mac、linux)

针对接收的消息加判断处理即可

三、半连接池

主要为了做缓冲,避免太多的无效等待

sever.listen(5)

四、黏包问题

# 服务端
import socket
 
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
server.listen(5)
 
sock, address = server.accept()
 
print(sock.recv(5))
print(sock.recv(5))
print(sock.recv(5))


# 客户端
import socket
 
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
 
 
client.send(b'jason')
client.send(b'kevin')
client.send(b'jerry')

1.TCP特性

流式协议:所有的数据类似于水流连接在一起

2.recv

不知道即将要接收的数据量多大,如果知道的话不会产生黏包

3.struct模块

struct模块无论数据长度是多少,都可以打包成固定长度,然后基于该固定长度可以反向解析出真实长度

import struct

info = 'something to send'
print(len(info))  # 17 数据原本的长度
res = struct.pack('i', len(info))  # 将数据原本的长度打包
print(len(res))  # 打包之后的长度是4
ret = struct.unpack('i', res)  # 将打包之后固定长度为4的数据拆包
print(ret[0])  # 13 又得到了原本数据的长度

思路:

发送:

  • 先将真实数据的长度制作成固定长度 4
  • 先发送固定长度的报头
  • 再发送真实数据

接收:

  • 先接收固定长度的报头 4
  • 再根据报头解压出真实长度
  • 根据真实长度接收即可

4.解决黏包问题的终极方案:

4.1.服务端:

  • 先构造一个数据的详细字典
  • 对字典数据进行打包处理,得到一个固定长度的数据 4
  • 将上述打包之后的数据发送给客户端
  • 将字典数据发送给客户端
  • 将真实数据发送给客户端
import socket
import os
import struct
import json
 
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
 
while True:
    sock, address = server.accept()
    while True:
        # 1.先构造数据文件的字典
        file_dict = {
            'file_name': 'Jason合集.txt',
            'file_size': os.path.getsize(r'../XXX视频合集.txt'),
            'file_desc': '内容很精彩 一定不要错过',
            'file_root': 'jason'
        }
        # 2.将字典打包成固定长度的数据
        dict_json = json.dumps(file_dict)
        file_bytes_dict = len(dict_json.encode('utf8'))
        dict_len = struct.pack('i', file_bytes_dict)
        # 3.发送固定长度的字典报头
        sock.send(dict_len)
        # 4.发送真实字典数据
        sock.send(dict_json.encode('utf8'))
        # 5.发送真实数据
        with open(r'../XXX视频合集.txt', 'rb') as f:
            for line in f:
                sock.send(line)
        break

4.2.客户端:

  • 先接收固定长度的数据 4
  • 根据固定长度解析出即将要接收的字典真实长度
  • 接收字典数据
  • 根据字典数据获取真实数据长度
  • 接收真实数据长度
import socket
import struct
import json
 
client = socket.socket()
client.connect(('127.0.0.1', 8080))
 
while True:
    # 1.先接收长度为4的报头数据
    header_len = client.recv(4)
    # 2.根据报头解包出字典的长度
    dict_len = struct.unpack('i', header_len)[0]
    # 3.直接接收字典数据
    dict_data = client.recv(dict_len)  # b'{"file_name":123123123}'
    # 4.解码并反序列化出字典
    real_dict = json.loads(dict_data)
    print(real_dict)
    # 5.从数据字典中获取真实数据的各项信息
    total_size = real_dict.get('file_size')  # 32423423423
    file_size = 0
    with open(r'%s' % real_dict.get('file_name'), 'wb') as f:
        while file_size < total_size:
            data = client.recv(1024)
            f.write(data)
            file_size += len(data)
        print('文件接收完毕')
        break
————————
  • 一、socket套接字1.基于文件类型的套接字家族2.基于网络类型的套接字家族3.socket模块之服务端4.socket模块之客户端
  • 1.基于文件类型的套接字家族
  • 2.基于网络类型的套接字家族
  • 3.socket模块之服务端
  • 4.socket模块之客户端
  • 二、代码优化1.send与recv2.消息自定义3.循环通信4.服务端能够持续提供服务5.消息不能为空6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)7.客户端异常退出会发送空消息(针对mac、linux)
  • 1.send与recv
  • 2.消息自定义
  • 3.循环通信
  • 4.服务端能够持续提供服务
  • 5.消息不能为空
  • 6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)
  • 7.客户端异常退出会发送空消息(针对mac、linux)
  • 三、半连接池
  • 四、黏包问题1.TCP特性2.recv3.struct模块4.解决黏包问题的终极方案:4.1.服务端:4.2.客户端:
  • 1.TCP特性
  • 2.recv
  • 3.struct模块
  • 4.解决黏包问题的终极方案:4.1.服务端:4.2.客户端:
  • 4.1.服务端:
  • 4.2.客户端:

一、socket套接字

1.基于文件类型的套接字家族

套接字家族名字:AF_UNIX

2.基于网络类型的套接字家族

套接字家族名字:AF_INET

3.socket模块之服务端

import socket
 
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池(暂且忽略)
server.listen(5)
# 4.开业 等待接客
sock, address = server.accept()
print(sock, address)  # sock是双向通道 address是客户端地址
# 5.数据交互
sock.send(b'hello big baby~')  # 朝客户端发送数据
data = sock.recv(1024)  # 接收客户端发送的数据 1024bytes
print(data)
# 6.断开连接
sock.close()  # 断链接
server.close()  # 关机

4.socket模块之客户端

import socket
 
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
# 3.数据交互
data = client.recv(1024)  # 接收服务端发送的数据
print(data)
client.send(b'hello sweet server')  # 朝服务端发送数据
# 4.关闭
client.close()

二、代码优化

1.send与recv

客户端与服务端不能同时执行同一个,不能同时收或发

  • 有一个收,另外一个就是发
  • 有一个发,另外一个就是收

2.消息自定义

input获取用户数据(主要是编码解吗)

3.循环通信

给数据交互环节添加循环

4.服务端能够持续提供服务

需求:不会因为客户端断开连接而报错

方法:异常捕获

  • 一旦客户端断开连接,服务端结束通信循环调到连接处等待

5.消息不能为空

判断是否为空,是则重新输入(主要针对客户端)

6.服务端频繁重启可能会报端口被占用的错(主要针对mac电脑)

from socket import SOL_SOCKET, SO_REUSEADDR
server.aetsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 加在bind前面

7.客户端异常退出会发送空消息(针对mac、linux)

针对接收的消息加判断处理即可

三、半连接池

主要为了做缓冲,避免太多的无效等待

sever.listen(5)

四、黏包问题

# 服务端
import socket
 
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
server.listen(5)
 
sock, address = server.accept()
 
print(sock.recv(5))
print(sock.recv(5))
print(sock.recv(5))


# 客户端
import socket
 
# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('127.0.0.1', 8080))
 
 
client.send(b'jason')
client.send(b'kevin')
client.send(b'jerry')

1.TCP特性

流式协议:所有的数据类似于水流连接在一起

2.recv

不知道即将要接收的数据量多大,如果知道的话不会产生黏包

3.struct模块

struct模块无论数据长度是多少,都可以打包成固定长度,然后基于该固定长度可以反向解析出真实长度

import struct

info = 'something to send'
print(len(info))  # 17 数据原本的长度
res = struct.pack('i', len(info))  # 将数据原本的长度打包
print(len(res))  # 打包之后的长度是4
ret = struct.unpack('i', res)  # 将打包之后固定长度为4的数据拆包
print(ret[0])  # 13 又得到了原本数据的长度

思路:

发送:

  • 先将真实数据的长度制作成固定长度 4
  • 先发送固定长度的报头
  • 再发送真实数据

接收:

  • 先接收固定长度的报头 4
  • 再根据报头解压出真实长度
  • 根据真实长度接收即可

4.解决黏包问题的终极方案:

4.1.服务端:

  • 先构造一个数据的详细字典
  • 对字典数据进行打包处理,得到一个固定长度的数据 4
  • 将上述打包之后的数据发送给客户端
  • 将字典数据发送给客户端
  • 将真实数据发送给客户端
import socket
import os
import struct
import json
 
server = socket.socket()
server.bind(('127.0.0.1', 8080))
server.listen(5)
 
while True:
    sock, address = server.accept()
    while True:
        # 1.先构造数据文件的字典
        file_dict = {
            'file_name': 'Jason合集.txt',
            'file_size': os.path.getsize(r'../XXX视频合集.txt'),
            'file_desc': '内容很精彩 一定不要错过',
            'file_root': 'jason'
        }
        # 2.将字典打包成固定长度的数据
        dict_json = json.dumps(file_dict)
        file_bytes_dict = len(dict_json.encode('utf8'))
        dict_len = struct.pack('i', file_bytes_dict)
        # 3.发送固定长度的字典报头
        sock.send(dict_len)
        # 4.发送真实字典数据
        sock.send(dict_json.encode('utf8'))
        # 5.发送真实数据
        with open(r'../XXX视频合集.txt', 'rb') as f:
            for line in f:
                sock.send(line)
        break

4.2.客户端:

  • 先接收固定长度的数据 4
  • 根据固定长度解析出即将要接收的字典真实长度
  • 接收字典数据
  • 根据字典数据获取真实数据长度
  • 接收真实数据长度
import socket
import struct
import json
 
client = socket.socket()
client.connect(('127.0.0.1', 8080))
 
while True:
    # 1.先接收长度为4的报头数据
    header_len = client.recv(4)
    # 2.根据报头解包出字典的长度
    dict_len = struct.unpack('i', header_len)[0]
    # 3.直接接收字典数据
    dict_data = client.recv(dict_len)  # b'{"file_name":123123123}'
    # 4.解码并反序列化出字典
    real_dict = json.loads(dict_data)
    print(real_dict)
    # 5.从数据字典中获取真实数据的各项信息
    total_size = real_dict.get('file_size')  # 32423423423
    file_size = 0
    with open(r'%s' % real_dict.get('file_name'), 'wb') as f:
        while file_size < total_size:
            data = client.recv(1024)
            f.write(data)
            file_size += len(data)
        print('文件接收完毕')
        break