网络编程

基于TCP协议通信套接字:

服务端:

import socket

#1.买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #SOCK_STEAM=>TCP流式协议,
print(phone) #是用来接收链接请求,从而建立链接的

#2.插手机卡
phone.bind(('127.0.0.1',8081)) #0-65535

#3.开机
phone.listen(5) # 同一时刻最大请求数为5个

# print('start....')
#4.等待电话请求
conn,client_addr=phone.accept() #(双向链接的套接字对象,存放客户端ip和端口的小元组)
print(conn) # conn代表双向链接,用来收发消息
print(client_addr)

#5.收发消息
data=conn.recv(1024) #1024接收的最大字节数bytes
print('收到客户的数据',data)
conn.send(data.upper())

#6.挂电话链接
conn.close()

#7.关机
phone.close()



客户端:
import socket

#1.买手机
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #SOCK_STEAM=>TCP流式协议,

#2.拨号
phone.connect(('127.0.0.1',8081))

#3.发收消息
phone.send('hello'.encode('utf-8')) # 只能发bytes类型
data=phone.recv(1024)
print('收到服务端的消息: ',data)

#4.挂电话链接
phone.close()



加上链接循环与通信循环:
服务端:
from socket import *

server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5)

# 链接循环
while True:
conn, client_addr = server.accept()
print(client_addr)

# 通信循环
while True:
try:
data = conn.recv(1024)
if len(data) == 0: break # 针对linux系统
print('-->收到客户端的消息: ', data)
conn.send(data.upper())
except ConnectionResetError:
break

conn.close()

server.close()


客户端:
from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8080))

# 通信循环
while True:
msg=input('>>: ').strip()
client.send(msg.encode('utf-8'))
data=client.recv(1024)
print(data)

client.close()

=========================================================================


基于TCP协议通信套接字:
服务端:
import socket

server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8082))

while True:
data,client_addr=server.recvfrom(1024)
print(data)
server.sendto(data.upper(),client_addr)

server.close()

客户端:
import socket

client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
msg=input('>>: ').strip()
client.sendto(msg.encode('utf-8'),('127.0.0.1',8082))
data,server_addr=client.recvfrom(1024)
print(data)

=========================================================================

粘包问题:

须知:只有TCP有粘包现象,UDP永远不会粘包

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。


   此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都           很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。


  1. TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务。收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。 即面向流的通信是无消息保护边界的。
  2. UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
  3. tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住,而udp是基于数据报的,即便是你输入的是空内容(直接回车),那也不是空消息,udp协议会帮你封装上消息头,实验略


解决粘包终极版:
服务端:
from socket import *
import subprocess
import struct
import json

server = socket(AF_INET, SOCK_STREAM)
server.bind(('127.0.0.1', 8081))
server.listen(5)

# 链接循环
while True:
conn, client_addr = server.accept()
print(client_addr)

# 通信循环
while True:
try:
cmd = conn.recv(1024) # cmd=b'dir'
if len(cmd) == 0: break # 针对linux系统
obj = subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
stdout = obj.stdout.read()
stderr = obj.stderr.read()
# 1. 先制作报头
header_dic = {
'filename': 'a.txt',
'md5': 'asdfasdf123123x1',
'total_size': len(stdout) + len(stderr)
}
header_json = json.dumps(header_dic)
header_bytes = header_json.encode('utf-8')

# 2. 先发送4个bytes(包含报头的长度)
conn.send(struct.pack('i', len(header_bytes)))
# 3 再发送报头
conn.send(header_bytes)

# 4. 最后发送真实的数据
conn.send(stdout)
conn.send(stderr)
except ConnectionResetError:
break

conn.close()
server.close()
客户端:
from socket import *
import struct
import json

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081))

# 通信循环
while True:
cmd=input('>>: ').strip()
if len(cmd) == 0:continue
client.send(cmd.encode('utf-8'))
#1. 先收4bytes,解出报头的长度
header_size=struct.unpack('i',client.recv(4))[0]

#2. 再接收报头,拿到header_dic
header_bytes=client.recv(header_size)
header_json=header_bytes.decode('utf-8')
header_dic=json.loads(header_json)
print(header_dic)
total_size=header_dic['total_size']

#3. 接收真正的数据
cmd_res=b''
recv_size=0
while recv_size < total_size:
data=client.recv(1024)
recv_size+=len(data)
cmd_res+=data

print(cmd_res.decode('gbk'))
client.close()
===========================================================
socketserver模块使用方法:
基于TCP协议:
服务端:
import socketserver

# 自定义类用来处理通信循环
class MyTCPhanler(socketserver.BaseRequestHandler):
def handle(self):
while True:
try:
data = self.request.recv(1024)
if len(data) == 0: break # 针对linux系统
print('-->收到客户端的消息: ', data)
self.request.send(data.upper())
except ConnectionResetError:
break

self.request.close()


if __name__ == '__main__':
server=socketserver.ThreadingTCPServer(('127.0.0.1',8081),MyTCPhanler)
server.serve_forever() # 链接循环

客户端:
from socket import *

client = socket(AF_INET, SOCK_STREAM)
client.connect(('127.0.0.1', 8081))

# 通信循环
while True:
# msg=input('>>: ').strip() #msg=''
# if len(msg) == 0:continue
# client.send(msg.encode('utf-8')) #client.send(b'')
client.send('hello'.encode('utf-8')) #client.send(b'')
# print('has send')
data=client.recv(1024)
# print('has recv')
print(data)

client.close()

基于UDP协议:
服务端:
import socketserver

class MyUdphandler(socketserver.BaseRequestHandler):
def handle(self):
data,sock=self.request #self.request相当于conn
sock.sendto(data.upper(),self.client_address)

if __name__ == '__main__':
server=socketserver.ThreadingUDPServer(('127.0.0.1',8081),MyUdphandler)
server.serve_forever()

客户端:
from socket import *

client=socket(AF_INET,SOCK_DGRAM)

while True:
client.sendto(b'hello',('127.0.0.1',8081))
data,server_addr=client.recvfrom(1024)
print(data)


 
原文地址:https://www.cnblogs.com/zhangpang/p/9594911.html