【问题标题】:C# SocketException Can not connect because the target machine has actively declined the connectionC# SocketException 无法连接,因为目标机器已主动拒绝连接
【发布时间】:2018-08-13 10:23:54
【问题描述】:

我正在尝试在本地用 c# 编写一个聊天客户端/服务器来熟悉 Sockets。

首先我用(非常简化的)以下代码启动服务器:

Server.cs

    private readonly MessageManager _messageManager;
    private readonly ChatServer _chatServer;

    public ChatServerSkeleton()
    {
        _messageManager = new MessageManager();
        _chatServer = new ChatServer();
        Console.WriteLine("Server is running on: " + _messageManager.MyAddress);
    }

然后我以 +- 相同的方式启动客户端,除了我将服务器地址存储在客户端中(我将服务器地址复制到提示符中)。

Client.cs

    private readonly MessageManager _messageManager;
    public ChatClient ChatClient { get; }

    public ChatClientSkeleton(IPEndPoint serverAddress, string name)
    {
        _messageManager = new MessageManager();
        ChatClient = new ChatClient(new ChatServerStub(serverAddress, _messageManager), name);

        Console.WriteLine($"IPAddress of {name} is: {_messageManager.MyAddress}");
        Console.WriteLine($"IPAddress of Server is: { serverAddress}");
    }

MessageManager.cs

    private readonly TcpListener _serverSocket;
    public IPEndPoint MyAddress { get; }

    public MessageManager()
    {
        try
        {
            //Create server socket on random port
            _serverSocket = new TcpListener(IPAddress.Any, FindFreeTcpPort());

            //Get host ip address
            IPAddress[] localIps = Dns.GetHostAddresses(Dns.GetHostName());
            IPAddress localhost = localIps.First(ip => ip.AddressFamily == AddressFamily.InterNetwork);

            //Get port of serversocket
            IPEndPoint ipEndPoint = _serverSocket.LocalEndpoint as IPEndPoint;
            int port = ipEndPoint.Port;

            //Create address
            MyAddress = new IPEndPoint(localhost, port);
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine("Something went wrong with the serversocket:");
            Console.Error.WriteLine(ex);
        } 
    }

FindFreeTcp 端口来自这里:https://stackoverflow.com/a/150974/5985593

到目前为止,一切似乎都有效。例如,服务器现在有 192.168.0.219:51080 和客户端 192.168.0.219:51085。

当我尝试使用 MessageManager.cs

中的此代码发送消息时出现问题
    public void Send(MethodCallMessage message, IPEndPoint address)
    {
        try
        {
            _serverSocket.Start();
            TcpClient destination = new TcpClient(address.Address.ToString(), address.Port);
            NetworkStream output = destination.GetStream();
            MessageReaderWriter.Write(message, output);
            destination.Close();
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine("Failed to write a message:");
            Console.Error.WriteLine(ex);
        }
        finally
        {
            _serverSocket.Stop();
        }
    }

_server.Start(); 行更具体。

谁知道我做错了什么?

提前致谢!

编辑: 在服务器上注册客户端时,它运行良好 1 次。但在那之后,如果我想发送一条消息,我会得到目标机器主动拒绝的 SocketException。

我确实在这里使用 AcceptTcpClient():

MessageManager.cs

    public MethodCallMessage WReceive()
    {
        MethodCallMessage result = null;

        try
        {
            //_serverSocket.Start();
            TcpClient client = _serverSocket.AcceptTcpClient();
            NetworkStream input = new NetworkStream(client.Client, true);
            result = MessageReaderWriter.Read(input);
            client.Close();
        }
        catch (Exception ex)
        {
            Console.Error.WriteLine("Failed to receive a message:");
            Console.Error.WriteLine(ex);
        }
        finally
        {
            //_serverSocket.Stop();
        }

        return result;
    }

该方法在ServerSkeleton & ClientSkeleton中使用如下:

    public void Run()
    {
        while (true)
        {
            MethodCallMessage request = _messageManager.WReceive();
            HandleRequest(request);
        }
    }

