标签:.so use header into imp ima 技术 补充 作业
UDP服务端
ss = socket() #创建一个服务器的套接字 ss.bind() #绑定服务器套接字 inf_loop: #服务器无限循环 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送) ss.close() # 关闭服务器套接字
UDP客户端
cs = socket() # 创建客户套接字 comm_loop: # 通讯循环 cs.sendto()/cs.recvfrom() # 对话(发送/接收) cs.close() # 关闭客户套接字
简单示例:
服务端:
# -*- coding: utf-8 -*- # __author__ = "maple" import socket ip_port=(‘127.0.0.1‘,9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) udp_server_client.bind(ip_port) while True: msg,addr = udp_server_client.recvfrom(BUFSIZE) print(‘recv: ‘,msg,addr) udp_server_client.sendto(msg.upper(),addr) udp_server_client.close()
客户端:
# -*- coding: utf-8 -*- # __author__ = "maple" import socket ip_port=(‘127.0.0.1‘,9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) print(udp_server_client._io_refs) while True: msg = input(‘>>>:‘).strip() if not msg:continue udp_server_client.sendto(msg.encode(‘utf-8‘),ip_port) back_msg,addr = udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode(‘utf-8‘),addr) udp_server_client.close()
发消息,都是将数据发送到自己端的发送缓存中。
收消息,都是从自己端的环从中收取数据。
1. tcp:send发送数据,recv接受数据。
2. udp:sendto发送数据,recvfrom接受数据。
tcp是基于数据流的,而udp是基于数据报的:
send(bytes_data):发送数据流,数据流bytes_data若为空,自己这段的缓冲区也为空,操作系统不会控制tcp协议发空包
sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包。
1.tcp协议:
(1)如果收消息缓冲区里的数据为空,那么recv就会阻塞(阻塞很简单,就是一直在等着收)
(2)只不过tcp协议的客户端send一个空数据就是真的空数据,客户端即使有无穷个send空,也跟没有一个样。
(3)tcp基于链接通信
验证(1):客户端发送空
验证(2):客户端直接终止程序
1 #_*_coding:utf-8_*_ 2 3 import subprocess 4 from socket import * 5 6 phone=socket(AF_INET,SOCK_STREAM) 7 phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) 8 phone.bind((‘127.0.0.1‘,8080)) 9 phone.listen(5) 10 11 conn,addr=phone.accept() 12 13 while True: 14 data=conn.recv(1024) 15 print(‘from client msg is ‘,data) 16 conn.send(data.upper())
1 #_*_coding:utf-8_*_ 2 3 import subprocess 4 from socket import * 5 6 phone=socket(AF_INET,SOCK_STREAM) 7 phone.connect((‘127.0.0.1‘,8080)) 8 9 10 while True: 11 msg=input(‘>>: ‘) 12 phone.send(msg.encode(‘utf-8‘)) 13 print(‘Client message has been sent‘) 14 15 data=phone.recv(1024) 16 print(‘from server msg is ‘,data.decode(‘utf-8‘)) 17 phone.close()
2.udp协议
(1)如果如果收消息缓冲区里的数据为“空”,recvfrom也会阻塞
(2)只不过udp协议的客户端sendinto一个空数据并不是真的空数据(包含:空数据+地址信息,得到的报仍然不会为空),所以客户端只要有一个sendinto(不管是否发送空数据,都不是真的空数据),服务端就可以recvfrom到数据。
(3)udp无链接
验证(1):客户端发送空,看服务端结果
1 #_*_coding:utf-8_*_ 2 、from socket import * 3 4 ip_port=(‘127.0.0.1‘,9003) 5 bufsize=1024 6 7 udp_server=socket(AF_INET,SOCK_DGRAM) 8 udp_server.bind(ip_port) 9 10 while True: 11 data1,addr=udp_server.recvfrom(bufsize) 12 print(data1)
1 from socket import * 2 ip_port=(‘127.0.0.1‘,9003) 3 bufsize=1024 4 5 udp_client=socket(AF_INET,SOCK_DGRAM) 6 7 while True: 8 msg=input(‘>>: ‘) 9 udp_client.sendto(msg.encode(‘utf-8‘),ip_port) #发送空,发现服务端可以接收空
验证(2):分别运行服务端
1 #_*_coding:utf-8_*_ 2 3 from socket import * 4 5 ip_port=(‘127.0.0.1‘,9003) 6 bufsize=1024 7 8 udp_server=socket(AF_INET,SOCK_DGRAM) 9 udp_server.bind(ip_port) 10 11 data1,addr=udp_server.recvfrom(1) 12 print(‘第一次收了 ‘,data1) 13 data2,addr=udp_server.recvfrom(1) 14 print(‘第二次收了 ‘,data2) 15 data3,addr=udp_server.recvfrom(1) 16 print(‘第三次收了 ‘,data3) 17 print(‘--------结束----------‘)
1 from socket import * 2 ip_port=(‘127.0.0.1‘,9003) 3 bufsize=1024 4 5 udp_client=socket(AF_INET,SOCK_DGRAM) 6 7 udp_client.sendto(b‘hello‘,ip_port) 8 udp_client.sendto(b‘world‘,ip_port) 9 udp_client.sendto(b‘egon‘,ip_port)
验证(3):不运行服务端,单独运行客户端,没有问题,但是消息丢了
1 #_*_coding:utf-8_*_ 2 3 from socket import * 4 5 ip_port=(‘127.0.0.1‘,9003) 6 bufsize=1024 7 8 udp_server=socket(AF_INET,SOCK_DGRAM) 9 udp_server.bind(ip_port) 10 11 data1,addr=udp_server.recvfrom(bufsize) 12 print(‘第一次收了 ‘,data1) 13 data2,addr=udp_server.recvfrom(bufsize) 14 print(‘第二次收了 ‘,data2) 15 data3,addr=udp_server.recvfrom(bufsize) 16 print(‘第三次收了 ‘,data3) 17 print(‘--------结束----------‘)
1 from socket import * 2 import time 3 ip_port=(‘127.0.0.1‘,9003) 4 bufsize=1024 5 6 udp_client=socket(AF_INET,SOCK_DGRAM) 7 8 udp_client.sendto(b‘hello‘,ip_port) 9 udp_client.sendto(b‘world‘,ip_port) 10 udp_client.sendto(b‘egon‘,ip_port) 11 12 print(‘客户端发完消息啦‘) 13 time.sleep(100)
注意:
1.你单独运行上面的udp的客户端,你发现并不会报错,相反tcp却会报错,因为udp协议只负责把包发出去,对方收不收,我根本不管,而tcp是基于链接的,必须有一个服务端先运行着,客户端去跟服务端建立链接然后依托于链接才能传递消息,任何一方试图把链接摧毁都会导致对方程序的崩溃。
2.上面的udp程序,你注释任何一条客户端的sendinto,服务端都会卡住,为什么?因为服务端有几个recvfrom就要对应几个sendinto,哪怕是sendinto(b‘‘)那也要有。
须知:只有TCP有粘包现象,UDP永远不会粘包。
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。
udp的recvfrom是阻塞的,一个recvfrom(x)必须对一个一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠
tcp的协议数据不会丢,没有收完包,下次接收,会继续上次继续接收,己端总是在收到ack时才会清除缓冲区内容。数据是可靠的,但是会粘包。
两种情况下会发生粘包:
发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
1 #_*_coding:utf-8_*_ 2 3 from socket import * 4 ip_port=(‘127.0.0.1‘,8080) 5 6 tcp_socket_server=socket(AF_INET,SOCK_STREAM) 7 tcp_socket_server.bind(ip_port) 8 tcp_socket_server.listen(5) 9 10 11 conn,addr=tcp_socket_server.accept() 12 13 14 data1=conn.recv(10) 15 data2=conn.recv(10) 16 17 print(‘----->‘,data1.decode(‘utf-8‘)) 18 print(‘----->‘,data2.decode(‘utf-8‘)) 19 20 conn.close()
1 #_*_coding:utf-8_*_ 2 3 import socket 4 BUFSIZE=1024 5 ip_port=(‘127.0.0.1‘,8080) 6 7 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 8 res=s.connect_ex(ip_port) 9 10 11 s.send(‘hello‘.encode(‘utf-8‘)) 12 s.send(‘feng‘.encode(‘utf-8‘))
接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包)
1 #_*_coding:utf-8_*_ 2 3 from socket import * 4 ip_port=(‘127.0.0.1‘,8080) 5 6 tcp_socket_server=socket(AF_INET,SOCK_STREAM) 7 tcp_socket_server.bind(ip_port) 8 tcp_socket_server.listen(5) 9 10 11 conn,addr=tcp_socket_server.accept() 12 13 14 data1=conn.recv(2) #一次没有收完整 15 data2=conn.recv(10)#下次收的时候,会先取旧的数据,然后取新的 16 17 print(‘----->‘,data1.decode(‘utf-8‘)) 18 print(‘----->‘,data2.decode(‘utf-8‘)) 19 20 conn.close()
1 #_*_coding:utf-8_*_ 2 import socket 3 BUFSIZE=1024 4 ip_port=(‘127.0.0.1‘,8080) 5 6 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 7 res=s.connect_ex(ip_port) 8 9 10 s.send(‘hello feng‘.encode(‘utf-8‘))
拆包的发生情况
当发送端缓冲区的长度大于网卡的MTU时,tcp会将这次发送的数据拆成几个数据包发送出去。
补充问题一:为何tcp是可靠传输,udp是不可靠传输
基于tcp的数据传输请参考我的另一篇文章http://www.cnblogs.com/linhaifeng/articles/5937962.html,tcp在数据传输时,发送端先把数据发送到自己的缓存中,然后协议控制将缓存中的数据发往对端,对端返回一个ack=1,发送端则清理缓存中的数据,对端返回ack=0,则重新发送数据,所以tcp是可靠的
而udp发送数据,对端是不会返回确认信息的,因此不可靠
补充问题二:send(字节流)和recv(1024)及sendall
recv里指定的1024意思是从缓存里一次拿出1024个字节的数据
send的字节流是先放入己端缓存,然后由协议控制将缓存内容发往对端,如果待发送的字节流大小大于缓存剩余空间,那么数据丢失,用sendall就会循环调用send,数据不会丢失
问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。
low版本的解决方法
1 #_*_coding:utf-8_*_ 2 3 import socket,subprocess 4 ip_port=(‘127.0.0.1‘,8080) 5 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 6 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 7 8 s.bind(ip_port) 9 s.listen(5) 10 11 while True: 12 conn,addr=s.accept() 13 print(‘客户端‘,addr) 14 while True: 15 msg=conn.recv(1024) 16 if not msg:break 17 res=subprocess.Popen(msg.decode(‘utf-8‘),shell=True,18 stdin=subprocess.PIPE,19 stderr=subprocess.PIPE,20 stdout=subprocess.PIPE) 21 err=res.stderr.read() 22 if err: 23 ret=err 24 else: 25 ret=res.stdout.read() 26 data_length=len(ret) 27 conn.send(str(data_length).encode(‘utf-8‘)) 28 data=conn.recv(1024).decode(‘utf-8‘) 29 if data == ‘recv_ready‘: 30 conn.sendall(ret) 31 conn.close()
1 #_*_coding:utf-8_*_ 2 3 import socket,time 4 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 5 res=s.connect_ex((‘127.0.0.1‘,8080)) 6 7 while True: 8 msg=input(‘>>: ‘).strip() 9 if len(msg) == 0:continue 10 if msg == ‘quit‘:break 11 12 s.send(msg.encode(‘utf-8‘)) 13 length=int(s.recv(1024).decode(‘utf-8‘)) 14 s.send(‘recv_ready‘.encode(‘utf-8‘)) 15 send_size=0 16 recv_size=0 17 data=b‘‘ 18 while recv_size < length: 19 data+=s.recv(1024) 20 recv_size+=len(data) 21 22 23 print(data.decode(‘utf-8‘))
为何low:
程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗。
为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。
struct模块
该模块可以把一个类型,如数字,转成固定长度的bytes
>>> struct.pack(‘i‘,1111111111111)
。。。。。。。。。
struct.error: ‘i‘ format requires -2147483648 <= number <= 2147483647 #这个是范围
1 import socket,struct,json 2 import subprocess 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 5 6 phone.bind((‘127.0.0.1‘,8080)) 7 8 phone.listen(5) 9 10 while True: 11 conn,addr=phone.accept() 12 while True: 13 cmd=conn.recv(1024) 14 if not cmd:break 15 print(‘cmd: %s‘ %cmd) 16 17 res=subprocess.Popen(cmd.decode(‘utf-8‘), 18 shell=True, 19 stdout=subprocess.PIPE, 20 stderr=subprocess.PIPE) 21 err=res.stderr.read() 22 print(err) 23 if err: 24 back_msg=err 25 else: 26 back_msg=res.stdout.read() 27 28 29 conn.send(struct.pack(‘i‘,len(back_msg))) #先发back_msg的长度 30 conn.sendall(back_msg) #在发真实的内容 31 32 conn.close()
1 #_*_coding:utf-8_*_ 2 3 import socket,time,struct 4 5 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 6 res=s.connect_ex((‘127.0.0.1‘,8080)) 7 8 while True: 9 msg=input(‘>>: ‘).strip() 10 if len(msg) == 0:continue 11 if msg == ‘quit‘:break 12 13 s.send(msg.encode(‘utf-8‘)) 14 15 16 17 l=s.recv(4) 18 x=struct.unpack(‘i‘,l)[0] 19 print(type(x),x) 20 # print(struct.unpack(‘I‘,l)) 21 r_s=0 22 data=b‘‘ 23 while r_s < x: 24 r_d=s.recv(1024) 25 data+=r_d 26 r_s+=len(r_d) 27 28 # print(data.decode(‘utf-8‘)) 29 print(data.decode(‘gbk‘)) #windows默认gbk编码
我们可以把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)
发送时:
先发报头长度
再编码报头内容然后发送
最后发真实内容
接收时:
先手报头长度,用struct取出来
根据取出的长度收取报头内容,然后解码,反序列化
从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容
1 import socket,struct,json 2 import subprocess 3 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 4 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 5 6 phone.bind((‘127.0.0.1‘,8080)) 7 8 phone.listen(5) 9 10 while True: 11 conn,addr=phone.accept() 12 while True: 13 cmd=conn.recv(1024) 14 if not cmd:break 15 print(‘cmd: %s‘ %cmd) 16 17 res=subprocess.Popen(cmd.decode(‘utf-8‘), 18 shell=True, 19 stdout=subprocess.PIPE, 20 stderr=subprocess.PIPE) 21 err=res.stderr.read() 22 print(err) 23 if err: 24 back_msg=err 25 else: 26 back_msg=res.stdout.read() 27 28 headers={‘data_size‘:len(back_msg)} 29 head_json=json.dumps(headers) 30 head_json_bytes=bytes(head_json,encoding=‘utf-8‘) 31 32 conn.send(struct.pack(‘i‘,len(head_json_bytes))) #先发报头的长度 33 conn.send(head_json_bytes) #再发报头 34 conn.sendall(back_msg) #在发真实的内容 35 36 conn.close()
1 from socket import * 2 import struct,json 3 4 ip_port=(‘127.0.0.1‘,8080) 5 client=socket(AF_INET,SOCK_STREAM) 6 client.connect(ip_port) 7 8 while True: 9 cmd=input(‘>>: ‘) 10 if not cmd:continue 11 client.send(bytes(cmd,encoding=‘utf-8‘)) 12 13 head=client.recv(4) 14 head_json_len=struct.unpack(‘i‘,head)[0] 15 head_json=json.loads(client.recv(head_json_len).decode(‘utf-8‘)) 16 data_len=head_json[‘data_size‘] 17 18 recv_size=0 19 recv_data=b‘‘ 20 while recv_size < data_len: 21 recv_data+=client.recv(1024) 22 recv_size+=len(recv_data) 23 24 print(recv_data.decode(‘utf-8‘)) 25 #print(recv_data.decode(‘gbk‘)) #windows默认gbk编码
FTP作业:上传下载文件
1 import socket 2 import struct 3 import json 4 import subprocess 5 import os 6 7 class MYTCPServer: 8 address_family = socket.AF_INET 9 10 socket_type = socket.SOCK_STREAM 11 12 allow_reuse_address = False 13 14 max_packet_size = 8192 15 16 coding=‘utf-8‘ 17 18 request_queue_size = 5 19 20 server_dir=‘file_upload‘ 21 22 def __init__(self, server_address, bind_and_activate=True): 23 """Constructor. May be extended, do not override.""" 24 self.server_address=server_address 25 self.socket = socket.socket(self.address_family, 26 self.socket_type) 27 if bind_and_activate: 28 try: 29 self.server_bind() 30 self.server_activate() 31 except: 32 self.server_close() 33 raise 34 35 def server_bind(self): 36 """Called by constructor to bind the socket. 37 """ 38 if self.allow_reuse_address: 39 self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 40 self.socket.bind(self.server_address) 41 self.server_address = self.socket.getsockname() 42 43 def server_activate(self): 44 """Called by constructor to activate the server. 45 """ 46 self.socket.listen(self.request_queue_size) 47 48 def server_close(self): 49 """Called to clean-up the server. 50 """ 51 self.socket.close() 52 53 def get_request(self): 54 """Get the request and client address from the socket. 55 """ 56 return self.socket.accept() 57 58 def close_request(self, request): 59 """Called to clean up an individual request.""" 60 request.close() 61 62 def run(self): 63 while True: 64 self.conn,self.client_addr=self.get_request() 65 print(‘from client ‘,self.client_addr) 66 while True: 67 try: 68 head_struct = self.conn.recv(4) 69 if not head_struct:break 70 71 head_len = struct.unpack(‘i‘, head_struct)[0] 72 head_json = self.conn.recv(head_len).decode(self.coding) 73 head_dic = json.loads(head_json) 74 75 print(head_dic) 76 #head_dic={‘cmd‘:‘put‘,‘filename‘:‘a.txt‘,‘filesize‘:123123} 77 cmd=head_dic[‘cmd‘] 78 if hasattr(self,cmd): 79 func=getattr(self,cmd) 80 func(head_dic) 81 except Exception: 82 break 83 84 def put(self,args): 85 file_path=os.path.normpath(os.path.join( 86 self.server_dir, 87 args[‘filename‘] 88 )) 89 90 filesize=args[‘filesize‘] 91 recv_size=0 92 print(‘----->‘,file_path) 93 with open(file_path,‘wb‘) as f: 94 while recv_size < filesize: 95 recv_data=self.conn.recv(self.max_packet_size) 96 f.write(recv_data) 97 recv_size+=len(recv_data) 98 print(‘recvsize:%s filesize:%s‘ %(recv_size,filesize)) 99 100 101 tcpserver1=MYTCPServer((‘127.0.0.1‘,8080)) 102 103 tcpserver1.run() 104 105 106 107 108 109 110 #下列代码与本题无关 111 class MYUDPServer: 112 113 """UDP server class.""" 114 address_family = socket.AF_INET 115 116 socket_type = socket.SOCK_DGRAM 117 118 allow_reuse_address = False 119 120 max_packet_size = 8192 121 122 coding=‘utf-8‘ 123 124 def get_request(self): 125 data, client_addr = self.socket.recvfrom(self.max_packet_size) 126 return (data, self.socket), client_addr 127 128 def server_activate(self): 129 # No need to call listen() for UDP. 130 pass 131 132 def shutdown_request(self, request): 133 # No need to shutdown anything. 134 self.close_request(request) 135 136 def close_request(self, request): 137 # No need to close anything. 138 pass
1 import socket 2 import struct 3 import json 4 import os 5 6 7 8 class MYTCPClient: 9 address_family = socket.AF_INET 10 11 socket_type = socket.SOCK_STREAM 12 13 allow_reuse_address = False 14 15 max_packet_size = 8192 16 17 coding=‘utf-8‘ 18 19 request_queue_size = 5 20 21 def __init__(self, server_address, connect=True): 22 self.server_address=server_address 23 self.socket = socket.socket(self.address_family, 24 self.socket_type) 25 if connect: 26 try: 27 self.client_connect() 28 except: 29 self.client_close() 30 raise 31 32 def client_connect(self): 33 self.socket.connect(self.server_address) 34 35 def client_close(self): 36 self.socket.close() 37 38 def run(self): 39 while True: 40 inp=input(">>: ").strip() 41 if not inp:continue 42 l=inp.split() 43 cmd=l[0] 44 if hasattr(self,cmd): 45 func=getattr(self,cmd) 46 func(l) 47 48 49 def put(self,args): 50 cmd=args[0] 51 filename=args[1] 52 if not os.path.isfile(filename): 53 print(‘file:%s is not exists‘ %filename) 54 return 55 else: 56 filesize=os.path.getsize(filename) 57 58 head_dic={‘cmd‘:cmd,‘filename‘:os.path.basename(filename),‘filesize‘:filesize} 59 print(head_dic) 60 head_json=json.dumps(head_dic) 61 head_json_bytes=bytes(head_json,encoding=self.coding) 62 63 head_struct=struct.pack(‘i‘,len(head_json_bytes)) 64 self.socket.send(head_struct) 65 self.socket.send(head_json_bytes) 66 send_size=0 67 with open(filename,‘rb‘) as f: 68 for line in f: 69 self.socket.send(line) 70 send_size+=len(line) 71 print(send_size) 72 else: 73 print(‘upload successful‘) 74 75 76 77 78 client=MYTCPClient((‘127.0.0.1‘,8080)) 79 80 client.run()
标签:.so use header into imp ima 技术 补充 作业
原文地址:http://www.cnblogs.com/maple-shaw/p/7152616.html