网络编程之tcp协议以及粘包问题
网络编程tcp协议与socket以及单例的补充
一、单例补充
- 实现单列的几种方式
#方式一:classmethod
# class Singleton:
#
# __instance = None
#
# @classmethod
# def singleton(cls):
#
# if not cls.__instance:
# cls.__instance = cls()
#
# return cls.__instance
#
# obj1 = Singleton.singleton()
# obj2 = Singleton.singleton()
# print(obj1)
# print(obj2)
# <__main__.Singleton object at 0x000002127F230D08>
# <__main__.Singleton object at 0x000002127F230D08>
#方式二: __new__
# class Singleton:
#
# __instance = None
#
# def __new__(cls, *args, **kwargs):
#
# if not cls.__instance:
# cls.__instance = object.__new__(cls)
#
# return cls.__instance
#
# obj1 = Singleton()
# obj2 = Singleton()
# print(obj1)
# print(obj2)
# <__main__.Singleton object at 0x00000257AAE10A88>
# <__main__.Singleton object at 0x00000257AAE10A88>
#方式三:装饰器
# def singleton(cls): #cls---> Father
#
# __instance = {}
#
# def inner(*args, **kwargs):
# if cls not in __instance:
# obj = cls(*args, **kwargs)
# __instance[cls] = obj
#
#
# return __instance[cls]
# return inner
#
# @singleton
# class Father:
# pass
#
# print(Father())
# print(Father())
# <__main__.Father object at 0x000001F17EB21548>
# <__main__.Father object at 0x000001F17EB21548>
#方式四
先定义一个Singletoncls的py文件内容如下:
class Singletoncls:
pass
obj = Singletoncls()
# from Singletoncls import obj
# print(obj)
# from Singletoncls import obj
# print(obj)
# from Singletoncls import obj
# print(obj)
# <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
# <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
# <Singletoncls.Singletoncls object at 0x00000249CD25BE48>
# 方式五:元类
二、tcp协议以及粘包问题
- 理论知识
传输层:
-tcp协议
-udp协议
端口(port):标识一台计算机上的某一个软件。
-0-1024:禁止使用,因为是操作系统在用
-8000---->以后接着用
- 以下的一些软件的固定端口不要碰:
django:8000
mysql:3306
redis:6379
flask:5000
tomcat:8080
mongodb:27017
...
要想要传输数据,必须建立双向通道
1、tcp协议:三次握手,四次挥手
-tcp协议建立双向通道
- 三次握手,键连接:
1:客户端向服务端发送建立连接的请求
2:服务端返回收到的请求信息给客户端,并且发送往客户端建立的请求
3:客户端接收到服务端的请求,返回请求成功给服务端,完成双向连接
- 反馈机制:
客户端往服务端发送请求,服务端必须返回响应
告诉客户端收到请求了,并且将服务端的数据一并返回给客户端
c--->s:一次请求,必须有一次响应
- 缺点:
- 洪水攻击:
指的是通过伪造大量的请求,往对方服务器发送请求
导致对方服务器跟不上,以至于瘫痪。
Linux系统有个参数可以限制
- 半连接池listen:限制用户在同一时间段内的访问数量
- 四次挥手,断开连接
1:客户端向服务端发送断开连接的请求
2:服务端返回收到请求的信息给客户端
3:服务端确认所有的数据发送完成以后,再发送同意断开连接的请求给客户端
4:客户端返回收到断开连接的请求,给服务端
2、socket套接字通信:
- 什么是socket?
socket是一个模块,又称套接字,用来封装 互联网协议(应用层以下的层)
- 为什么要有socket?
socket可以实现 互联网协议应用层已下的层的工作
- 提高开发效率
- 怎么使用socket?
import socket
写socket套接字:
client
sever
3、粘包问题
-1)问题:无法确认对方发送过来的数据的大小
-2)问题:在发送数据间隔短并且数据量小的情况下,会将所有数据一次性发送
解决:确认对方数据的大小
4、解决粘包问题(struct模块)
-无论那一段发送数据
-客户端
-1)先制作报头,并发送(struct)
-2)发送真实数据
服务端
-1)接收报头,并解包获取 真实的数据长度
-2)根据真实数据长度 接收真实数据
recv(真实数据长度)
三、socket套接字
- socket套接字初级版本
- 演示
-sever:
'''
先启动套接服务端
'''
import socket
#买手机
sever = socket.socket()
#绑定手机卡
sever.bind(
('127.0.0.1', 9876)
)
#半连接池
sever.listen(5) #最多同时五个人坐椅子,实际上==6
print(
'服务端正在运行...'
)
#等待电话接入
#coon:指的是服务端往客户端的管道
coon, addr = sever.accept()
#接听对方讲话的内容
#data客户端发送过来的消息
data = coon.recv(1024) #一次性可接受1024bytes的数据
print(data)
>>>服务端正在运行...
>>>b'hello'
#挂电话
coon.close()
-client:
'''
先启动服务端后再启动客户端
'''
import socket
#买手机
client = socket.socket()
#拨打电话
client.connect(
('127.0.0.1', 9876)
)
print('客户端正在运行...')
#必须发送bytes类型的数据
#开始讲话
client.send(b'hello')
# 或client.send('hello'.encode('utf_8'))
>>>客户端正在运行...
- socket套接字升级版本
- sever
'''
注意:
客户端法送一次,我服务端就得先接受一次,之后才可以再向客户端发送消息
'''
import socket
#买手机
sever = socket.socket()
#绑定手机卡
#里面绑定的是一个元祖
sever.bind(
('127.0.0.1', 9876)
)
#半连接池
sever.listen(5)
print('服务端正在等待服务...')
#等待电话接入
#coon:指的是服务端往客户端的管道
coon, addr = sever.accept()
#接听对方讲话的内容
data = coon.recv(1024)
print(data)
#服务端往客户端发送消息
coon.send(b'hi i am sever')
#挂电话
coon.close()
>>>服务端正在等待服务...
>>>b'hello i am client...'
- client
'''
启动服务端后再启动客户端
'''
import socket
#买手机
client = socket.socket()
#拨号
client.connect(
('127.0.0.1', 9876)
)
print('客户端正在发送请求...')
client.send(b'hello i am client...')
#接收服务端的请求
data = client.recv(1024)
print(data)
client.close()
>>>客户端正在发送请求...
>>>b'hi i am sever'
- socket套接字高级版本
-sever
import socket
#买手机
sever = socket.socket()
#绑定手机卡
sever.bind(
('127.0.0.1', 9876)
)
#半连接池
sever.listen(5)
print('服务端正在运行...')
#等待电话接入
coon, addr = sever.accept()
while True:
#接收对方讲话内容
#data客户端发送过来的消息
data = coon.recv(1024)
if len(data) == 0:
break
if data.decode('utf-8') == 'q':
break
print(data.decode('utf-8'))
send_data = input('服务端...')
coon.send(send_data.encode('utf-8'))
#挂电话
coon.close()
服务端正在运行...
服务端...你好啊亚峰
-client
import socket
#买手机
client = socket.socket()
#拨打电话
client.connect(
('127.0.0.1', 9876)
)
print('客户端正在发送请求...')
while True:
send_data = input('客户端>>>:').strip()
client.send(send_data.encode('utf-8'))
data = client.recv(1024)
if data.decode('utf-8') == 'q':
break
if len(data) == 0:
break
print(data.decode('utf-8'))
client.close()
>>>客户端正在发送请求...
>>>客户端>>>:你好啊热巴
>>>好啊亚峰
- socket套接字终级版本
- sever
import socket
#买手机
sever = socket.socket()
#绑定手机卡
sever.bind(
('127.0.0.1', 9876)
)
#半连接池
sever.listen(5)
print('服务端正在服务...')
#循环实现可接受多个用户访问
while True:
coon, addr = sever.accept()
print(addr)
#循环实现通信
while True:
try:
#监听代码是否有异常出现
#接听对方讲话的内容
#data客户端发送过来的消息
data = coon.recv(1024)
if len(data) == 0:
break
if data.decode('utf-8') =='q':
break
print(data.decode('utf-8'))
send_data = input('服务端>>>...')
#服务端向客户端发送消息
coon.send(send_data.encode('utf-8'))
except Exception as e:
print(e)
break
#挂电话
coon.close()
>>>
服务端正在服务...
('127.0.0.1', 52467)
我想找迪丽热巴
服务端>>>...你好啊亚峰,我是热巴,有什么能帮你吗
热巴youare beautiful
服务端>>>...谢谢亚峰
-client
import socket
#买手机
client = socket.socket()
#拨打号码
client.connect(
('127.0.0.1', 9876)
)
print('客户端正在发送请求...')
while True:
send_data = input('客户端>>>:')
client.send(send_data.encode('utf-8'))
data = client.recv(1024)
if data.decode('utf-8') == 'q':
break
if len(data) == 0:
break
print(data.decode('utf-8'))
>>>
client.close()
客户端正在发送请求...
客户端>>>:我想找热巴
你好啊亚峰,我是热巴,有什么能帮你吗
客户端>>>:热巴you are beautiful
谢谢亚峰
客户端>>>:
四、粘包问题
- 粘包问题的出现以及几种情况
- 第一个问题
-sever
#问题一不知道数据的具体长度
# import socket
#
# import subprocess
#
# #买手机
# sever = socket.socket()
#
# #绑定电话卡
# sever.bind(
# ('127.0.0.1', 9867)
# )
#
# #半整数池
# sever.listen(5)
#
# while True:
# coon, addr = sever.accept()
# print(addr)
#
# while True:
# try:
# #recv从内存中获取数据
# cmd = coon.recv(1024)
#
# if len(cmd) == 0:
# continue
# cmd = cmd.decode('utf-8')
# if cmd == 'q':
# break
#
# #调用subprocess连接终端,对终端进行操作,并获取操作后正确或错误的结果
# obj = subprocess.Popen(
# cmd, shell=True, stdout=subprocess.PIPE,
# stderr=subprocess.PIPE
# )
# #结果交给result变量名
# result = obj.stdout.read() + obj.stderr.read()
# print(len(result))
#
# #windows默认是gbk
# print(result.decode('gbk'))
#
# #将结果返回给客户端
# coon.send(result)
# except Exception as e:
# print(e)
# break
#
# coon.close()
-client
# import socket
#
# client = socket.socket()
#
# client.connect(
# ('127.0.0.1', 9867)
# )
#
# while True:
#
# cmd = input('客户端输入的内容:')
#
# client.send(cmd.encode('utf-8'))
#
# data = client.recv(19190)
#
# print(len(data))
#
# print(data.decode('gbk'))
- 第二种问题
-sever
#问题二:当发送多次传入的数据长度却不是很长的时候,服务端多次接收后面接收的没内容
import socket
sever = socket.socket()
sever.bind(
('127.0.0.1', 9000)
)
sever.listen(5)
coon, addr = sever.accept()
data = coon.recv(10)
print(data)
data = coon.recv(1024)
print(data)
data = coon.recv(1024)
print(data)
>>>b'hellohello'
>>>b'hello'
>>>b''
- client
#问题二
import socket
client = socket.socket()
client.connect(
('127.0.0.1', 9000)
)
client.send(b'hello')
client.send(b'hello')
client.send(b'hello')
五、解决粘包问题
- 演示
- sever
import socket
import subprocess
import struct
sever = socket.socket()
sever.bind(
('127.0.0.1', 9000)
)
sever.listen(5)
while True:
coon, addr = sever.accept()
print(addr)
while True:
try:
#获取客户端传过来的报头
header = coon.recv(4)
#解包获取真实的数据长度
data_len = struct.unpack('i', header)[0]
#准备接收真实数据
cmd = coon.recv(data_len)
if len(cmd) == 0:
continue
cmd = cmd.decode('utf-8')
if cmd == 'q':
break
#调用subprocess连接终端,对终端进行操作,并获取操作后的正确或错误的结果
obj = subprocess.Popen(
cmd, shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
#结果交给result变量名
result = obj.stdout.read() + obj.stderr.read()
print('发送给客户端的真实长度', len(result))
#将结果返回给客户端,做一个报头,返回给客户端
header = struct.pack('i', len(result))
print(len(header))
coon.send(header)
coon.send(result)
except Exception as e:
print(e)
break
coon.close()
- client
import struct
import socket
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)
#接收服务端返回的报头
s_header = client.recv(4)
#解包,接收服务端返回的真实数据
data_len = struct.unpack('i', s_header)[0]
result = client.recv(data_len)
print('接收服务器返回的真实数据长度', len(result))
print(result.decode('gbk'))
- 演示二
-sever
import socket
import struct
import json
sever = socket.socket()
sever.bind(
('127.0.0.1', 9000)
)
sever.listen(5)
while True:
coon, addr = sever.accept()
print(addr)
while True:
try:
#获取客户端传来的报头
header = coon.recv(4)
#解包,获取真实的数据长度
json_len = struct.unpack('i', header)[0]
#接收json(dictionary)的真实长度
json_bytes_data = coon.recv(json_len)
#反序列化将bytes类型数据变成json数据
json_data = json_bytes_data.decode('utf-8')
back_dic = json.loads(json_data)
print(back_dic)
print(back_dic.get('movie_len'))
except Exception as e:
print(e)
break
coon.close()
>>>('127.0.0.1', 53414)
>>>{'movie_name': '色戒', 'movie_len': 100000}
>>>100000
-client
import struct
import socket
import json
client = socket.socket()
client.connect(
('127.0.0.1', 9000)
)
while True:
movie_name = input('请输入上传电影的名字:').strip()
#伪装电影真实数据
movie_len = 100000
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)
>>>请输入上传电影的名字:色戒
网络编程之tcp协议以及粘包问题的更多相关文章
- day28 8_7 网络编程之tcp协议
一.socket模块 socket模块就是用来网络搭建的模块,socket也叫套接字. 创建网络连接,需要使用两个模块进行模拟,一个作为server服务器端,一个作为client客户端. 在服务器端, ...
- 网络编程之TCP协议与UDP协议
了解网络就要了解一些基本的协议今天主要跟大家分享一些关于TCP 协议UDP协议的相关知识 首先介绍一下TCP协议 TCP(Transmission Cintrol Protocol)可靠的.面向连接的 ...
- 网络编程之TCP协议怎么使用?
TCP 通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器会写的数据 表示客户端的类: java.net.Socket;此类实现客户端套接字.套接字是两台机器间通信的端点 套接字:包含了 ...
- java 26 - 7 网络编程之 TCP协议代码优化
上次所写的代码中,客户端和服务器端所进行的数据传输所用的是字节流. 优化: A:这次,为了高效,对这个字节流通过转换流来进行包装,包装成高效字符流. B:这次,传输的数据是通过键盘录入的数据. 服务器 ...
- java 26 - 6 网络编程之 TCP协议 传输思路 以及 代码
TCP传输 Socket和ServerSocket 建立客户端和服务器 建立连接后,通过Socket中的IO流进行数据的传输 关闭socket 同样,客户端与服务器是两个独立的应用程序 TCP协议发送 ...
- python六十九课——网络编程之TCP协议
1.1 概述: TCP协议通过三次握手协议将客户端与服务器端连接,两端使用各自的Socket对象.Socket对象中包含了IO流,供数据传输. 即:TCP协议在客户端与服务器端通过Socket组成了I ...
- java 26 - 9 网络编程之 TCP协议多用户上传文件
TCP实现多用户上传文件: 需要同时给多用户上传文件,这样就得用多线程来实现. 实际上,这样的话,上传的先后顺序和速度就跟客户端的带宽有关:带宽够,就容易抢占到线程的执行权: 首先,创建个线程类:(这 ...
- java 26 - 8 网络编程之 TCP协议的练习
TCP练习: 1.客户端键盘录入,服务器输出文本文件 客户端代码: public class ClientDemo { public static void main(String[] args) t ...
- java 26 - 8 网络编程之 TCP协议上传图片
上次的是上传TXT文件,这次上传的是图片.同样,上传成功需要反馈给客户端. 区别: TXT文件用记事本打开,我们可以看得懂,所以用了缓冲字符流,对通道内的字节流进行包装了. 而图片用记事本打开,我们看 ...
随机推荐
- Shell(六):输入/输出重定向
重定向的作用是将命令的执行结果输出到指定的文件中. 重定向命令列表如下: 文件描述符 0 通常是标准输入(STDIN),1 是标准输出(STDOUT),2 是标准错误输出(STDERR). 1.输出重 ...
- DiskCatalogMaker for Mac常见问题解答
DiskCatalogMaker for Mac是Mac上简单实用的磁盘管理工具,可以帮助您对多张光盘使用批量扫描模式, 生成缩略图图像选项,更加清晰,并请将其快速编目引擎与其他编目人员比较,在本篇文 ...
- Ubuntu : apt 命令
apt 命令是一个功能强大的命令行工具,它不仅可以更新软件包列表索引.执行安装新软件包.升级现有软件包,还能够升级整个 Ubuntu 系统(apt 是 Debian 系操作系统的包管理工具).与更专业 ...
- 我用 Python 破解了同事的加密压缩包!
作者 | 朱小五 又是一杯奶茶. 事情的经过是这样的: 又是奶茶,行吧快点开工,争取李大伟回来之前搞定 李大伟说是6位数字密码 那么我们可以利用python生成全部的六位数字密码 #生成从 ...
- Go 开发关键技术指南 | 为什么你要选择 GO?(内含超全知识大图)
作者 | 杨成立(忘篱) 阿里巴巴高级技术专家 关注"阿里巴巴云原生"公众号,回复 Go 即可查看清晰知识大图! 导读:从问题本身出发,不局限于 Go 语言,探讨服务器中常常遇到的 ...
- GO汇总
1.基础 GO语言介绍以及开发环境配置 Go-包 Go-数据类型以及变量,常量 Go-获取变量数据类型 GO-数组与切片 GO-切片拷贝以及赋值 Go-函数 Go-闭包 GO-逻辑判断(if,else ...
- 6.Ansible Roles角色实战
==Roles小技巧:== 1.创建roles目录结构,手动或使用ansible-galaxy init test roles 2.编写roles的功能,也就是tasks. nginx rsyncd ...
- 获取input type=file 的文件内容(纯文本)
一.获取input type=file 的文件内容(纯文本) 1.需求一 通过点击其他事件,来触发 文件选择框(限定格式为 .c 文件),而不是手动鼠标点击触发. [思路:] step1:将 inpu ...
- Linux系统学习 十五、VSFTP服务—匿名用户访问(不推荐使用,不安全)
匿名用户访问 基本配置: anonymous_enable #允许匿名用户访问 anon_upload_enable #允许匿名用户上传 anon_mkdir_write ...
- LeetCode--回文数(简单)
题目描述: 判断一个整数是否是回文数.回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数. 示例 1: 输入: 121 输出: true 示例 2: 输入: -121 输出: false 解 ...