C# 无法从C中的.Net套接字正确读取数据#
我在C#中有一个客户机和服务器类,它使用套接字通信。服务器如下所示: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
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缓冲区和读取索引吗?我举了一个异步示例,(更新)如何使用它。试着抓住它。