网络编程小总结/socket套接字/TCP粘包问题

内容回顾

网络编程:使计算机之间基于网络实现数据交互

交换机:只要是接入了交换机的计算机 彼此之间都是互联的

局域网:互联网其实都是由N多个局域网连接而成

基于以太网协议通信的特点

  • 通信基本靠吼
    • 广播
    • 单播

arp协议:根据IP地址获取mac地址

OSI协议:(补充)

路由器:连接局域网

域名解析:www.baidu.com >>> 14.215.177.39:80

应用层

  • HTTP协议:超文本传输协议
  • FTP协议

UDP协议

  • 数据报协议
  • 无需建立双向通道 数据传输是不安全的
  • 将内存中的数据直接发送出去 不会做保留
  • 例如早期的qq

TCP协议类似于打电话

UDP协议类似于发短信


今日内容

socket套接字


解决端口占用问题

from socket import SOL_SOCKET,SO_REUSEADDR
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,
1) #就是它,在bind前加

服务端

import socket
​
# 买手机
server = socket.socket()  # 不传参数时,默认用的就是TCP协议
# 插电话卡 bind((host,port)) 绑定ip和port
server.bind(('127.0.0.1',8080))
# 开机 半连接池
server.listen(5)
# 接听电话 等着别人给你打电话
conn, addr = server.accept()  # 阻塞,等待
# 听别人说话 接收1024个字节数据
data = conn.recv(1024)  # 阻塞,等待
# 给别人回话
conn.send(b'hello baby~')
# 挂电话
conn.close()
# 关机
sercer.close()

客户端

import socket
​
client = socket.socket()  # 拿电话
client.connect(('127.0.0.1',8080))  # 拨号,写的是对方的ip和port
client.send(b'hello world!')  # 对别人说话
data = client.recv(1024)  # 听别人说话,接收1024个字节数据
client.close()  # 挂电话

总结:

127.0.0.1是本机回还地址,只能自己识别自己,不能访问其他

send和recv对应:

  • 不要出现两边都是相同的情况
  • recv是跟内存要数据,至于数据的来源,我们无需考虑

关于半连接池

半连接池的概念

图解:假如上述一共7个对象,首位已在被执行,有5个等待位对应5个对象,还有1个对象在等待进入等待位,server.listen(5)中的5就是5个等待位

通信循环下的服务端

import socket
​
server = socket.socket()  # 生成一个对象
server.bind(('127.0.0.1',8080))  # 绑定ip和port
server.listen(5)  # 半连接池,最大等待数目
while True:  # 使服务端24小时不停的提供服务
    conn, addr = server.accept()  # 等到别人来 conn就是类似于双向通道
    print(addr)  # ('127.0.0.1',51323) 客户端的地址
    while True:
        try:
            data = conn.recv(1024)
            print(data)  # b""是二进制数据,针对mac与linux客户端异常退出之后,服务端不会报错,只会一直接收b""
            if len(data) == 0: break
            conn.send(data.upper())
        except ConnectionResetError as e:
            printi(e)
            break
    conn.close()

通信循环下的客户端

import socket
​
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
    msg = input('>>>:').encode('utf-8')
    if len(msg) == 0: continue
    client.send(msg)
    data = client.recv(1024)
    print(data)

TCP粘包问题

TCP特点

  • 会将数据量比较小的一次性发给对方

如何将数据打包成固定的数据包(运用struct模块)?

import struct
​
res = 'ssssssssssssssssssssssss'# 当原始数据特别大时,i模式打包不了,需要更换其他模式
res1 = struct.pack('i',len(res))
print(len(res1))  # 打包后的数据长度为4
​
res2 = struct.unpack('i',res1)[0]
print(res2)  # 解包之后的真实数据长度

如果遇到数据量特别大时,又该如何解决?

import struct
# 当原始数据特别大的时候 i模式打包不了 需要更换模式?
# 如果遇到数据量特别大的情况 该如何解决?
d = {
    'name':'jason',
    'file_size':34554354353453545245345324565465465654,
    'info':'为大家的骄傲'
}
import json
json_d = json.dumps(d)
print(len(json_d))
​
res1 = struct.pack('i',len(json_d))
print(len(res1))
​
res2 = struct.unpack('i',res1)[0]
print('解包之后的',res2)

关于subprocess的应用

import subprocess
​
cmd = input('cmd>>>:')
obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
print(obj.stdout.read().decode('gbk'))  # 正确命令返回的结果
print(obj.stderr.read().decode('gbk'))  # 错误的命令返回的结果
# subprocess获取到的数据 拿完就没有了  不能重复的拿
print(obj.stdout.read().decode('gbk'))  # 正确命令返回的结果
print(obj.stderr.read().decode('gbk'))  # 错误的命令返回的结果

解决粘包问题的服务端

import socket
import subprocess
import struct
import json
​
server = socket.socket()
server.bind(('127.0.0.1',8080))
server.listen(5)
while True:
    conn, addr = server.accept()
    while True:
        try:
            cmd = conn.recv(1024)
            if len(cmd) == 0:break
            cmd = cmd.decode('uft-8')
            obj = subprocess.Popen(cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
            res = obj.stdout.read() + obj.stderr.read()
            json_d = json.dumps(d)
            # 先制作一个字典的报头
            header = struct.pack('i',len(json_d))
            # 发送字典报头
            conn.send(header)
            # 发送字典
            conn.send(json_d.encode('utf-8'))
            # 再发真实数据
            conn.send(res)
        except ConnectionResetError:
            break
    conn.close()   

解决粘包问题的客户端

import socket
import struct
import json
​
client = socket.socket()
client.connect(('127.0.0.1',8080))
while True:
    msg = input('>>>:').encode('utf-8')
    if len(msg) == 0: continue
    client.send(msg)
    # 先接收字典报头
    header_dict = client.recv(4)
    # 解析报头 获取字典的长度
    dict_size = struct.unpack('i',header_dict)[0]  # 解包时一定要加上索引0
    # 接收字典数据
    dict_bytes = client.recv(dict_size)
    dict_json = json.loads(dict_bytes.decode('utf-8'))
    # 从字典中获取真实数据
    recv_size = 0
    real_data = b''
    while recv_size < dict_json.get('file_size'):
        data = client.recv(1024)
        real_data += data
        recv_size += len(data)
    print(real_data.decode('gbk'))

解决粘包问题

服务端

  • 1.先制作一个发送给客户端的字典
  • 2.制作字典的报头
  • 3.发送字典的报头
  • 4.发送字典
  • 5.再发真实数据

客户端

  • 1.先接受字典的报头
  • 2.解析拿到字典的数据长度
  • 3.接受字典
  • 4.从字典中获取真实数据的长度
  • 5.接受真实数据

案例实现:写一个上传电影的功能

  • 1.循环打印某一个文件夹下面的所有的文件
  • 2.用户选取想要上传的文件
  • 3.将用户选择的文件上传到服务端
  • 4.服务端保存该文件
posted @ 2019-08-07 18:48  泡泡茶壶i  阅读(257)  评论(0编辑  收藏  举报