C# TCP套接字服务器-检测传入数据的方法
我正在尝试编写一个服务器,它将侦听端口,接受传入的客户端连接,然后将该客户端连接放入堆栈中,等待将来的消息到达。理想情况下,当消息到达时,我希望触发一个事件,然后允许服务器执行操作 我已经写了几乎所有的内容…我正在使用C# TCP套接字服务器-检测传入数据的方法,c#,sockets,tcp,C#,Sockets,Tcp,我正在尝试编写一个服务器,它将侦听端口,接受传入的客户端连接,然后将该客户端连接放入堆栈中,等待将来的消息到达。理想情况下,当消息到达时,我希望触发一个事件,然后允许服务器执行操作 我已经写了几乎所有的内容…我正在使用AcceptTCPClient()来实际建立一个新的客户端连接,这很好。然后我创建一个线程,将套接字传递给一个类,该类保存客户机状态和一些其他数据。但是,我认为在该线程上阻塞并等待将来传入的连接的唯一方法是调用NetworkStream.Read()之类的函数,然后阻塞直到字节到达
AcceptTCPClient()
来实际建立一个新的客户端连接,这很好。然后我创建一个线程,将套接字传递给一个类,该类保存客户机状态和一些其他数据。但是,我认为在该线程上阻塞并等待将来传入的连接的唯一方法是调用NetworkStream.Read()之类的函数,然后阻塞直到字节到达
所以这里是一个基本问题-我使用Protobuff net,它允许我反序列化网络流,而不是单个字节数组。因此,通过读取前两个字节,我必须重置读取,将networkstream传递给protobuff反序列化方法,然后继续
我真正想要的是一种方法,该方法在检测到某些字节之前进行阻塞,但在准备就绪之前,不需要我实际读取这些字节
有人知道我怎样才能做到这一点吗
更新
正如下面的评论所指出的,这似乎不是.Net所支持的,因此最简单的解决方案似乎是使用下面使用异步读/写的Tsukasa示例
我编写它是为了消耗线路上的所有字节,然后将这些字节传递给protobuff反序列化方法
这不是我想要的,但效果很好。谢谢大家的帮助
private byte[] buffer = new byte[256];
private Socket socket;
private NetworkStream networkStream;
private AsyncCallback callbackRead;
private AsyncCallback callbackWrite;
public Socket Socket
{
get { return socket; }
}
public ClientProxy(Socket clientSocket)
{
socket = clientSocket;
networkStream = new NetworkStream(clientSocket);
callbackRead = new AsyncCallback(OnReadComplete);
callbackWrite = new AsyncCallback(OnWriteComplete);
}
public void ReadAsync()
{
networkStream.BeginRead(buffer, 0, buffer.Length, callbackRead, null);
}
private void OnReadComplete(IAsyncResult ar)
{
int bytesRead = networkStream.EndRead(ar);
if (bytesRead > 0)
{
MemoryStream stream = new MemoryStream(buffer);
Message data;
data = Serializer.DeserializeWithLengthPrefix<Message>(stream, PrefixStyle.Fixed32);
if (data.Type == Chat.Type.User && data.Action == Chat.Action.Add)
{
Communication.RegisterClient(data.From, socket.Handle.ToString());
}
Communication.readMessage(data);
ReadAsync();
}
else
{
networkStream.Close();
socket.Close();
networkStream = null;
socket = null;
}
}
private byte[]buffer=新字节[256];
专用插座;
专用网络流;
私有异步回调回调回读;
私有异步回调回调回写;
公用插座
{
获取{返回套接字;}
}
公共客户端代理(套接字客户端套接字)
{
socket=clientSocket;
networkStream=新的networkStream(clientSocket);
callbackRead=新的异步回调(OnReadComplete);
callbackWrite=新的异步回调(OnWriteComplete);
}
public void ReadAsync()
{
networkStream.BeginRead(buffer,0,buffer.Length,callbackRead,null);
}
私有void OnReadComplete(IAsyncResult ar)
{
int bytesRead=networkStream.EndRead(ar);
如果(字节读取>0)
{
MemoryStream stream=新的MemoryStream(缓冲区);
消息数据;
data=Serializer.DeserializeWithLengthPrefix(流,PrefixStyle.Fixed32);
if(data.Type==Chat.Type.User&&data.Action==Chat.Action.Add)
{
Communication.RegisterClient(data.From、socket.Handle.ToString());
}
通信。读取消息(数据);
ReadAsync();
}
其他的
{
networkStream.Close();
socket.Close();
networkStream=null;
socket=null;
}
}
BeginRead(),它允许您在数据准备就绪时调用事件,这样您的进程可能处于阻塞状态,操作系统仅在资源准备就绪时才会唤醒它
class Client
{
private byte[] buffer = new byte[256];
private Socket socket;
private NetworkStream networkStream;
private AsyncCallback callbackRead;
private AsyncCallback callbackWrite;
public Client(Socket clientSocket)
{
socket = clientSocket;
networkStream = new NetworkStream(clientSocket);
callbackRead = new AsyncCallback(OnReadComplete);
callbackWrite = new AsyncCallback(OnWriteComplete);
}
public void StartRead()
{
networkStream.BeginRead(buffer, 0, buffer.Length, callbackRead, null);
}
private void OnReadComplete(IAsyncResult ar)
{
int bytesRead = networkStream.EndRead(ar);
if (bytesRead > 0)
{
string s = System.Text.Encoding.ASCII.GetString(buffer, 0, bytesRead);
//do something with complete data here
networkStream.BeginWrite(buffer, 0, bytesRead, callbackWrite, null);
}
else
{
networkStream.Close();
socket.Close();
networkStream = null;
socket = null;
}
}
private void OnWriteComplete(IAsyncResult ar)
{
networkStream.EndWrite(ar);
networkStream.BeginRead(buffer, 0, buffer.Length, callbackRead, null);
}
}
用法
您可以轮询
DataAvailable
属性,但它不可靠且效率低下
更好的方法是在protobuf流前面加上长度前缀。它还允许在不读取protobuf消息的情况下进行读取。通过使用前缀,您可以调用异步读取,该读取将在有可用内容时立即返回(如果您使用的是长度标头,则只需将读取配置为仅读取4个字节)
如果您不想自己处理网络操作,可以使用我的apache许可库。下面是一个使用protobuf net的示例:尝试使用TcpListener类。@user1929959。。。这将有助于如何解决问题?你应该使用一个缓冲内存流,并通过它从网络流中读取。@user1929959,这并不能解决他的问题。你能更具体一点吗?在流上使用protobuf网络块有什么问题吗?如果您关心线程数,我相信protobuf net支持异步IO。这并不能回答这个问题。他想知道如何让“HandleClientComms”线程阻塞,直到接收到数据,而不实际执行readAdded HandleClientComms有点隐藏:“然而,我唯一能想到的阻塞和等待该线程上未来传入连接的方法是调用NetworkStream.Read()之类的东西这个问题是问题的下一段。我想我被他问的问题弄糊涂了。而(myNetworkStream.DataAvailable),然后等待触发某个事件,然后处理数据?@Tsukasa目前我认为这个问题无法回答。我在等待澄清。@usr:afaik确切的问题没有解决方案吗?那么给出替代方案有什么不对呢?我们不知道他的问题是什么。这个问题目前无法回答。对于无法回答的问题,任何答案都是错误的。你知道他的问题是什么吗?他想阻止,但不是在protobuf网络内。不知道为什么。他是这样定义的:
我真正想要的是一种方法,在检测到某些字节之前一直阻塞,但在准备好之前不需要我实际读取字节。a)你没有提供一个现实的解决方案,b)这可能是一个误导性的要求。如果你不理解他想要实现什么,你就不能提供替代方案。这就是为什么我对你的第一个评论说:“好吧,确切的问题没有解决方案?那么提供替代方案有什么错?”。
bool running = true;
IPAddress localAddr = IPAddress.Parse("127.0.0.1");
TcpListener tcpListener = new TcpListener(localAddr, 3000);
tcpListener.Start();
while (running)
{
while (!tcpListener.Pending())
{
Thread.Sleep(10);
}
Socket socket = tcpListener.AcceptSocket();
Client client = new Client(socket);
client.StartRead();
}