网络编程与黏包问题

传输层

1.TCP协议: 

        TCP协议规定了数据在传输过程中需要遵循的规则,数据传输过程中能够遵循的协议有很多其中TCP协议和UDP协议是较为常见的两个。

        1.1:三次握手  

网络编程与黏包问题_第1张图片        

        1.2:四次握手

网络编程与黏包问题_第2张图片

 

\bullet  基于TCP传输数据因为有双向通道所以很安全

\bullet  TCP传数据不容易丢失因为有二次确认机制 ,每次发送数据都需要返回确认消息否则在一定时间会反复发送

2.UDP协议

        UDP协议发送数据没有任何的通道也没有任何的限制,但是没有TCP协议传输数据来的安全(没有二次确认机制)。

        eg:发短信重要发送了 不管别人有没有看到也不管回不回复。

应用层

        应用层提供各种各样的应用层协议,这些协议嵌入在各种我们使用的应用程序中,主要取决程序员自己采用什么策略和协议。

\bullet  常见协议有:HTTP HTTPS FTP.......

scoket套接字编程

1.scoket套接字的作用

        可以看成是两个网络应用程序进行通信时,各自通信连接中的端点

2.套接字家族

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

                套接字家族的名字:AF_UNIX

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

                套接字家族的名字:AF_INET

3.代码实现

'''服务端'''

import socket
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.bind(('0.0.0.0', 8080))  # 地址、端口
# 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()  # 关机
'''客户端'''

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

4.代码优化

        4.1:自定义消息

        4.2:循环通信

        4.3:服务端保持持续提供服务

        4.4:判断输入消息不能为空

'''服务端'''
import socket
from socket import SOL_SOCKET, SO_REUSEADDR
# 1.创建一个socket对象
server = socket.socket()  # 括号内什么都不写 默认就是基于网络的TCP套接字
# 2.绑定一个固定的地址(ip\port)
server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)  # 就是它,在bind前加
server.bind(('127.0.0.1', 8080))  # 127.0.0.1本地回环地址(只允许自己的机器访问)
# 3.半连接池
server.listen(5)
# 4.开业 等待接客
while True:
    sock, address = server.accept()
    print(sock, address)  # sock是双向通道 address是客户端地址
    # 5.数据交互
    while True:
        try:
            msg = input('请输入发送给客户端的消息>>>:').strip()
            if len(msg) == 0: continue    # 判断输入的是否是空的
            sock.send(msg.encode('utf8'))  # 朝客户端发送数据
            data = sock.recv(1024)  # 接收客户端发送的数据 1024bytes
            if len(data) == 0:      # 判断接受的是否是空
                break
            print(data.decode('utf8'))
        except ConnectionResetError:
            sock.close()
            break
'''客户端'''
import socket

# 1.产生一个socket对象
client = socket.socket()
# 2.连接服务端(拼接服务端的ip和port)
client.connect(('0.0.0.0', 8080))
# 3.数据交互
while True:
    data = client.recv(1024)  # 接收服务端发送的数据
    print(data.decode('utf8'))
    msg = input('请输入发送给客户端的消息>>>:').strip()
    if len(msg) == 0: # 判断是否是零
        msg = '暂无消息'
    client.send(msg.encode('utf8'))  # 朝服务端发送数据

5.半连接池

        当服务器在响应了客户端的第一次请求后会进入等待状态,半连接池其实就是一个容器,系统会自动将半连接放入这个容器中,可以避免半连接过多。

server.listen(5)
# 自定义数量

TCP黏包问题及解决思路

1.什么是黏包问题

        接收端不知道发送端将要传送的字节流的长度,UDP不会出现这种情况而TCP协议, 数据流会向流水一样一起, 接收端不清楚每个消息的界限, 不知道每次应该去多少字节的数据。

2.TCP特性

        流式协议:所有的数据类似于水流一样连接在一起传输(数据量小并且间隔很短就会自动组织到一起)

3.struct模块

Packing(打包成字符串)

Unpacking(解包)

网络编程与黏包问题_第3张图片

 

4.黏包解决方法:

'''客户端'''
import socket
import struct
import json

client = socket.socket()
client.connect(('0.0.0.0', 8080))

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

server = socket.socket()
server.bind(('0.0.0.0', 8080))
server.listen(5)

while True:
    sock, address = server.accept()
    while True:
        # 构造数据文件的字典
        file_dict = {
            'file_name': 'a.txt',
            'file_size': os.path.getsize(r'文件绝对路径')
        }
        # 将字典转换成json格式
        dict_json = json.dumps(file_dict)
        file_bytes_dict = len(dict_json.encode('utf8'))
        # 将字典打包成固定长度的数据
        dict_len = struct.pack('i', file_bytes_dict)
        # 发送固定长度的字典报头
        sock.send(dict_len)
        # 发送真实字典数据
        sock.send(dict_json.encode('utf8'))
        # 发送真实数据
        with open(r'读取文件', 'rb') as f:
            for line in f:
                sock.send(line)
        break

你可能感兴趣的:(网络,udp,tcp/ip)