socket是什么
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
socket工作流程
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
tcp通信
接下来我们就实现一个socket通讯例子,就那tcp举例
服务端:
import socket
#获取socket对象
# AF_INET:IPv4 网络协议的套接字类型
# SOCK_STREAM:提供面向连接的稳定数据传输,即TCP协议
mServerSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#解决端口占用问题
mServerSocket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
#绑定ip+端口
mServerSocket.bind(("127.0.0.1",8080))
mServerSocket.listen(5)
print("start...")
conn,addr = mServerSocket.accept()
# print(conn,addr)
#最大接收的字节数
data = conn.recv(1024)
print("recv:",data.decode("utf-8"))
conn.send(data.upper())
conn.close()
mServerSocket.close()
客户端:
import socket
mClientSocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
mClientSocket.connect(("127.0.0.1",8080))
mClientSocket.send("hello 套接字".encode("utf-8"))
data = mClientSocket.recv(1024)
print("recv:",data.decode("utf-8"))
mClientSocket.close()
运行服务端,在运行客户端
服务端输出如下:
E:\python\python_sdk\python.exe E:/python/py_pro/server.py
start...
recv: hello 套接字
Process finished with exit code 0
客户端输出如下:
E:\python\python_sdk\python.exe E:/python/py_pro/client.py
recv: HELLO 套接字
Process finished with exit code 0
udp通信
udp是无链接的,先启动哪一端都不会报错
服务端:
from socket import *
server=socket(AF_INET,SOCK_DGRAM)
server.bind(('127.0.0.1',8083))
while True:
data,client_addr=server.recvfrom(1024)
print('客户端的数据: ',data)
server.sendto(data.upper(),client_addr)
客户端:
from socket import *
client=socket(AF_INET,SOCK_DGRAM)
while True:
msg=input('>>: ').strip()
client.sendto(msg.encode('utf-8'),('127.0.0.1',8083))
data,server_addr=client.recvfrom(1024)
print(data.decode('utf-8'))
客户端输入:
>>: wyf
WYF
>>:
服务端输出:
E:\python\python_sdk\python.exe "E:/python/py_pro/2 基于udp协议的套接字通信/服务端.py"
客户端的数据: b'wyf'
udp数据报协议
UDP(user datagram protocol,用户数据报协议)是无连接的,面向消息的,提供高效率服务。不会使用块的合并优化算法,, 由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了。 即面向消息的通信是有消息保护边界的。
udp的recvfrom是阻塞的,一个recvfrom(x)必须对唯一一个sendinto(y),收完了x个字节的数据就算完成,若是y>x数据就丢失,这意味着udp根本不会粘包,但是会丢数据,不可靠
第一种情况
服务端:
from socket import *
server=socket(AF_INET,SOCK_DGRAM)
server.bind(('127.0.0.1',8081))
data,client_addr=server.recvfrom(11111111)
print('数据2: ',data)
客户端:
from socket import *
client=socket(AF_INET,SOCK_DGRAM)
client.sendto('hello'.encode('utf-8'),('127.0.0.1',8081))
client.sendto('world'.encode('utf-8'),('127.0.0.1',8081))
启动客户端,服务端
数据2: b'hello'
第二种情况
如果将服务端代码改成如下:
from socket import *
server=socket(AF_INET,SOCK_DGRAM)
server.bind(('127.0.0.1',8081))
data,client_addr=server.recvfrom(11111111)
print('数据1: ',data)
data,client_addr=server.recvfrom(11111111)
print('数据2: ',data)
启动客户端、服务端会输出如下:
数据1: b'hello'
数据2: b'world'
第三种情况
如果将服务端改成如下:
from socket import *
server=socket(AF_INET,SOCK_DGRAM)
server.bind(('127.0.0.1',8081))
data,client_addr=server.recvfrom(11111111)
print('数据1: ',data)
data,client_addr=server.recvfrom(1)
print('数据2: ',data)
会如下报错
E:\python\python_sdk\python.exe "E:/python/py_pro/3 数据报协议/服务端.py"
Traceback (most recent call last):
数据1: b'hello'
File "E:/python/py_pro/3 数据报协议/服务端.py", line 11, in <module>
data,client_addr=server.recvfrom(1)
OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小。