我正在编写一个客户端库,它通过套接字与服务器通信。
我能够连接服务器并发送第一个请求,以响应我的第一个请求,我成功收到响应。但我的第二个请求从未在服务器上收到,只有在我明确退出服务器时才会调用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;
}
}
正如所建议的那样,我已经用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);
}
}
答案 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
进行删除。我正在为第一个请求添加分隔符,因为它不需要加密。我还在第二个请求中添加了分隔符,但我首先添加了分隔符,然后加密了请求,并且还在加密分隔符。
一旦我从请求中删除了分隔符,然后对其进行加密,并将加速器请求的分隔符添加到其工作正常...