python-网络编程socket模块详解
# ### tcp 循环发消息 import socket # 1.创建一个对象 sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 2.绑定ip,端口号,在网络上注册该主机 sk.bind( ("127.0.0.1",9004) ) # 3.监听端口 sk.listen() while True: # 4.建立三次握手 conn,addr = sk.accept() # 5.收发数据的逻辑 # 该循环的作用:是循环发消息 while True: # 接受消息 res = conn.recv(1024) print(res.decode()) # 发送数据 strvar = input("请输入你要给对方的消息:") conn.send(strvar.encode()) # 退出 if strvar == "q": break # 6.四次挥手 conn.close() # 7.退还端口 sk.close()
# ### tcp 客户端 import socket # 1.创建一个socket对象 sk = socket.socket() # 2.与服务器建立连接 sk.connect( ("127.0.0.1",9004) ) # 3.收发数据的逻辑 while True: # 发送消息 strvar = input("请输入您要发送的消息:") sk.send(strvar.encode()) # 接受消息 res = sk.recv(1024) if res == b"q": break print(res.decode()) # 4.关闭连接 sk.close()
从上面的服务端与客户端我们可以知道,客户端无法单独存在,必须等待对应的服务端开启后才能与服务端开启通讯,而在TCP下的c/s模式下只能支持一客户端一服务器通话,而其他客户端连接进来无法做到实时通讯,必须等待前面的客户端断开连接后我才能与服务端交流,但同时也因为这个特性也早就了TCP下的传输数据稳定,不丢包等优点,缺点:速度慢连接拖拉
下面我们看看UDP下的socket是怎么样子的吧
服务端
# ### udp 服务端 import socket # 1.创建一个socket对象 sk = socket.socket(type=socket.SOCK_DGRAM) # 2.绑定ip,端口号 sk.bind( ("127.0.0.1",9000) ) # 3.收发数据 # udp协议,如果作为服务器,第一次需要先接受消息,才能得到对方的ip和端口号 msg,cli_addr = sk.recvfrom(1024) print(msg,cli_addr) """ b's9\xe6\x80\xbb\xe5\x86\xb3\xe8\xb5\x9b,lpl\xe5\x86\x8d\xe9\x80\xa0\xe8\xbe\x89\xe7\x85\x8c,\xe5\x87\xa4\xe5\x87\xb0\xe6\xb6\x85\xe6\xa7\x83' ('127.0.0.1', 53492) """ print(msg.decode()) # 发送数据给客户端 msg = "iG牛逼" sk.sendto(msg.encode("utf-8"),cli_addr) # 4.关闭连接 sk.close()
客户端
# ### 客户端 import socket # 1.创建socket对象 sk = socket.socket(type=socket.SOCK_DGRAM) # 2.收发数据 msg = "s9总决赛,lpl再造辉煌,凤凰涅槃" # sendto(发送的二进制字节流消息,对方的ip和端口[元组]) sk.sendto(msg.encode("utf-8"),("127.0.0.1",9000)) # 接受数据 msg,addr = sk.recvfrom(1024) print(msg.decode("utf-8")) print(addr) # 3.关闭udp连接 sk.close
从上面我们可以看出,UDP协议下连接服务器时不会因为TCP的三次握手跟四次挥手而造成的连接拖沓,因为这个特点理论上可以同时又多个客户端同时连接服务器,而服务器可以同时给这些客户端提供服务,且不会理客户端的当前的连接状态,我只管发,但你收不收我不管,udp协议是基于这样的情况下运作的,那么UDP跟TCP的对比有啥优点呢?
快,不仅连接快,传输速度也快,但同时也造就了容易丢包,解决方法 创建缓存区,把数据一股气全部丢到缓存区里让客户端子去取,这样避免造成大面积丢包
这两种协议在传输数据时都存在黏包现象,为什么呢?
因为数据传输过程中服务端发送的太快,而客户端接收的太慢了,也就造成了数据黏包,从而造成数据紊乱,
解决这个问题有两个方案
(1)在服务端与客户端加延时可以避免黏包的现象发生
(2)把要传送的数据大小先发送给客户端,然后约定好每次发送多少,通过这样准确的发送与接收也可以避免黏包现象的发生
下面我通过代码来解释这个现象
# ### 黏包 服务端 import socket import time sk = socket.socket() sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 在网络上注册该主机 sk.bind( ("127.0.0.1",9001) ) # 监听端口 sk.listen() # 三次握手 conn,addr = sk.accept() # 处理收发数据的逻辑 # 在真正第一次发送数据之前,把真实的数据长度先发过去 conn.send("00000120".encode()) # 第一次发送 msg = "hello," * 20 conn.send(msg.encode()) # time.sleep(0.1) # 第二次发送 conn.send("world".encode()) # 四次挥手 conn.close() # 退还端口 sk.close()
# ### 客户端 import socket import time # 创建一个tcp对象 sk = socket.socket() # 连接服务器 sk.connect( ("127.0.0.1",9001) ) time.sleep(0.2) # 接受发送过来的真实的数据长度 res = sk.recv(8) num = int(res.decode("utf-8")) # 处理收发数据逻辑 res1 = sk.recv(num) # hello, res2 = sk.recv(1024) # world print(res1) print(res2) # 关闭链接 sk.close()
这是通过延时接收隔离了黏包现象,简单点就是接收速度变慢了
不推荐这种用法,影响效率
下面第二种
# ### 客户端 import socket import time # 创建一个tcp对象 sk = socket.socket() # 连接服务器 sk.connect( ("127.0.0.1",9001) ) time.sleep(0.2) # 接受发送过来的真实的数据长度 res = sk.recv(8) num = int(res.decode("utf-8")) # 处理收发数据逻辑 res1 = sk.recv(num) # hello, res2 = sk.recv(1024) # world print(res1) print(res2) # 关闭链接 sk.close()
# ### 客户端 import socket import struct sk = socket.socket() sk.connect( ("127.0.0.1",9000) ) # 处理收发数据的逻辑 # 接受第一次发送过来的长度 n = sk.recv(4) tup = struct.unpack("i",n) n = tup[0] print(n,type(n)) # 接受第二次发送过来的真实数据 res1 = sk.recv(n) print(res1.decode("utf-8")) res2 = sk.recv(1024) print(res2.decode("utf-8")) # 关闭链接 sk.close()
这种就是我上面说的那样,通过先计算文件大小然后在跟客户端商量好每次发多少给你客户端接收多少,即避免了黏包,又提升力了效率
zsir 日常分享