如何通过异步连接获取完整的消息而不仅仅是其中的一部分

时间:2012-06-13 12:30:21

标签: c# sockets asynchronous client-server socketasynceventargs

我已经实现了这个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);只接收部分消息而不是整个消息。 非常感谢你的帮助!

4 个答案:

答案 0 :(得分:3)

给我的关于创建协议的答案对我帮助最大。我最终做的是在消息中添加一个“标题”来说明它的大小。客户端然后解析标头,并根据消息大小标头和接收的实际字节数检查是否还有更多数据要发送。如果还有更多的数据需要阅读,那么我会这样做:

if (messageSizeRemaining > 0)
{
      sock.ReceiveAsync(e);
      return;
}

答案 1 :(得分:1)

  

我需要更改什么才能让客户端尝试读取更多数据以查看我是否真正阅读了整个消息?

你必须实现一个协议,说“这里来50个字节”“这就是所有数据,再见”

每当收到一个数据时,都会调用

ProcessReceiveCompleted(),这可以是一个字节和很多字节之间的任何数据。套接字不保证整个发送“消息”,因为消息不存在于该级别。收到的数据量取决于CPU和网络利用率等因素。

这就是你的协议所在。这必须在服务器和客户端中定义。例如,HTTP使用两个CRLF来让客户说<​​em>“这是我的请求,请立即回复”。另一方面,服务器(默认情况下)在发送所有数据时关闭连接,但也发送Content-length标头,以便客户端可以验证它是否已收到所有数据。

因此,您必须将数据存储在ProcessReceiveCompleted()中,并在收到完整邮件后开始处理。

答案 2 :(得分:0)

如果您使用网络服务,则可以使用双工频道。 客户端使用以下内容在服务器上注册数据流的地方:

[OperationContract(IsOneWay = true)]
void RequestData();

服务器会将调用OneWay操作的数据发送回客户端,如:

[OperationContract(IsOneWay = true)]
void SendNewData(byte[] data, bool completed)

客户端将所有数据组合在一起,当收到完成的标志时,它将从服务器取消注册。

要了解有关DuplexChannels的更多信息,请查看此处:Duplex Services

答案 3 :(得分:0)

我已经同步使用以下方法(我知道你想要异步)从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
             }
   }

希望这有效