当前位置: 首页 > 知识库问答 >
问题:

服务器退出后关闭客户端套接字

焦宏硕
2023-03-14

我正在编写一个简单的客户端/服务器套接字程序,其中客户端与服务器连接并通信,然后它们向服务器发送退出消息,然后服务器关闭连接。代码如下所示。

服务器.py

import socket
import sys
from threading import Thread

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    # This is to prevent the socket going into TIME_WAIT status and OSError
    # "Address already in use"
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error as e:
    print('Error occured while creating the socket {}'.format(e))

server_address = ('localhost', 50000)
sock.bind(server_address)

print('**** Server started on {}:{} ****'.format(*server_address))

sock.listen(5)


def client_thread(conn_sock, client_add):
    while True:
        client_msg = conn_sock.recv(1024).decode()
        if client_msg.lower() != 'exit':
            print('[{0}:{1}] {2}'.format(*client_add, client_msg))
            serv_reply = 'Okay ' + client_msg.upper()
            conn_sock.send(bytes(serv_reply, 'utf-8'))
        else:
            conn_sock.close()
            print('{} exitted !!'.format(client_add[0]))
            sys.exit()


try:
    # Keep the server until there are incominmg connections
    while True:
        # Wait for the connctions to accept
        conn_sock, client_add = sock.accept()
        print('Recieved connection from {}:{}'.format(*client_add))
        conn_sock.send(
            bytes('***** Welcome to {} *****'.format(server_address[0]), 'utf-8'))
        Thread(target=client_thread, args=(
            conn_sock, client_add), daemon=True).start()

except Exception as e:
    print('Some error occured \n {}'.format(e))
except KeyboardInterrupt as e:
    print('Program execution cancelled by user')
    conn_sock.send(b'exit')
    sys.exit(0)

finally:
    sock.close()

client.py

import socket
import sys

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 50000)
print('Connecting to {} on {}'.format(*server_address))
sock.connect(server_address)


def exiting(host=''):
    print('{} exitted !!'.format(host))
    sys.exit()


while True:
    serv_msg = sock.recv(1024).decode()
    if serv_msg.lower() != 'exit':
        print('{1}: {0}'.format(serv_msg, server_address[0]))
        client_reply = input('You: ')
        sock.send(bytes(client_reply, 'utf-8'))

        if client_reply.lower() == 'exit':
            exiting()
    else:
        exiting('Server')

我想要的是万一服务器通过ctrl-c或任何其他方式退出,我希望关闭所有客户端套接字,并将msg发送给客户端,他们也应该在其上关闭套接字。

我在下面除部分之外的部分做了一些工作,但由于某些原因,客户端没有收到服务器发送的消息。

except KeyboardInterrupt as e:
print('Program execution cancelled by user')
conn_sock.send(b'exit')
sys.exit(0)

令人惊讶的是,如果我srvr_reply从client_thread发送“退出”消息,客户端会接受消息并在其末端退出客户端套接字。所以我不确定为什么服务器无法在上述代码的除外部分中发送相同的消息。

共有1个答案

江煜
2023-03-14

很抱歉,除非您尝试通过连接发送数据,否则无法检测到TCP / IP连接的异常终止。

这被称为“半开放”套接字,在Python文档中也有提到。

通常,当服务器进程崩溃时,操作系统会关闭TCP/IP套接字,向客户端发出关闭信号。

当客户端接收到信号时,可以在轮询的同时检测到服务器的终止。轮询机制(即轮询/电子轮询/k队列)将测试HUP(挂起)事件。

这就是为什么“半开”套接字在开发中不会发生,除非是强制的。当客户端和服务器都在同一台机器上运行时,操作系统将发送有关关闭的信号。

但是,如果服务器计算机崩溃或连接丢失(即移动设备),则不会发送此类信号,客户端永远不会知道。

检测异常终止的唯一方法是失败的< code>write尝试< code>read不会检测到该问题(它将表现为好像没有接收到数据)。

