我读了一些C#聊天源代码&我看到:在具有大量连接客户端的聊天服务器上,服务器监听器将在一个独立的线程和运行中运行。每个连接的客户端也将在一个单独的线程中运行。 代码示例:
启动服务器&开始在分离的线程中听:
public void StartListening()
{
// Get the IP of the first network device, however this can prove unreliable on certain configurations
IPAddress ipaLocal = ipAddress;
// Create the TCP listener object using the IP of the server and the specified port
tlsClient = new TcpListener(1986);
// Start the TCP listener and listen for connections
tlsClient.Start();
// The while loop will check for true in this before checking for connections
ServRunning = true;
// Start the new tread that hosts the listener
thrListener = new Thread(KeepListening);
thrListener.Start();
}
private void KeepListening()
{
// While the server is running
while (ServRunning == true)
{
// Accept a pending connection
tcpClient = tlsClient.AcceptTcpClient();
// Create a new instance of Connection
Connection newConnection = new Connection(tcpClient);
}
}
连接也将在一个单独的线程中运行:
public Connection(TcpClient tcpCon)
{
tcpClient = tcpCon;
// The thread that accepts the client and awaits messages
thrSender = new Thread(AcceptClient);
// The thread calls the AcceptClient() method
thrSender.Start();
}
因此,如果聊天服务器具有10000个连接的客户端,则聊天服务器应用程序将具有10002个线程(一个主线程,一个服务器线程和10000个客户端线程)。我认为聊天服务器将是大量线程的开销。请帮我解决一下。感谢。
更新 我相信聊天示例仅适用于学习网络和它们不适合真实世界的模型。请给我一个真实的解决方案。感谢。
答案 0 :(得分:1)
减轻负担的标准机制称为选择,它可以复用多个Socket实例以监视准备读取或写入的实例。请参阅此文档:http://codeidol.com/csharp/csharp-network/Csharp-Network-Programming-Classes/Csharp-Socket-Programming/并向下滚动到select()部分。
答案 1 :(得分:1)
如果使用.Net framework 2.0 SP2或更高版本,则可以使用基于IO Completion ports的新的asyncrhronous套接字模型。在这种情况下,您不应该创建自己的线程,因为IO完成端口为您完成所有工作。
这里有一些例子:
tcpServer = new System.Net.Sockets.TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);
private void EndAcceptSocket(IAsyncResult asyncResult)
{
TcpListener lister = (TcpListener)asyncResult.AsyncState;
Socket sock = lister.EndAcceptSocket(asyncResult);
//handle socket connection (you may add socket to you internal storage or something)
//start accepting another sockets
lister.BeginAcceptSocket(EndAcceptSocket, lister);
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted;
e.SetBuffer(new byte[socketBufferSize], 0, socketBufferSize);
sock.ReceiveAsync(e);
}
void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
var sock = (Socket)sender;
if (!sock.Connected)
{
//handle socket disconnection
}
var buf = new byte[size];
Array.Copy(e.Buffer, buf, size);
//handle received data
//start reading new data
sock.ReceiveAsync(e);
}
答案 2 :(得分:0)
为了使事情变得更糟,你还必须在一些任意数量的线程之间进行通信(它是一个聊天服务器,人们想要互相交谈,而不是自己。)我建议调查UDP - 可以用一个服务器上的单线程并且很好地适应网络活动 - 人们很少在聊天交换中一次写几句话,这对于大小有限的UDP数据报非常方便。
当然还有其他方法,但有一点可以肯定的是,你永远无法在那个规模上为每个套接字做线程。
答案 3 :(得分:0)
1)你永远不会想要运行许多线程 - 即使你可以让它们在你的盒子上运行(你不能 - 每个线程都有一个与之相关的堆栈,它需要真正的RAM并且当你开始更多时而且你的盒子里的物理资源也会越来越多,看着它爆炸了。
2)您将需要研究线程池 - 使用较少量的线程来处理大量工作 - 通常从您尝试尽快通过的工作队列中读取。
3)你会想要查看io完成端口 - 当io(像磁盘读取或网络io)等待你采取行动时进行回调的方法 - 想想一个线程(或者一个池)线程)专门用于获取io通知,然后推动将该io带入队列的操作,然后是另一个负责实际消息传递/日志记录等的线程池。
4)当你超越一台机器时会发生什么?如果你成功了,你希望做什么? :-)通常人们会使用一组N台机器进行聊天 - 然后根据用户的标识符进行哈希(认为代表用户的GUID)或者UserID / bigint,具体取决于与某些内部身份验证令牌相对应的内容从登录到登录)允许他们确定性地将用户的状态/状态信息路由到专用于消息传递的那组N个框中的特定机器。因此,如果散列到服务器N [2]的用户需要检查这些朋友是否已登录,则很容易知道他们的每个朋友确切地知道他们的朋友的状态应该在哪台机器上,因为后端始终将这些朋友哈希到IM与每个用户标识哈希对应的计算机。 (即,您只知道来自用户ID的服务器场中的哪个服务器应该处理该用户的IM状态。
只是不要'认为你会旋转一堆线程,这将节省一天。它很草率,只能用很少的数量。
答案 4 :(得分:0)