1. sticky package
# Tcp protocol when sending data packets will be sticky phenomenon.
(1) Data package is sticky because the client / server will have a data buffer,
the buffer is used to temporarily hold data, in order to ensure a complete receiver to be able to data, so the buffer will be set relatively large.
(2) send and receive data at frequent, since borderless tcp transmitting a message, do not know how much should the length taken
cause the client / server, are likely to be a plurality of data as data capture, resulting in sticky package
Example: The following three methods is adopted, Time module 1 may be used, the client or the server or sender of the data received one time slightly delayed, this can be done non-stick package.
2. The server in this example when sending data to send data to a client, let the client know how much data will be accepted, so it will not stick package. (method 1)
3. The method of malpractice 1. Once the server is sending too much data, each client does not need to accept the number of data and then again on the line assignment, write directly on the receiving eight bytes on the line, server-side use of "00000120" to allow customers to end know that each time you take 8 bytes on the line,
res0 = int (sk.recv (8) .decode ( "utf-8")) is not required to change each time the number 8, 8 bytes each receiving line, in such a way server transmitting to the line can.
The above figure is the number of bytes you need to accept. So it will not cause sticky package.
4. Method three is packed into the data line 4 bytes transmitted on the line according to the line use socketserver be transmitted using any number stuct.pack () on the line, and then let the client know you accept four bytes, then at the client, use struct.unpack () is 4 bytes of the packet on the line decompression to obtain the original data, so that the package will not cause sticky.
5. When the above circumstances are sent twice to the data in the server from the client to establish, generally twice the data without anything, because the tcp is borderless, do not know how many bytes to accept the customer end for the first time to accept recv (10) a second time to accept the client recv (10), the server side for the first time helllo send a second transmission is world, due to the borderless cause for the first time accepted that b " helloworl "the second is to accept b" d "have launched come.
Solution one :( will not cause stick pack)
Client code:
# ### 客户端 import socket import time sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) # time.sleep(0.2) res0 = int(sk.recv(1).decode("utf-8")) #res0 "6" print(res0) res1 = sk.recv(res0) print(res1) # print(res1.decode("utf-8")) res2 = sk.recv(10) print(res2) sk.close()
服务器端:
# ### server Import socket Import Time SK = socket.socket () # add this sentence before the bind method, you can make a port reuse sk.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) # tie set the address port (registered on the network host) sk.bind (( " 127.0.0.1 " , 9000 )) sk.listen () Conn, addr = sk.accept () conn.send ( " . 6 " .encode ( " UTF -8 " )) Message = " Hello, " conn.send (message.encode ( " UTF-. 8" )) # The time.sleep (0.1) conn.send ( " world " .encode ( " UTF-8 " )) # fourth wave conn.Close () # refunded port sk.close ()
Summary: In tcp line contract You can allow data to a client know how much we will send the packet to him, as is starting a 6 and then send "hello" and "world"
after the first receipt hello, received a second data will not cause sticky packet data.
Method two:
using recv (8) to accept the 8 bytes, so that the server can send data.
Client:
# ### 客户端 import socket import time sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) # time.sleep(0.2) res0 = int(sk.recv(8).decode("utf-8")) #res0 "6" print(res0) res1 = sk.recv(res0) print(res1) # print(res1.decode("utf-8")) res2 = sk.recv(10) print(res2) sk.close()
Service-Terminal
# ### server Import socket Import Time SK = socket.socket () # add this sentence before the bind method, you can make a port reuse sk.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) # tie set the address port (registered on the network host) sk.bind (( " 127.0.0.1 " , 9000 )) sk.listen () Conn, addr = sk.accept () conn.send ( " 00.00012 million " .encode ( " UTF -8 " )) Message = " Hello, " * 20 is conn.send (message.encode ( "8-UTF " )) # the time.sleep (0.1) conn.send ( " world " .encode ( " UTF-8 " )) # fourth wave conn.Close () # refunded port sk.close ()
# server end use # time.sleep (0.1) delay the server to the client can also send data, which is sent to a data server and then wait for the client to send data again after receiving a second data like this will do.
Method three:
# Use module socketserver
Client code:
# ### client Import Socket Import struct Import Time SK = socket.socket () sk.connect (( " 127.0.0.1 " , 9000 )) the time.sleep ( 0.1 ) # to accept a length of 4 bytes, he is actually that number to be transmitted to the conversion. n-sk.recv = (. 4 )
# n- = struct.unpack ( " I " , n-) [0] Print (n-) # next receive data sent from the server RES1 = SK .recv (n-) Print (res1.decode ( " UTF-. 8 " )) RES2Sk.recv = (1024 ) Print (res2.decode ( " UTF-. 8 " )) # spaces are not encoded in ascii, attention. Sk.send (B " i_love_you " ) # close the connection sk.close ()
Server code:
# ### server Import Socket Import struct SK = socket.socket () sk.setsockopt (socket.SOL_SOCKET, socket.SO_REUSEADDR, . 1 ) sk.bind (( " 127.0.0.1 " , 9000 )) sk.listen () Conn, addr = sk.accept () InP = INPUT ( " >>> MSG: " ) MSG = inp.encode ( " UTF-. 8 " ) # length of the transmission data converted by pack, having a fixed length becomes 4 byte values RES = struct.pack ( " I " , len (MSG)) conn.send (RES) # Next, start the real transmission data conn.send (MSG) conn.send ( " World " .encode ( " UTF-. 8 " )) RES = conn.recv (1024 ) Print (RES) Print (res.decode ( " UTF-8 " )) # fourth wave conn.Close () # refunded port sk.close ()
Summary: using, after SocketServer, the first server transmits the data to the client, no matter how long the length of the transmission transmitted by struct.pack ( "i", len ( msg)) after the calculation
value is in accordance with 4-byte length to be transmitted, then the byte is sent to the client, it knows to accept the first four bytes of the line length, the client using unpack (), the data obtained on the line on the line
to restore the original data, so as not to cause sticky package.
2.
Package # sticky phenomenon:
the transmitting side, since the two short data transmission interval time short, the packets are formed at the transmitting end sticky
# package sticky phenomena:
at the receiving end, since the two data are almost simultaneously transmitted to a counterpart cache, all formed at the receiving end of the packet sticky
# summary:
transmitting end, a time interval between the short packet or a receiver, receiving is not timely, it will stick package
core because borderless tcp data taken, not according to the transmission Analyzing sequence
# Package to solve the sticky scenarios:
scenarios in real-time communication, need to read the message sent is what the
# package does not need to solve the sticky scene:
download or upload a file, and finally put packages together, sticky bag does not matter.
4. Module socketserver
The bottom of the socket is # network protocols, based on the original socket module, and a layer of the package, is SocketServer
SocketServer to achieve tcp protocol, server side concurrency.
:( basic usage method to solve the sticky bag three points will be used to knowledge)
import struct # pack 把任意长度的数字转化成具有固定4个字节长度的字节流 # unpack 将4个字节的值恢复成原本的数据,最后返回一个元组 # i => int 我要转化的当前数据类型是整型 res = struct.pack("i",304535345) print(res) print(len(res)) res = struct.unpack("i",res) print(res) print(res[0],type(res[0]))
5.sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)加上可以实现端口的互用
6.socketserver并发
客户端1代码:(不带循环的客户端就行访问服务器)
# ### 客户端 import socket sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) sk.close() ''' try: print(name) # 无论是否报错,都会执行finally里面代码块; finally: pass ''' # print(name) """ 如果有一些语句,即使在发生报错的情况也仍然要调用或处理一些逻辑,那么使用finally """ # try: # print(name) # finally: # print(123) # print(456) ''' try这个代码块如果有错误,不执行else代码块中的内容 如果没有报错,那么执行else代码块中的内容. try ... except ... else 要配合使用 else不能单独拿出来和try使用. try ... finally ... 可以配合在一起使用. ''' ''' try: # print(name) print(123) except: pass else: print(789) '''
服务器1代码:
import socketserver # 需要自定义一个类,并且继承socketserver.BaseRequestHandler class MyServer(socketserver.BaseRequestHandler): def handle(self): #<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 9000), raddr=('127.0.0.1', 54088)> print(self.request) print("--->执行这句话") # Threading( (ip,端口号) , 自定义类 ) server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) , MyServer) # 循环调用 server.serve_forever()
服务器代码:
只要有客户端连接服务器就会打印出下面的代码。self.request相当于,接收到有人连接服务器,myserver这个类下面的函数handle就会自动去处理。看看是谁连接到了我,从而支持并发。
# 由于客户端没有给服务器发送消息,服务器这边采用可socketserver支持多人连接。
# 带循环的客户端2
import socket sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) while True: sk.send(b"wangwen_dashuaguo") msg = sk.recv(1024) print(msg) sk.close()
# 服务器代码2:
import socketserver class MyServer(socketserver.BaseRequestHandler): # 在handle里面自定义收发逻辑 def handle(self): print("--->这句话被执行了") conn = self.request while True: msg = conn.recv(1024).decode("utf-8") print(msg) conn.send(msg.upper().encode("utf-8")) # 产生一个对象 server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) ,MyServer ) # 循环调用 server.serve_forever()
运行代码:
小结:
1.上面开的三个客户端都是发送wangwen_dashuaguo
2.服务器这边的逻辑就是只要有客户端来连接我我就使用
# 产生一个对象 server = socketserver.ThreadingTCPServer( ("127.0.0.1",9000) ,MyServer )
来产生一个对象就行处理,在这个类当中可以发现只要是客户端给我发消息我就可以接受到,然后再将他发送的内容发送给他。从而形成了并发,可以是多个客户端连接服务器。conn = self.request也很关键。