C#套接字编程,发送后续请求失败

时间:2018-04-11 08:11:16

标签: c# sockets

我正在编写一个客户端库,它通过套接字与服务器通信。 我能够连接服务器并发送第一个请求,以响应我的第一个请求,我成功收到响应。但我的第二个请求从未在服务器上收到,只有在我明确退出服务器时才会调用ReadCallback

以下是代码,如果有问题请告诉我,因为我确信服务器端没有任何问题。

       if (string.IsNullOrWhiteSpace(host))
                throw new ArgumentNullException(nameof(host));

            var remoteEndPoint = ResidueUtils.ResolveEndPoint(host, port);
            _socket.BeginConnect(remoteEndPoint, new AsyncCallback(ConnectCallback), _socket);
            connectDone.WaitOne();

            if (!_socket.Connected)
                throw new Exception("could not connect to the specified host.");

            var connectionRequest = RequestBuilder.BuildConnectionRequest();
            byte[] connectionRequestBytes = Encoding.ASCII.GetBytes(connectionRequest);

            _socket.BeginSend(connectionRequestBytes, 0, connectionRequestBytes.Length, 0,
                new AsyncCallback(SendCallback), _socket);
            sendDone.WaitOne();

            //reset the send event , so we could use it later again.
            sendDone.Reset();

            //read the response
            var responseObj = new ResponseObject { WorkSocket = _socket };
            _socket.BeginReceive(responseObj.Buffer, 0, ResponseObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), responseObj);
            readDone.WaitOne();               
            readDone.Reset();

            if (!string.IsNullOrWhiteSpace(response))
            {
                var privateKey = ResidueUtils.GetRSAPrivateKey();
                var decryptedResponse = ResidueUtils.RsaDecryptWithPrivate(response.TrimBraces(), privateKey);

                var residueResponse = JsonHelper.Parse(decryptedResponse);
                if (residueResponse.Status == 0)
                {
                    Residue.getInstance().Key = residueResponse.Key;

                    var ackRequest = RequestBuilder.BuildAckRequest();
                    var encryptedRequest = ResidueUtils.Encrypt(ackRequest, Residue.getInstance().Key);
                    var encryptedRequestBytes = Encoding.ASCII.GetBytes(encryptedRequest);
               // this request is never received at the server end
                    _socket.BeginSend(encryptedRequestBytes, 0, encryptedRequestBytes.Length, 0, new AsyncCallback(SendCallback), _socket);
                    sendDone.WaitOne();

                    sendDone.Reset();
                    response = "";

                    var ackResponse = new ResponseObject { WorkSocket = _socket };
                    _socket.BeginReceive(ackResponse.Buffer, 0, ResponseObject.BufferSize, 0, new AsyncCallback(ReadCallback), ackResponse);
                    readDone.WaitOne();

                    readDone.Reset();
                    var ack = response;
                }
            }

回调

     private void SendCallback(IAsyncResult ar)
     {
        try
        {
            Socket socket = (Socket)ar.AsyncState;
            int bytesSent = socket.EndSend(ar);
            sendDone.Set();

        }
        catch (Exception ex)
        {

            throw;
        }
    }

    private void ReadCallback(IAsyncResult result)
    {
        try
        {
            var state = (ResponseObject)result.AsyncState;
            Socket socket = state.WorkSocket;

            int bytesRead = socket.EndReceive(result);
            if (bytesRead > 0)
            {
                state.Response.Append(Encoding.ASCII.GetString(state.Buffer, 0, bytesRead));
                //socket.BeginReceive(state.Buffer, 0, ResponseObject.BufferSize, 0,
                //    new AsyncCallback(ReadCallback), state);

                response = state.Response.ToString();
                readDone.Set();
            }

            else
            {
                if (state.Response.Length > 1)
                {
                    response = state.Response.ToString();
                }
                readDone.Set();
            }
        }
        catch (Exception ex)
        {

            throw;
        }
    }

编辑1

