C# SocketException 无法连接,因为目标机器已主动拒绝连接
C# SocketException Can not connect because the target machine has actively declined the connection
我正在尝试在本地用 c# 编写聊天 client/server 以熟悉套接字。
首先,我使用(非常简单的)以下代码启动服务器:
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 端口来自这里:
到目前为止一切似乎都有效。假设服务器现在有 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);
}
}
所以流程基本如下:
- 我启动服务器(实例化新的消息管理器,第三个片段
和 运行 serverskeleton(最后一个片段)
- 服务器打印IP
控制台,我复制 ip 并启动客户端
- 实例化客户端&
将服务器 ip 设置为我复制粘贴的
- 启动客户端框架(最后
片段)
已调用 Start() 的 TcpListener 侦听传入连接,然后将它们堆叠在队列中。一旦该队列已满,就会产生套接字异常。要从队列中删除连接,您需要使用 TcpListener 的 AcceptTcpClient or 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 上发送和接收。当您完成该客户端上的任何数据后 transmitting/receiving 您可以将其关闭,如果这就是您想要的......您不需要每次发送消息时都关闭和打开 tcp 连接,您可以在你完成它之前保持打开状态,事实上打开和关闭 tcp 连接会在它们通过的握手协议中产生一些开销。
不过有一些注意事项……Tcp 连接可能会变成 "half open",尤其是无线连接,这可能会导致问题。进入这里有点复杂,但我推荐 Stephen Cleary 的这套精彩文章作为通读。阅读整个博客,因为里面有很多有用的信息。
所以,回到简单的,我会...
- 一个 serverStart() 方法,您可以在其中启动服务器侦听。
- 一个 serverAccept() 方法,您可以在其中检查是否有任何挂起的连接并在存在时接受它们。
- 连接到服务器的客户端的 clientConnect() 方法
- 进行数据传输的服务器和客户端的 Read() 和 Write() 方法。
正常情况下,流量是...
- 服务器侦听
- 客户端连接
- 服务器接受
- 客户端发送
- 服务器接收
- (然后服务器 sends/receives, 客户端 sends/receives)
- 一切都关闭并关闭
我正在尝试在本地用 c# 编写聊天 client/server 以熟悉套接字。
首先,我使用(非常简单的)以下代码启动服务器:
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 端口来自这里:
到目前为止一切似乎都有效。假设服务器现在有 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);
}
}
所以流程基本如下:
- 我启动服务器(实例化新的消息管理器,第三个片段 和 运行 serverskeleton(最后一个片段)
- 服务器打印IP 控制台,我复制 ip 并启动客户端
- 实例化客户端& 将服务器 ip 设置为我复制粘贴的
- 启动客户端框架(最后 片段)
已调用 Start() 的 TcpListener 侦听传入连接,然后将它们堆叠在队列中。一旦该队列已满,就会产生套接字异常。要从队列中删除连接,您需要使用 TcpListener 的 AcceptTcpClient or 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 上发送和接收。当您完成该客户端上的任何数据后 transmitting/receiving 您可以将其关闭,如果这就是您想要的......您不需要每次发送消息时都关闭和打开 tcp 连接,您可以在你完成它之前保持打开状态,事实上打开和关闭 tcp 连接会在它们通过的握手协议中产生一些开销。
不过有一些注意事项……Tcp 连接可能会变成 "half open",尤其是无线连接,这可能会导致问题。进入这里有点复杂,但我推荐 Stephen Cleary 的这套精彩文章作为通读。阅读整个博客,因为里面有很多有用的信息。
所以,回到简单的,我会...
- 一个 serverStart() 方法,您可以在其中启动服务器侦听。
- 一个 serverAccept() 方法,您可以在其中检查是否有任何挂起的连接并在存在时接受它们。
- 连接到服务器的客户端的 clientConnect() 方法
- 进行数据传输的服务器和客户端的 Read() 和 Write() 方法。
正常情况下,流量是...
- 服务器侦听
- 客户端连接
- 服务器接受
- 客户端发送
- 服务器接收
- (然后服务器 sends/receives, 客户端 sends/receives)
- 一切都关闭并关闭