gpt4 book ai didi

c# - C#套接字BeginAccept事件停止触发

转载 作者:行者123 更新时间:2023-12-03 12:09:19 26 4
gpt4 key购买 nike

下面的代码创建套接字服务器和客户端

我启动服务器,并且如果我一个接一个地启动客户端,那么它可以正常工作

如果我立即启动三个客户端,则对于一个或多个客户端,不会触发BeginAccept事件

下面的结果是在执行以下代码之后

服务器启动

服务器正在等待连接...

客户端0.0.0.0:6352请求连接

客户端0.0.0.0:6353请求连接

客户端127.0.0.1:6351请求连接

客户端127.0.0.1:6351已连接

客户端127.0.0.1:6352已连接

客户端127.0.0.1:6353已连接

ServerOnClientConnection客户端:127.0.0.1:6351

服务器正在等待连接...

ServerOnClientConnection客户端:127.0.0.1:6353

代码如下

    using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace Test {
public class TestSockets {

#region server
Socket serverSocket;
bool serverIsAlive;
public ManualResetEvent waitForConnection = new ManualResetEvent(false);
private Encoding encod = Encoding.Unicode;

public void ServerStartInThread() {
byte[] bytes = new Byte[1024];
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 5500);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket = socket;
try {
socket.Bind(localEndPoint);
socket.Listen(100);
Thread pollThread = new Thread(delegate () {
serverIsAlive = true; // needs if reopen
SendMessage("Server Started");
while (serverIsAlive) {
try {
SendMessage("Server is waiting for a connection...");
socket.BeginAccept(new AsyncCallback(ServerOnClientConnection), socket);
waitForConnection.Reset();
waitForConnection.WaitOne();
}
catch (Exception ex) {
SendMessage("Server: " + ex.ToString());
}
}
SendMessage("Server Stopped");
socket.Close();
}) {
Name = "SocketServer"
};
pollThread.Start();
}
catch (Exception ex) {
SendMessage("Server: " + ex.ToString());
}
}

public void ServerOnClientConnection(IAsyncResult ar) {
try {
Socket listener = (Socket)ar.AsyncState;
Socket clientSocket = listener.EndAccept(ar);
SendMessage("ServerOnClientConnection Client: " + clientSocket.RemoteEndPoint.ToString());
StateObject state = new StateObject() {
socket = clientSocket
};
clientSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ServerReceiveCallback), state);
waitForConnection.Set();
}
catch (Exception ex) {
SendMessage("ServerOnClientConnection: " + ex.ToString());
}
}

public void ServerReceiveCallback(IAsyncResult ar) {
StateObject state = (StateObject)ar.AsyncState;
Socket socket = state.socket;
try {
if (socket == null) return;
if (!socket.Connected) {
return;
}
int bytesRead = socket.EndReceive(ar);
if (bytesRead > 0) {
state.sb.Append(encod.GetString(state.buffer, 0, bytesRead));
socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ServerReceiveCallback), state);
}
}
catch (Exception ex) {
SendMessage("ServerReceiveCallback: " + ex.ToString());
}
}
#endregion

#region client
private Socket client;
private bool isAlive = false;
private ManualResetEvent connectDone = new ManualResetEvent(false);

public void StartInThread() {
try {
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint remoteEP = new IPEndPoint(ipAddress, 5500);
Thread pollThread = new Thread(delegate () {
isAlive = true;
while (isAlive) {
try {
if (client != null && client.Connected) {
continue;
}
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(remoteEP, new AsyncCallback(ClientConnectCallback), client);
SendMessage(string.Format("Client {0} requests connection", client.LocalEndPoint.ToString()));
connectDone.Reset();
connectDone.WaitOne(3000, false);
if (client.Connected) {
StateObject state = new StateObject() {
socket = client
};
SendMessage(string.Format("Client {0} connected", client.LocalEndPoint.ToString()));
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ClientReceiveCallback), state);
}
}
catch (Exception ex) {
SendMessage("ClientStartInThread1: " + ex.ToString());
}
}
SendMessage("Client Disconnected");
}) {
Name = "ClientThread"
};
pollThread.Start();
}
catch (Exception ex) {
SendMessage("ClientStartInThread2: " + ex.ToString());
}
}

