'''
客户端和服务器架构
C/S架构:client --> server
用户需要下载对应得客户端才能访问服务器
B/S架构:Browser --> server
用户只需要通过一个浏览器就可以访问所有服务器
OSI七层模型:
应用层:为应用程序提供网络服务
数据层:数据格式化、加密、解密
会话层:建立、维护、管理会话连接
传输层:建立、维护、管理端到端连接
网络层:IP寻址和路由选择
数据链路层:控制数据帧在物理链路上的传输
物理层:传输比特流
socket简介:将传输层及以下的数据传输过程封装成一个socket模块
TCP:
TCP服务器
1. 建立socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2. 绑定本地服务器IP+Port
s.bind(('ip',port))
3. 设置最大连接缓冲
s.listen(128)
4. 获取对端的TCP socket和IP+PORT
a, b = s.accept()
5. 获取对端发来的数据
a.recv(1024).decode('utf8')
6. 向对端发送数据
a.send(''.encode('utf8'))
7. 关闭与对端建立的socket连接
a.close()
8. 关闭本地socket连接
s.close()
TCP客户端
1. 建立socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
2. 指定对端TCP服务器
s.connect(('ip', port))
3. 向对端发发送数据
s.send(''.encode('utf8'))
4. 接收对端发来的数据
s.recv(1024).decode('utf8')
5. 关闭socket连接
s.close()
UDP
UDP服务器:
1. 创建socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2. 绑定本地IP+PORT
s.bind(('IP', port))
3. 获取对端发送的数据
a, b = s.recvfrom(1024)
a.decode('utf8')
4. 向对端发送数据
s.sendto(''.encode('utf8'), b)
5. 关闭本地socket
s.close()
UDP客户端
1. 创建socket连接
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
2. 指定对端UDP服务器IP+PORT
s.connect(('IP', PORT))
3. 向对端发送数据
s.send(''.encode('utf8'))
s.sendto(''.encode('utf8'), ('IP', PORT))
4. 接收对端的数据
s.recv(1024).decode('utf8')
a, b = s.recvfrom(1024)
a.decode('utf8')
5. 关闭socket
s.close()
注意事项:
1. recv、recvfrom本身原理就是从本地主机的内存中读取数据,并不是向对端请求数据,所以这两个方法是一个IO操作,会阻塞程序
2. 对于只要能够获取到对端 IP+PORT 数据的方法,一律都有可能是返回 ('IP', PORT) 格式
3. 对于TCP连接会出现粘包问题,对于UDP连接会出现丢包问题
4. 对于TCP连接,不能发送空数据,因为对于空数据,本地主机会认为没有数据从而不会发送数据,导致对端会阻塞在recv发方法
链接循环:
while True:
a, b = accept()
while True:
a.send(byte)
client_data = a.recv(1024).decode('utf8')
if clent_data = '\n':
break
a.close()
通信循环
while True:
a.send(byte)
client_data = a.recv(1024).decode('utf8')
if clent_data = '\n':
break
TCP并发:
TCP服务器:
from socketserver import BaseRequestHandler, ThreadingTCPServer
class ceshi(BaseRequestHandler):
def Handle(self):
a, b = self.request, self.client_address
while True:
Try:
data = a.recv(1024).decode('utf8')
data1 = a.send(data.upper().encode('utf8'))
if len(data1) == 0:
break
except Exception as e:
break
a.close()
s = ThreadingTCPServer(('IP', Port), ceshi)
s.serve_forever()
UDP并发
UDP服务器:
from socketserver import BaseRequestHandler, ThreadingUDPServer
class ceshi(BaseRequestHandler):
def handle(self):
a, b = self.request
c = self.client_address
data = b.sendto(a.upper(), c)
s = ThreadingUDPServer(('0.0.0.0', 8000), ceshi)
s.serve_forever()
socketserver
ThreadingUDPServer
ThreadingTCPServer
.serve_forever()
BaseRequestHandler
地址占用问题解决:
在bind上加一行
s = setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
粘包问题:
接收方无法确认数据长度,从而拿取数据时,拿少了
发送方发送数据,TCP对于数据量小的数据并不会直接发送,而是将多个数据量小的数据合并成一个大数据发送
粘包问题解决:
获取对端每次发送数据的长度,然后按照该长度从内存重取出指定长度数据
1. 自定义报文
'''