基于tcp和udp协议的套接字

时间:2023-03-09 15:40:40
基于tcp和udp协议的套接字

socket:是在应用层和传输层之间的一个抽象层,它把TCP/IP层的复杂的操作封装抽象,并提供一些接口供应用层调用

套接字:被设计用于同一台主机上多个应用程序之间的通信,被称为进程之间通信或IPC
  基于文件类型的套接字:AF_UNIX
    UNIX一切皆文件,基于文件的套接字调用的就是底层文件系统来抓取数据,两个套接字进程运行在同一机器
    可以访问同一文件系统间接完成通信
  基于网络类型的套接字:AF_INET

套接字工作流程

基于tcp和udp协议的套接字

基于tcp和udp协议的套接字

套接字工作流程:服务端先初始化socket(),然后与端口进行绑定bind(),对端口监听listen(),调用accept()阻塞,
  直到等到客户端连接,这时如果客户端初始化一个socket(),然后连接服务器connect(),如果连接成功,则客户端和服务端建立连接。
  客户端发送请求,服务端接收请求并处理请求,然后把处理的数据回复给客户端,客户读取数据,最后关闭连接,交互结束

基于tcp协议的套接字:tcp是基于链接的,必须先启动服务端,再启动客户端

#服务端
import socket ip_port = ("127.0.0.1", 8000)
back_log = 5
buffer_size = 1024 phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 参数分别为(基于网络通讯,TCP协议),表示基于TCP协议的网络通讯
phone.bind(ip_port) # 参数为IP地址和端口号,以元组的形式放进参数里,且IP地址是字符串
phone.listen(back_log) # 表示当前可以有几个链接
print("--->")
while True: #设定循环,可以接收多个链接
conn, addr = phone.accept( ) # 等待客户端信息,得到两个数据,一个是(对方的链接,一个是对方的通讯地址),元组形式 while True: #循环,服务端和客户端能循环多次的传输数据
try: #客户端断开链接时,服务端也会断开链接,这时循环时就会出现异常,需要处理异常
data = conn.recv(buffer_size) # 接收客户端信息,参数代表可以接收多少字节,得到的是二进制形式的数据
print("接到客户端信息:", data.decode("utf-8"))
conn.send(data.upper( )) # 向客户端回传信息
except Exception:
break
conn.close() phone.close()
#客户端
import socket
ip_port = ("127.0.0.1",8000)
back_log = 5
buffer_size = 1024 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(ip_port) #链接服务端 while True:
msg = input(">>>")
if not msg:continue #输入为空时,可以send到自己的缓存中,但os不会将空发给硬件,对方便recv不到数据
phone.send(msg.encode("utf-8")) #向服务端传内容,传输的是二进制格式
data = phone.recv(buffer_size) #获取服务端传回的内容
print("收到服务端发来的消息:",data) phone.close()

补充1:

应用程序和os都是先加载到缓存再使用,所有操作都是对缓存读取和传输数据,只有os才可以操作硬件

应用程序将数据send到缓存中,然后由os操作传给硬件,再进行网络传输,对方在recv到(recv也是从缓存中recv数据)

如果send的数据为空,可以send到自己的缓存中,但recv不到空

补充2:

对于服务端来说,客户端断开链接,服务端accept不到链接,就会出现异常,程序就会停止,可以加个异常处理

补充3:

有时重启服务端时会出现异常:address already in use,这是断开链接时进入四次挥手,但time_wait状态在占用地址

可以加一个socket配置,重用IP和端口

在phone.bind()前加一个phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)

基于udp协议的套接字:udp是无链接传输,先启动哪边都不会报错,可以实现并发,同时与多个客户端通讯

from socket import *         #将socket下的所有属性都导入过来,这样可以直接使用属性

ip_port = ("127.0.0.1",8080)
buffer_size = 1024 udp_server = socket(AF_INET,SOCK_DGRAM) #DGRAM表示数据报式套接字
udp_server.bind(ip_port) while True:
data,addr = udp_server.recvfrom(buffer_size) #接收到客户端发过来是数据加客户端的IP和端口
print(data,addr)
udp_server.sendto(data.upper(),addr) #无链接需要指定客户端的IP和端口
from socket import *

ip_port = ("127.0.0.1",8080)
buffer_size = 1024 udp_client = socket(AF_INET,SOCK_DGRAM) while True:
msg = input(">>")
udp_client.sendto(msg.encode("utf-8"),ip_port) #sendto需要指定发送到哪个IP和端口
data,addr = udp_client.recvfrom(buffer_size)
print(data.decode("utf-8")) udp_client.close()

补充1:

udp协议send一个空,依然可以fevcfrom到一个空