正如所建议的那样,我已经用TcpClient替换了原始套接字,但仍然遇到了同样的问题。我尝试过的一件事是在第一个请求之前发送第二个请求,只是为了确定请求本身是否有问题,但是我收到了来自服务器的有效响应,这意味着什么都没有请求本身有问题。以下是我的新实现..

 using(_tcpClient = new TcpClient(host, port))
            {
                var connectionRequest = RequestBuilder.BuildConnectionRequest();
                byte[] connectionRequestBytes = Encoding.ASCII.GetBytes(connectionRequest);

                NetworkStream stream = _tcpClient.GetStream();
                stream.Write(connectionRequestBytes, 0, connectionRequestBytes.Length);

                var data = new byte[4098];
                var bytes = stream.Read(data, 0, data.Length);
                response = Encoding.ASCII.GetString(data, 0, bytes);

                var privateKey = ResidueUtils.GetRSAPrivateKey();
                var decryptedResponse = ResidueUtils.RsaDecryptWithPrivate(response.TrimBraces(), privateKey);

                var residueResponse = JsonHelper.Parse(decryptedResponse);
                if (residueResponse.Status == 0)
                {
                    Residue.getInstance().Key = residueResponse.Key;
                    //Residue.getInstance().ClientID = residueResponse.ClientID;

                    var ackRequest = RequestBuilder.BuildAckRequest();
                    var encryptedRequest = ResidueUtils.Encrypt(ackRequest, Residue.getInstance().Key);
                    var encryptedRequestBytes = Encoding.ASCII.GetBytes(encryptedRequest);
                    //var stream = _tcpClient.GetStream();
                    stream.Write(encryptedRequestBytes, 0, encryptedRequestBytes.Length);

                     data = new byte[4098];
                     // I receive nothing to read here..
                     bytes = stream.Read(data, 0, data.Length);
                    response = Encoding.ASCII.GetString(data, 0, bytes);

                }
            }

2 个答案:

答案 0 :(得分:1)

我无法使用模拟服务器重现基于TcpClient的客户端代码的问题:

   var server = new TcpListener(IPAddress.Loopback, port);            
    server.Start();
    while (true)
    {               
        var client = server.AcceptTcpClient();
        var stream = client.GetStream();
        using (var reader = new StreamReader(stream, Encoding.ASCII))
        using (var writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true })
        {
            var msg = reader.ReadLine();
            writer.WriteLine("[im an encrypted response]");
            msg = reader.ReadLine();
            writer.WriteLine("[im an encrypted response second bit]");
        }
    }

发布的代码似乎没有使用消息终止符/长度前缀。

由于协议似乎是基于文本的,因此快速解决方法是使用换行终止符:

    using (var _tcpClient = new TcpClient(host, port))
    {
        var stream = _tcpClient.GetStream();
        using (var reader = new StreamReader(stream, Encoding.ASCII))
        using (var writer = new StreamWriter(stream, Encoding.ASCII) { AutoFlush = true })
        {                    
            writer.WriteLine(RequestBuilder.BuildConnectionRequest());
            var response = reader.ReadLine();
            var privateKey = ResidueUtils.GetRSAPrivateKey();
            var decryptedResponse = ResidueUtils.RsaDecryptWithPrivate(response.TrimBraces(), privateKey);
            var residueResponse = JsonHelper.Parse(decryptedResponse);
            if (residueResponse.Status == 0)
            {                        
                Residue.getInstance().Key = residueResponse.Key;
                var ackRequest = RequestBuilder.BuildAckRequest();
                var encryptedRequest = ResidueUtils.Encrypt(ackRequest, Residue.getInstance().Key);
                writer.WriteLine(encryptedRequest);
                response = reader.ReadLine();
                Console.WriteLine("Client response [" + response + "]");
            }
        }
    }

虽然由于换行符分隔符需要更改服务器。

使用UTF-8编码可能会更好。

[编辑]

看起来像lot more information is needed to understand the actual problem

我的最终建议是首先使用基于以上内容的模拟服务器:

  • 为客户端发送的邮件添加换行符
  • 修改模拟服务器响应,使它们更真实

这有望让您证明客户端代码可以通过多个连接成功传输消息,这是问题的主题。

答案 1 :(得分:0)

感谢大家对我的问题的回复和帮助。我能够解决这个问题。

实际上问题实际上是在请求中。服务器希望每个请求都使用\r\n\r\n进行删除。我正在为第一个请求添加分隔符,因为它不需要加密。我还在第二个请求中添加了分隔符,但我首先添加了分隔符,然后加密了请求,并且还在加密分隔符。

一旦我从请求中删除了分隔符,然后对其进行加密,并将加速器请求的分隔符添加到其工作正常...