分类
技术

Socket中用非阻塞模式的recvfrom方法接收

上一篇中的Client端和Sever端的代码,只有在Client端发送 exit 才能安全结束两个python进程,如果直接退会导致while循环后的 s.close() 没有被执行,那么被占用的端口就不能被释放,只用重启才能接触端口占用。Client端还好,可以手动输入exit来结束,Sever端则需要一直等接收到才能结束,这显然是不合理的。

解决这个问题只需要在结束的时候执行 s.close() 即可,可以用 try: ;except KeyboardInterrupt:;语句来捕获结束的信号,在命令行按下Ctrl-C触发执行except KeyboardInterrupt:中的内容即可。Client端的代码如下

Client端:

import socket
# UDP客户端持续发送消息代码
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建UDP类型的套接字
#s.bind(("127.0.0.1", 46552)) #UDPclient端可以不绑定端口,client端不绑定时是随机端口发送的
addr = ("127.0.0.1", 46551)
try:
    while True:
        data = input("请输入:")
        s.sendto(data.encode("gbk"), addr)
        if data == "exit":
            print("结束聊天!")
            break
    s.close()
except KeyboardInterrupt: #Ctrl-C
    s.close()
    print('结束聊天!')

Sever端用同样的方法并不能直接退出python进程,经分析,发现是因为 s.recvfrom 默认是阻塞状态,所以调用不会立即返回,而是进入睡眠等待操作完成,只需要将将套接字设置为非阻塞模式, recvfrom 函数在非阻塞模式下进行调用,如果没有数据包可用,则会抛出 socket.error 异常,该异常可以通过 try/except 语句进行捕获,从而使程序可以继续执行其他操作。Sever端代码如下

Sever端:

import socket
# 持续通信
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 创建UDP类型的套接字
s.bind(("127.0.0.1", 46551))  # 绑定端口,ip可以不写
s.setblocking(False)  # 将套接字设置为非阻塞模式(默认阻塞模式,执行到recvfrom函数会阻塞)
#recvfrom函数在非阻塞模式下进行调用,如果没有数据包可用,则会抛出socket.error异常,该异常可以通过try/except语句进行捕获,从而使程序可以继续执行其他操作
print("等待接收数据!")
try:
    while True:
        try:
            recv_data, recv_addr = s.recvfrom(1024)  # 1024表示本次接收的最大字节数,recvfrom一直监听知道由数据被接受
            print(recv_data, recv_addr)
            data = recv_data.decode('gbk')
            print(f"收到远程信息:{data},from {recv_addr}")
            if data == "exit":
                print("结束聊天!")
                break
        except socket.error:
            pass
    s.close()
except KeyboardInterrupt: #Ctrl-C
    s.close()
    print('结束聊天!')

阻塞就是让当前调用线程一直处于停止等待当中,挂起的状态,线程函数会被卡住。
非阻塞则是不管运行结果如何,都会继续往下执行(往往都要处理很多返回结果),线程函数里一般都是一个循环,不停的轮询。
再理一下发送接收函数。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

在此处输入验证码 : *

Reload Image