所以流程基本如下:

  1. 我启动服务器(实例化新的消息管理器,第 3 个 sn-p 并运行 serverskeleton (last sn-p)
  2. 服务器打印 IP 控制台,我复制 ip 并启动​​客户端
  3. 实例化客户端 & 将服务器 ip 设置为我复制粘贴的内容
  4. 启动客户端骨架(最后 sn-p)

【问题讨论】:

    标签: c# sockets network-programming


    【解决方案1】:

    已调用 Start() 的 TcpListener 侦听传入连接,然后将它们堆叠在队列中。一旦该队列已满,则会导致套接字异常。要从队列中删除连接,您需要使用 TcpListener 的 AcceptTcpClient 或 AcceptSocket 方法。这将为您提供一个可以发送和接收数据的连接。

    我猜可能发生的情况是您收到了第一个传入客户端,但不接受并删除它以发送和接收数据,并且由于待处理队列列表已满,您的后续连接被拒绝(这只是一个猜)。

    有一个重载方法 ...TcpListener.Start(int backlog) ...允许您设置待处理队列列表的大小(因此您可以有 5、10 或更多连接等待在 TcpListener 中被接受)

    使用服务器 TCP 套接字的过程是您将其设置为侦听本地地址和端口。然后客户端尝试连接到该端点。当他们连接时,TCP 侦听套接字接受连接,然后将其传递给一个套接字,该套接字是传输数据的套接字。监听套接字继续监听新的连接,它本身并不传输数据。

    我希望这是有道理的?

    所以服务器的行为会更像这样......

    _serverSocket.Start();
    TcpClient myAcceptedConnection = _serverSocket.AcceptTcpClient();
    // in synchronous blocking socket situation the program flow halts 
    // here til a connection is established
    // once you have a connection ... do stuff with myAcceptedConnection
    

    如果您希望避免阻塞情况,可以使用TcpListener.Pending() 查看队列中是否有任何连接等待

    编辑 1:

    好吧,我看到的唯一奇怪的事情是您在 MessageSend 方法中调用了 _serverSocket.Start() 方法?服务器通常不会从发送消息开始......它通常会等待侦听连接,接收并读取连接然后回复(或者它可能会在连接时发送问候等)。

    就我个人而言,我会将服务器的侦听方面与数据的发送和接收分开......毕竟您希望您的服务器一直在侦听传入连接,直到您将其关闭。当你检测到一个传入的连接(可能通过在循环中检查 Pending()),你可以接受它并在新的 TcpClient 上发送和接收。当您完成在该客户端上传输/接收的任何数据时,您可以将其关闭,如果这是您想要的……您不需要在每次发送消息时关闭和打开 tcp 连接,您可以离开它打开直到你完成它,实际上打开和关闭 tcp 连接会在它们通过的握手协议中产生一些开销。

    但有一些警告...... Tcp 连接可能会“半开”,尤其是无线连接可能会导致问题。进入这里有点复杂,但我推荐Stephen Cleary 的这组出色的文章作为一个很好的通读。阅读整个博客,因为那里有大量的好信息。

    所以,回到简单的,我会...

    1. 一个 serverStart() 方法,用于启动服务器侦听。
    2. 一个 serverAccept() 方法,用于检查是否有任何挂起的连接,如果存在则接受它们。
    3. 用于连接服务器的客户端的 clientConnect() 方法
    4. 用于进行数据传输的服务器和客户端的 Read() 和 Write() 方法。

    通常情况下,流程是......

    1. 服务器监听
    2. 客户端连接
    3. 服务器接受
    4. 客户端发送
    5. 服务器接收
    6. (然后服务器发送/接收,客户端发送/接收)
    7. 一切都关闭并关闭

    【讨论】:

    • 哇!谢谢!这确实有很大帮助:D!不过,我确实在一个 WReceive() 方法中使用了 AcceptTcpClient(),该方法在 while(true) {} 中调用。 (我会将它添加到 Q 中)如果我使用 Pending() 检查它确实表明在第一个之后还有一个待处理。那么 AcceptTcpClient() 是否意味着队列被清除?否则我错过了一些东西,问题可能是我的项目结构。我会更新问题,但您的回答很有帮助!
    • @JC97 AcceptTcpClient 接受并删除队列中的第一个连接,如果只有一个,那么是的,它被清除......如果有更多,那么它只会减少一个。如果您可以显示更多代码,例如如何运行服务器以及如何运行客户端,我可能会提供更多帮助。顺便说一句,我认为 TCP 侦听套接字中的默认挂起队列大小为 2(无论如何我都想不通……不完全确定是这样)
    • 添加了更多代码。如果还不够,我会在某个地方上传一个 git 存储库
    • @JC97 回复了,如果你愿意可以发到 github 我可以调试你的代码看看哪里出错了
    • @JC97 我从来没有在服务器上收到套接字错误,但是我无法让客户端相互通信,但那是因为他们关闭了与服务器的连接然后启动一旦他们自己注册了他们自己的服务器。因此,要继续前进,我将保持与服务器的连接打开,从客户端开始删除服务器......然后将来自客户端的消息发送到已经打开的连接。然后,服务器将这些消息路由到它已注册的客户端。 (在退出应用程序之前,所有连接都保持打开状态)
    猜你喜欢
    • 2021-11-01
    • 1970-01-01
    • 2014-05-09
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多