这就是为什么他们发明了ping概念,这就是为什么HTTP / 1.1服务器和客户端(不支持ping)使用超时来假设终止。

这里有一篇关于半开放插座的好博客文章。

EDIT(因评论而澄清)

如何处理这种情况:

我建议如下:

>

  • 将显式Ping消息(或Empty/NULL消息)添加到您的协议(客户端和服务器都能理解的消息)。

    通过记录每个发送接收操作来监视套接字的不活动状态。

    在代码中添加超时监视。这意味着您将需要实现轮询,例如select(或轮询或特定于操作系统的 /

    当达到连接超时时,发送 Ping / 空消息。

    为了简单的解决方案,请在发送ping后重置超时。

    下一次轮询套接字时,轮询机制应该会提醒您连接失败。或者,第二次尝试ping服务器/客户机时,您会得到一条错误消息。

    请注意,即使连接丢失,第一个发送操作也可能成功。

    这是因为 TCP/IP 层发送消息,但发送函数不等待 TCP/IP 的 ACK 确认。

    但是,当您第二次执行ping操作时,TCP/IP层可能已经意识到没有< code>ACK到来,并在套接字中注册了错误(这需要时间)。

    退出服务器之前发送失败的原因

    我留下的关于这个问题的评论是错误的(部分)。

    conn_sock.send(b'退出')失败的主要原因是conn_sock是客户端线程中的局部变量,无法从引发SIGINT(CTRL C)的全局状态访问。

    这是有道理的,因为如果服务器有多个客户端会发生什么?

    但是,< code>socket.send确实只调度要发送的数据,所以数据实际发送的假设是不正确的。

    另请注意,如果内核缓冲区中没有足够的空间,socket.send 可能不会发送整条消息。

  •  类似资料:
    • 我正在开发一个非常简单的Java客户机/服务器系统(只是为了让我的脚沾满套接字)。由于某种原因,我一直收到“套接字已关闭”错误。。。这是我的密码。。 服务器文件 客户端文件 我在客户端的第41行得到了错误,然后在第46行得到了NullPointerException。。 提前感谢您的帮助。我只是想在这里学习。

    • 我使用的代码女巫只允许4个不同端口上的4个连接。此代码正在工作,但当客户端关闭连接时,它无法重新建立连接。连接被拒绝。认为是因为线程关闭。如何解决这个问题?我无法更改端口号... 从套接字导入* BUFF=25 def服务器(主机、端口): 如果名称==“main”:导入线程

    • 问题内容: 我正在通过TCP创建一个Java客户端/服务器应用程序,其中有两个套接字: 一种是交换消息。 二是用于文件传输。 我已经在服务器中创建了两个ServerSockets,以便 通过接受ServerSockets 创建套接字1和2 。 首先,客户端通过第一个套接字发送一些字节, 以便它可以告诉服务器它需要哪个文件。 然后,服务器通过第二个套接字将文件发送给客户端。 客户端收到文件后,尝试将

    • 我试图用java实现一个客户端服务器,在这里我读取客户端中的输入并在服务器中执行UperCase,然后返回客户端并打印UperCase。我使用ObjectOutputStream和ObjectInputStream进行读写,但是当我在客户机中键入一个msg时,程序会显示以下错误: Digite uma msg casa java.io.eofexception位于java.io.datainput

    • 问题内容: 我必须使用Java的套接字API编写多线程客户端和服务器。客户端和服务器都是多线程的,因此服务器可以处理多个连接,客户端可以测试服务器处理连接的能力。 我的代码在这里:https : //github.com/sandyw/Simple-Java-Client- Server 我有几个可能是相关的问题。一,偶尔会有一个客户端线程抛出 从其位置来看,这意味着服务器在客户端完成从套接字读取

    • 我想用java写一个传输文件套接字程序。但我有问题,如何取消时传输文件。 当我在客户端关闭inputstream时,服务器如何知道它关闭了outputstream。 这是我的代码:客户 ...... 服务器 ...... 但当我关闭inputstream时没有显示任何内容