private void ClientConnectCallback(IAsyncResult ar) {
try {
Socket socket = (Socket)ar.AsyncState;
socket.EndConnect(ar);
connectDone.Set();
}
catch (Exception ex) {
SendMessage("ClientConnectCallback: " + ex.ToString());
}
}

private void ClientReceiveCallback(IAsyncResult ar) {
StateObject state = (StateObject)ar.AsyncState;
Socket socket = state.socket;
if (socket == null || !socket.Connected) return;
try {
int bytesRead = socket.EndReceive(ar);
if (bytesRead > 0) {
state.sb.Append(encod.GetString(state.buffer, 0, bytesRead));
socket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ClientReceiveCallback), state);
}
else {
socket.Close();
}
}
catch (Exception ex) {
SendMessage("ClientReceiveCallback: " + ex.ToString());
socket.Close();
}
}

#endregion

private void SendMessage(string v) {
System.Diagnostics.Debug.WriteLine(v);
}

public static void Start() {
TestSockets server = new TestSockets();
server.ServerStartInThread();
TestSockets c1 = new TestSockets();
c1.StartInThread();
TestSockets c2 = new TestSockets();
c2.StartInThread();
TestSockets c3 = new TestSockets();
c3.StartInThread();

}
}
public class StateObject {
public Socket socket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
}

}

最佳答案

您的代码中存在一个竞争条件,导致两次设置waitForConnection事件句柄,第二次调用Set()无效,然后主服务器线程才有机会继续经过BeginAccept()并重置该句柄。这导致它错过了第二组。

解决该问题的一种方法是切换到信号量对象。例如:

#region server
Socket serverSocket;
bool serverIsAlive;
SemaphoreSlim waitForConnection = new SemaphoreSlim(0);
private Encoding encod = Encoding.Unicode;

public void ServerStartInThread()
{
byte[] bytes = new Byte[1024];
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 5500);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket = socket;
try
{
socket.Bind(localEndPoint);
socket.Listen(100);
Thread pollThread = new Thread(delegate () {
serverIsAlive = true; // needs if reopen
SendMessage("Server Started");
while (serverIsAlive)
{
try
{
SendMessage("Server is waiting for a connection...");
socket.BeginAccept(new AsyncCallback(ServerOnClientConnection), socket);
waitForConnection.Wait();
}
catch (Exception ex)
{
SendMessage("Server: " + ex.ToString());
}
}
SendMessage("Server Stopped");
socket.Close();
})
{
Name = "SocketServer"
};
pollThread.Start();
}
catch (Exception ex)
{
SendMessage("Server: " + ex.ToString());
}
}

public void ServerOnClientConnection(IAsyncResult ar)
{
try
{
Socket listener = (Socket)ar.AsyncState;
Socket clientSocket = listener.EndAccept(ar);
SendMessage("ServerOnClientConnection Client: " + clientSocket.RemoteEndPoint.ToString());
StateObject state = new StateObject()
{
socket = clientSocket
};
clientSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ServerReceiveCallback), state);
waitForConnection.Release();
}
catch (Exception ex)
{
SendMessage("ServerOnClientConnection: " + ex.ToString());
}
}

这将允许 ServerOnClientConnection()方法增加信号量计数,以便主服务器线程可以继续循环,直到观察到所有可接受的连接为止。

但是,坦率地说,使用这些事件句柄的MSDN示例(我假设您的代码直接或间接基于此示例)被恕我直言。它们引入了实际上不需要线程同步的复杂性,从而不必要地使代码复杂化,并使代码难以正常工作。

更加惯用的方法是根本没有多余的线程,在 BeginAccept()方法中调用 ServerStartInThread()(您可能将其重命名为 ServerStart()),并在处理当前接受的客户端之后在 BeginAccept()方法中调用 ServerOnClientConnection()。 IE。就像您现在处理 BeginReceive()一样。

也就是说,恕我直言,甚至惯用的方法也已过时。最初设计API时,它工作得很好,但是.NET从那以后已经走了很长一段路,并且拥有处理此类异步操作的更好的机制。我在 this answer中发布了一个简单的聊天服务器示例,该示例显示了如何使用 async/ await以更简单,更易于阅读的方式实现这种事情。您可能需要查看有关如何将相同技术应用于自己的代码的想法。

关于c# - C#套接字BeginAccept事件停止触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45650711/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com