网络编程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)
#
#
#
# 方式五:元类
二、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套接字
-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'))
>>>客户端正在运行...
- 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'
-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()
>>>客户端正在发送请求...
>>>客户端>>>:你好啊热巴
>>>好啊亚峰
- 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)
>>>请输入上传电影的名字:色戒