C# 无法从C中的.Net套接字正确读取数据#

C# 无法从C中的.Net套接字正确读取数据#,c#,sockets,asyncsocket,networkstream,C#,Sockets,Asyncsocket,Networkstream,我在C#中有一个客户机和服务器类,它使用套接字通信。服务器如下所示: public class AsyncTcpServer { private Socket _server_socket; private Socket _client_socket; private byte[] _receive_buffer; private byte[] _send_buffer; private Networ

我在C#中有一个客户机和服务器类,它使用套接字通信。服务器如下所示:

    public class AsyncTcpServer
    {
        private Socket _server_socket;
        private Socket _client_socket;
        private byte[] _receive_buffer;
        private byte[] _send_buffer;
        private NetworkStream _ns;


        public void Start()
        {
            try
            {
                _server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _server_socket.Bind(new IPEndPoint(IPAddress.Any, 17999));
                _server_socket.Listen(0);
                _server_socket.BeginAccept(new AsyncCallback(BeginAccept), null);
            }
            catch(Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void BeginAccept(IAsyncResult ar)
        {
            try
            {         
                _client_socket = _server_socket.EndAccept(ar);

                _receive_buffer = new byte[_client_socket.ReceiveBufferSize];
                _send_buffer = new byte[_client_socket.ReceiveBufferSize]; 
                _ns = new NetworkStream(_client_socket);

                _client_socket.BeginReceive(_receive_buffer, 0, _receive_buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallback), null);
            }
            catch(Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void RecieveCallback(IAsyncResult ar)
        {
            try 
            {
                string text = Encoding.ASCII.GetString(_receive_buffer);
                Debug.Print("Server Received: " + text);                
            }
            catch (Exception e)
            {
                Debug.Print("Unexpected exception: " + e.Message);
            }
        }

        public void Send(byte [] bytes)
        {
            try
            {
                _ns.Write(bytes, 0, bytes.Length);
            }
            catch (Exception e)
            {
                Debug.Print("Unexpected exception: " + e.Message);
            }            
        }
    }
    public class AsyncTcpClient
    {
        private Socket _client_socket;
        private byte[] _buffer;
        private const int HEADER_SIZE = sizeof(int);

        public void Start()
        {
            _client_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);         
            _client_socket.BeginConnect(new IPEndPoint(IPAddress.Loopback, 17999), new AsyncCallback(ConnectCallback), null);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                _client_socket.EndConnect(ar);                
                _buffer = new byte[_client_socket.ReceiveBufferSize];
                StartReceive();
                byte[] buffer = Encoding.ASCII.GetBytes("Connected!");
                _client_socket.Send(buffer);
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void StartReceive(int offset = 0)
        {
            try
            {
                _client_socket.BeginReceive(_buffer, offset, _buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallback), null);
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void RecieveCallback(IAsyncResult ar)
        {
            try
            {
                int bytes_processed = 0;
                int bytes_read = _client_socket.EndReceive(ar);

                if (bytes_read > 0)
                {
                    NetworkStream ns = new NetworkStream(_client_socket);

                    while (ns.DataAvailable && (bytes_processed < bytes_read))
                    {
                        byte[] len_bytes = new byte[HEADER_SIZE];
                        ns.Read(len_bytes, 0, HEADER_SIZE);
                        int current_chunk_size = BitConverter.ToInt32(len_bytes, 0);

                        if (current_chunk_size > 0)
                        {
                            byte[] data_buff = new byte[current_chunk_size];
                            ns.Read(data_buff, 0, current_chunk_size);
                            string s = Encoding.ASCII.GetString(data_buff);
                            bytes_processed += (HEADER_SIZE + current_chunk_size);
                            Debug.WriteLine(s);
                        }
                    }                                        
                }
                StartReceive();         
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }

            StartReceive();
        }
    }
客户机如下所示:

    public class AsyncTcpServer
    {
        private Socket _server_socket;
        private Socket _client_socket;
        private byte[] _receive_buffer;
        private byte[] _send_buffer;
        private NetworkStream _ns;


        public void Start()
        {
            try
            {
                _server_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _server_socket.Bind(new IPEndPoint(IPAddress.Any, 17999));
                _server_socket.Listen(0);
                _server_socket.BeginAccept(new AsyncCallback(BeginAccept), null);
            }
            catch(Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void BeginAccept(IAsyncResult ar)
        {
            try
            {         
                _client_socket = _server_socket.EndAccept(ar);

                _receive_buffer = new byte[_client_socket.ReceiveBufferSize];
                _send_buffer = new byte[_client_socket.ReceiveBufferSize]; 
                _ns = new NetworkStream(_client_socket);

                _client_socket.BeginReceive(_receive_buffer, 0, _receive_buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallback), null);
            }
            catch(Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void RecieveCallback(IAsyncResult ar)
        {
            try 
            {
                string text = Encoding.ASCII.GetString(_receive_buffer);
                Debug.Print("Server Received: " + text);                
            }
            catch (Exception e)
            {
                Debug.Print("Unexpected exception: " + e.Message);
            }
        }

        public void Send(byte [] bytes)
        {
            try
            {
                _ns.Write(bytes, 0, bytes.Length);
            }
            catch (Exception e)
            {
                Debug.Print("Unexpected exception: " + e.Message);
            }            
        }
    }
    public class AsyncTcpClient
    {
        private Socket _client_socket;
        private byte[] _buffer;
        private const int HEADER_SIZE = sizeof(int);

        public void Start()
        {
            _client_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);         
            _client_socket.BeginConnect(new IPEndPoint(IPAddress.Loopback, 17999), new AsyncCallback(ConnectCallback), null);
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                _client_socket.EndConnect(ar);                
                _buffer = new byte[_client_socket.ReceiveBufferSize];
                StartReceive();
                byte[] buffer = Encoding.ASCII.GetBytes("Connected!");
                _client_socket.Send(buffer);
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void StartReceive(int offset = 0)
        {
            try
            {
                _client_socket.BeginReceive(_buffer, offset, _buffer.Length, SocketFlags.None, new AsyncCallback(RecieveCallback), null);
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }
        }

        private void RecieveCallback(IAsyncResult ar)
        {
            try
            {
                int bytes_processed = 0;
                int bytes_read = _client_socket.EndReceive(ar);

                if (bytes_read > 0)
                {
                    NetworkStream ns = new NetworkStream(_client_socket);

                    while (ns.DataAvailable && (bytes_processed < bytes_read))
                    {
                        byte[] len_bytes = new byte[HEADER_SIZE];
                        ns.Read(len_bytes, 0, HEADER_SIZE);
                        int current_chunk_size = BitConverter.ToInt32(len_bytes, 0);

                        if (current_chunk_size > 0)
                        {
                            byte[] data_buff = new byte[current_chunk_size];
                            ns.Read(data_buff, 0, current_chunk_size);
                            string s = Encoding.ASCII.GetString(data_buff);
                            bytes_processed += (HEADER_SIZE + current_chunk_size);
                            Debug.WriteLine(s);
                        }
                    }                                        
                }
                StartReceive();         
            }
            catch (Exception e)
            {
                Debug.Print(e.Message);
            }

            StartReceive();
        }
    }
在客户端,我解析前4个字节(sizeof(int))以确定负载长度,然后解析负载本身。这在我第一次这样做时起作用,但在那之后,
NetworkStream
DataAvailable
成员为false,我无法解析负载的其余部分

为什么
DataAvailable
false?我对用C语言做这件事很陌生-我是不是完全错了


提前谢谢

我想您忘记了ReceiveCallback中的EndReceive。(服务器代码)

这将分离不同的数据包


异步示例:


您仍然需要添加一些异常处理代码

public static class SocketReader
{
    public static void ReadFromSocket(Socket socket, int count, Action<byte[]> endRead)
    {
        // read from socket, construct a new buffer.
        DoReadFromSocket(socket, 0, count, new byte[count], endRead);
    }

    public static void ReadFromSocket(Socket socket, int count, ref byte[] buffer, Action<byte[]> endRead)
    {
        // if you do have a buffer available, you can pass that one. (this way you do not construct new buffers for receiving.
        // the ref is because if the buffer is too small, it will return the newly created buffer.

        // if the buffer is too small, create a new one.
        if (buffer.Length < count)
            buffer = new byte[count];

        DoReadFromSocket(socket, 0, count, buffer, endRead);
    }

    // This method will continues read until count bytes are read. (or socket is closed)
    private static void DoReadFromSocket(Socket socket, int bytesRead, int count, byte[] buffer, Action<byte[]> endRead)
    {
        // Start a BeginReceive.
        socket.BeginReceive(buffer, bytesRead, count - bytesRead, SocketFlags.None, (result) =>
        {
            // Get the bytes read.
            int read = socket.EndReceive(result);

            // if zero bytes received, the socket isn't available anymore.
            if (read == 0)
            {
                endRead(new byte[0]);
                return;
            }

            // increase the bytesRead, (index point for the buffer)
            bytesRead += read;

            // if all bytes are read, call the endRead with the buffer.
            if (bytesRead == count)
                endRead(buffer);
            else
                // if not all bytes received, start another BeginReceive.
                DoReadFromSocket(socket, bytesRead, count, buffer, endRead);

        }, null);
    }

}

我想你忘记了ReceiveCallback中的EndReceive。(服务器代码)

这将分离不同的数据包


异步示例:


您仍然需要添加一些异常处理代码

public static class SocketReader
{
    public static void ReadFromSocket(Socket socket, int count, Action<byte[]> endRead)
    {
        // read from socket, construct a new buffer.
        DoReadFromSocket(socket, 0, count, new byte[count], endRead);
    }

    public static void ReadFromSocket(Socket socket, int count, ref byte[] buffer, Action<byte[]> endRead)
    {
        // if you do have a buffer available, you can pass that one. (this way you do not construct new buffers for receiving.
        // the ref is because if the buffer is too small, it will return the newly created buffer.

        // if the buffer is too small, create a new one.
        if (buffer.Length < count)
            buffer = new byte[count];

        DoReadFromSocket(socket, 0, count, buffer, endRead);
    }

    // This method will continues read until count bytes are read. (or socket is closed)
    private static void DoReadFromSocket(Socket socket, int bytesRead, int count, byte[] buffer, Action<byte[]> endRead)
    {
        // Start a BeginReceive.
        socket.BeginReceive(buffer, bytesRead, count - bytesRead, SocketFlags.None, (result) =>
        {
            // Get the bytes read.
            int read = socket.EndReceive(result);

            // if zero bytes received, the socket isn't available anymore.
            if (read == 0)
            {
                endRead(new byte[0]);
                return;
            }

            // increase the bytesRead, (index point for the buffer)
            bytesRead += read;

            // if all bytes are read, call the endRead with the buffer.
            if (bytesRead == count)
                endRead(buffer);
            else
                // if not all bytes received, start another BeginReceive.
                DoReadFromSocket(socket, bytesRead, count, buffer, endRead);

        }, null);
    }

}

以下是我确定的解决方案:

服务器:

public class Listener
    {
        private TcpListener _listener;
        private TcpClient _client;

        public void Start()
        {
            _listener = new TcpListener(IPAddress.Loopback, 17999);
            _listener.Start();
            _listener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), _listener);
        }

        private void AcceptTcpClientCallback(IAsyncResult ar)
        {
            try
            {
                Debug.WriteLine("Accepted tcp client callback");
                _client = _listener.EndAcceptTcpClient(ar);               
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }            
        }

        public void Write(string data)
        {
            try
            {
                NetworkStream ns = _client.GetStream();
                byte[] buffer = Encoding.ASCII.GetBytes(data);
                ns.Write(buffer, 0, buffer.Length);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

    }
客户:

{[DATA_LENGTH_IN_BYTES][PAYLOAD_BYTES]}
public class Client
    {
        private TcpClient _client;
        private byte[] _buffer;

        public void Start()
        {
            try
            {
                _client = new TcpClient();
                _client.BeginConnect(IPAddress.Loopback, 17999, new AsyncCallback(ConnectCallback), _client);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                NetworkStream ns = _client.GetStream();
                _buffer = new byte[_client.ReceiveBufferSize];
                ns.BeginRead(_buffer, 0, _buffer.Length, new AsyncCallback(ReadCallback), null);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void ReadCallback(IAsyncResult ar)
        {
            try
            {
                NetworkStream ns = _client.GetStream();
                int read = ns.EndRead(ar);
                string data = Encoding.ASCII.GetString(_buffer, 0, read);

                var res = data.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (var r in res)
                {
                    Debug.WriteLine(r); // process messages
                }
                ns.BeginRead(_buffer, 0, _buffer.Length, ReadCallback, _client);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }            
        }
    }
其中,从服务器到客户端的消息的格式如下:

string message = "This is a message" + "\r\n";
_listener.Send(message);
我喜欢这个简单的选择。它要短得多,而且(至少对我来说)更容易管理


HTH

以下是我确定的解决方案:

服务器:

public class Listener
    {
        private TcpListener _listener;
        private TcpClient _client;

        public void Start()
        {
            _listener = new TcpListener(IPAddress.Loopback, 17999);
            _listener.Start();
            _listener.BeginAcceptTcpClient(new AsyncCallback(AcceptTcpClientCallback), _listener);
        }

        private void AcceptTcpClientCallback(IAsyncResult ar)
        {
            try
            {
                Debug.WriteLine("Accepted tcp client callback");
                _client = _listener.EndAcceptTcpClient(ar);               
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }            
        }

        public void Write(string data)
        {
            try
            {
                NetworkStream ns = _client.GetStream();
                byte[] buffer = Encoding.ASCII.GetBytes(data);
                ns.Write(buffer, 0, buffer.Length);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

    }
客户:

{[DATA_LENGTH_IN_BYTES][PAYLOAD_BYTES]}
public class Client
    {
        private TcpClient _client;
        private byte[] _buffer;

        public void Start()
        {
            try
            {
                _client = new TcpClient();
                _client.BeginConnect(IPAddress.Loopback, 17999, new AsyncCallback(ConnectCallback), _client);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                NetworkStream ns = _client.GetStream();
                _buffer = new byte[_client.ReceiveBufferSize];
                ns.BeginRead(_buffer, 0, _buffer.Length, new AsyncCallback(ReadCallback), null);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }
        }

        private void ReadCallback(IAsyncResult ar)
        {
            try
            {
                NetworkStream ns = _client.GetStream();
                int read = ns.EndRead(ar);
                string data = Encoding.ASCII.GetString(_buffer, 0, read);

                var res = data.Split(new [] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);

                foreach (var r in res)
                {
                    Debug.WriteLine(r); // process messages
                }
                ns.BeginRead(_buffer, 0, _buffer.Length, ReadCallback, _client);
            }
            catch (Exception e)
            {
                Debug.WriteLine(e.Message);
            }            
        }
    }
其中,从服务器到客户端的消息的格式如下:

string message = "This is a message" + "\r\n";
_listener.Send(message);
我喜欢这个简单的选择。它要短得多,而且(至少对我来说)更容易管理


HTH

我通常不使用数据流或网络流。请参阅下面的示例。我通常不使用DataAvailable或NetworkStreams。见我下面的例子。嗨,杰伦,谢谢你的深入回答。不幸的是,在服务器端调用end receive不起作用,但我重新考虑了一下,决定使用TcpListener和TcpClient形式的包装套接字。下面列出了我的新解决方案……嗨,杰伦,谢谢你的深入回答。不幸的是,在服务器端调用end receive不起作用,但我重新考虑了一下,决定使用TcpListener和TcpClient形式的包装套接字。下面列出了我的新解决方案…只有
NetworkStream NetworkStream=\u client.GetStream()不执行任何操作。现在能用了吗?问题是,当一条消息被分成多个数据包时,它就出错了。提示:如果不删除\r\n,可以使用Debug.Write方法。这样,部分消息的处理更容易。
NetworkStream NetworkStream=\u client.GetStream()
不是必需的,您是正确的,这是一个过视距,没有任何作用。我已经编辑了答案。我只想在客户端连接后能够向其写入数据。关于如何处理缓冲区,您认为不丢失单独数据包中数据的最佳方法是什么?将您的编辑添加到我的解决方案中,我最终阅读了两次:一次是使用
int bytes\u read=ns.EndRead(ar)和一个带有
ns.Read(len_字节,0,sizeof(Int32))。您的建议是完全不使用NetworkStreams来管理
\u缓冲区和读取索引吗?我举了一个异步示例,(更新)如何使用它。试着抓住它。只有
NetworkStream NetworkStream=\u client.GetStream()不执行任何操作。现在能用了吗?问题是,当一条消息被分成多个数据包时,它就出错了。提示:如果不删除\r\n,可以使用Debug.Write方法。这样,部分消息的处理更容易。
NetworkStream NetworkStream=\u client.GetStream()
不是必需的,您是正确的,这是一个过视距,没有任何作用。我已经编辑了答案。我只想在客户端连接后能够向其写入数据。关于如何处理缓冲区,您认为不丢失单独数据包中数据的最佳方法是什么?将您的编辑添加到我的解决方案中,我最终阅读了两次:一次是使用
int bytes\u read=ns.EndRead(ar)和一个带有
ns.Read(len_字节,0,sizeof(Int32))。您的建议是完全不使用NetworkStreams来管理
\u缓冲区和读取索引吗?我举了一个异步示例,(更新)如何使用它。试着抓住它。