如何通过异步连接获取完整的消息,而不仅仅是其中的一部分
本文关键字:不仅仅是 一部分 消息 连接 异步 获取 何通过 | 更新日期: 2023-09-27 18:37:05
我已经实现了这个C#客户端,它通过异步连接与我在Java中创建的另一个服务器进行通信。这是客户端:
public class NetworkTransaction
{
private GenericRequest request;
private const string serverName = "192.168.1.101";
private const int serverPort = 7777;
private const int TIMEOUT_MILLISECONDS = 3000;
private Delegate method;
public NetworkTransaction(GenericRequest request, Delegate method)
{
this.request = request;
this.method = method;
}
public void SendRequest()
{
SocketAsyncEventArgs connectionOperation = new SocketAsyncEventArgs();
DnsEndPoint hostEntry = new DnsEndPoint(serverName, serverPort);
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
connectionOperation.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArg_Completed);
connectionOperation.RemoteEndPoint = hostEntry;
connectionOperation.UserToken = socket;
try
{
socket.ConnectAsync(connectionOperation);
}
catch (SocketException ex)
{
throw new SocketException((int)ex.ErrorCode);
}
}
private void SocketEventArg_Completed(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
ProcessConnectCompleted(e);
break;
case SocketAsyncOperation.Receive:
ProcessReceiveCompleted(e);
break;
case SocketAsyncOperation.Send:
ProcessSendCompleted(e);
break;
default:
throw new Exception("Invalid operation completed");
}
}
private void ProcessConnectCompleted(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
byte[] buffer = Encoding.UTF8.GetBytes(request.ToJson() + "'n'r");
e.SetBuffer(buffer, 0, buffer.Length);
Socket sock = e.UserToken as Socket;
sock.SendAsync(e);
}
}
private void ProcessSendCompleted(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
Socket sock = e.UserToken as Socket;
sock.ReceiveAsync(e);
}
}
private void ProcessReceiveCompleted(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
string dataFromServer = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
Socket sock = e.UserToken as Socket;
sock.Shutdown(SocketShutdown.Send);
sock.Close();
method.DynamicInvoke(dataFromServer);
}
}
}
如何从服务器获取长消息?我需要更改什么才能使客户端尝试读取更多数据以查看我是否真的阅读了整个消息?由于行可能存在问题:string dataFromServer = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
,它只接收消息的一部分而不是整个消息。
关于创建协议的答案对我帮助最大。我最终做的是在消息中附加一个"标题",说明它的大小。然后,客户端解析标头,并根据消息大小标头和接收的实际字节数检查是否还有更多数据要发送。如果还有更多数据需要读取,那么我会这样做:
if (messageSizeRemaining > 0)
{
sock.ReceiveAsync(e);
return;
}
我需要更改什么才能使客户端尝试读取更多数据以查看我是否真的阅读了整个消息?
你必须实现一个协议,说"这里有50个字节"或"这是所有数据,再见"。
每当收到一段数据时都会调用ProcessReceiveCompleted()
,这可以是介于 1 到 7 个字节之间的任何内容。套接字不保证将"消息"作为一个整体发送,因为该级别不存在消息。接收的数据量取决于 CPU 和网络利用率等因素。
这就是您的协议的用武之地。这必须在服务器和客户端中定义。例如,HTTP使用两个CRLF
让客户端说"这是我的请求,请立即回复"。另一方面,服务器(默认情况下)在发送所有数据时关闭连接,但也发送Content-length
标头,以便客户端可以验证它是否已收到所有数据。
因此,您必须将数据存储在ProcessReceiveCompleted()
中,并在收到整条消息后开始处理。
如果使用 Web 服务,则可以使用双工通道。客户端使用如下方式在服务器上注册数据流:
[OperationContract(IsOneWay = true)]
void RequestData();
服务器会通过向客户端发送单向操作的数据,例如:
[OperationContract(IsOneWay = true)]
void SendNewData(byte[] data, bool completed)
客户端将所有数据组合在一起,当收到完成的标志时,它将从服务器注销。
要了解有关双工通道的更多信息,请查看此处:双工服务。
我同步使用以下方法(我知道你想要异步)从HTTP获取数据
public static Stream getDataFromHttp(string strUrl)
{
Stream strmOutput = null;
try
{
HttpWebRequest request;
Uri targetUri = new Uri(strUrl);
request = (HttpWebRequest)HttpWebRequest.Create(targetUri);
request.Timeout = 5000;
request.ReadWriteTimeout = 20000;
request.Method = "Get";
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)";
if (request != null)
{
request.GetResponse();
if (request.HaveResponse)
{
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
strmOutput = response.GetResponseStream();
}
}
}
catch (WebException wex)
{
}
catch (Exception ex)
{
}
return strmOutput;
}
现在,要将流转换为文本,请使用以下命令:
Public Static String StreamToString(Stream stream)
{
StreamReader SR = new StreamReader(stream);
try
{
String strOutput = SR.ReadToEnd();
Return strOutput;
}
catch(Exception ex)
{
return ex.message
}
}
希望这有效