【问题标题】:TCP Socket Server - A method for detecting incoming dataTCP Socket Server - 一种检测传入数据的方法
【发布时间】:2014-08-06 14:06:47
【问题描述】:

我正在尝试编写一个服务器来侦听端口,接受传入的客户端连接,然后将该客户端连接放入堆栈中以等待将来的消息到达。理想情况下,当消息到达时,我想触发一个事件,然后允许服务器执行操作。

我已经写了几乎所有的内容...我正在使用AcceptTCPClient() 来实际获取一个新的客户端连接,这很好。然后我创建一个线程,将套接字传递给一个保存客户端状态和一些其他数据的类。但是,我认为阻塞并等待该线程上未来传入连接的唯一方法是调用 NetworkStream.Read() 之类的东西,然后阻塞直到字节到达。

所以这是根本问题 - 我正在使用 Protobuff-net,它允许我反序列化网络流而不是单个字节数组。所以通过读取前几个字节,我必须重置读取,将网络流传递给 protobuff 反序列化方法,然后继续。

我真正想要的是一种在检测到某些字节之前一直阻塞的方法,但在我准备好之前不需要我实际读取字节。

任何人都知道如何实现这一目标?

更新

正如下面的 cmets 所建议的,这似乎不是 .Net 支持的,因此最简单的解决方案似乎是使用下面使用异步读/写的 Tsukasa 示例。

我已经编写它来消耗线路上的所有字节,然后将这些字节传递给 protobuff Deserialize 方法。

不是我想要的,但效果很好。感谢大家的帮助。

    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;
        }
    }

【问题讨论】:

  • 尝试使用 TcpListener 类。
  • @user1929959... 这将有助于具体如何?
  • 您应该使用缓冲的内存流并通过它从网络流中读取。
  • @user1929959,这并没有解决他的问题
  • 你能再具体点吗?仅在流上放置 protobuf-net 块有什么问题吗?如果您担心线程数,我确定 protobuf-net 支持异步 IO。

标签: c# sockets tcp


【解决方案1】:

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);
    }
}

用法

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();
}

【讨论】:

  • 这不能回答问题。他想知道如何让“HandleClientComms”线程阻塞直到接收到数据而不实际执行读取
  • 添加了 HandleClientComms
  • 它有点隐藏:“但是,我认为阻塞并等待该线程上未来传入连接的唯一方法是调用 NetworkStream.Read() 之类的东西,然后阻塞直到字节到达。 "这个问题是问题的下一段。
  • 我想我对他的要求有点困惑。 while(myNetworkStream.DataAvailable) 然后等到某些东西被触发后再处理数据?
  • @Tsukasa 目前我认为这个问题无法回答。我正在等待澄清。
【解决方案2】:

您可以轮询DataAvailable 属性,但它不可靠且效率低下。

更好的方法是为 protobuf 流添加一个长度前缀。它还允许在不读取 protobuf 消息的情况下进行读取。通过使用前缀,您可以调用异步读取,该读取将在有可用内容时立即返回(如果您使用长度标头,只需将读取配置为仅读取 4 个字节)。

如果您不想自己处理网络操作,可以使用我的 apache 许可库。这是使用 protobuf-net 的示例:http://blog.gauffin.org/2014/06/easy-and-perfomant-clientserver-communication-with-protobuf-net-griffin-framework/

【讨论】:

  • @usr: afaik 有没有解决确切问题的方法?那么提供替代解决方案有什么问题呢?
  • 我们不知道他的问题是什么。这个问题目前无法回答。任何无法回答的问题的答案都是错误的。 知道他的问题是什么吗?他想阻止,但不是在 protobuf-net 内。不知道为什么。
  • 他是这样定义的:All I really want is a method that blocks until some bytes are detected but doesn't require me to actually read the bytes until I'm ready.
  • a) 你没有提供一个现实的解决方案,b) 这可能是一个被误导的要求。如果您不了解他想要达到的目标,您就无法提供替代方案。
  • 这就是为什么我对您的第一条评论说“afaik 没有解决确切问题的方法?那么提供替代解决方案有什么问题?”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-01-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多