I'm trying to use my Socket wrapper to connect to specified whois server to receive some information (yes I know TcpClient exists). To do so I made two classes. One - SocketClient - takes in Socket and has methods which handle all the connection stuff.(I didn't include IDisposable implementation)
public class SocketClient : ISocketClient
{
private const int HEADER_SIZE = 4;
private Socket _socket;
private bool disposedValue;
public SocketClient(Socket socket) { _socket = socket; }
public virtual Stream Connect(IPEndPoint endPoint)
{
_socket.Connect(endPoint);
return new NetworkStream(_socket);
}
public void Send(Stream networkStream, string message, Encoding encoding)
{
var bodyBytes = encoding.GetBytes(message);
var headerBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(bodyBytes.Length + HEADER_SIZE));
networkStream.Write(headerBytes, 0, HEADER_SIZE);
networkStream.Write(bodyBytes, 0, bodyBytes.Length);
}
public string Receive(Stream networkStream)
{
var header = ReadStream(networkStream, HEADER_SIZE);
var bodyLength = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(header)) -HEADER_SIZE;
var bodyBytes = ReadStream(networkStream, bodyLength); //Here exception is thrown
return Encoding.UTF8.GetString(bodyBytes);
}
static byte[] ReadStream(Stream networkStream, int bytesToRead)
{
var buffer = new byte[bytesToRead];
var bytesRead = 0;
while (bytesRead < bytesToRead)
{
var bytesReceived = networkStream.Read(buffer, bytesRead, bytesToRead - bytesRead);
if (bytesReceived == 0)
throw new SocketIsClosedException("Tried to read from closed socket.");
bytesRead += bytesReceived;
}
return buffer;
}
Second - WhoisResolver - Takes in SocketClient and uses it to send and receive whois info.
public class WhoisResolver : IWhoisResolver
{
private ISocketClient _socketClient;
private bool disposedValue;
public WhoisResolver(ISocketClient socketClient)
{
_socketClient = socketClient;
}
public string? Lookup(string fullDomainName, string whoisServerAddress, int whoisServerPort = 43)
{
try
{
var ip = Dns.GetHostAddresses(whoisServerAddress).First();
var stream = _socketClient.Connect(new IPEndPoint(ip, whoisServerPort));
_socketClient.Send(stream, $"{fullDomainName}\r\n",System.Text.Encoding.ASCII); //Here socket is still open
var response = _socketClient.Receive(stream);
_socketClient.Disconnect(false);
return response;
}catch
{
return null;
}
}
}
Here's how I use it:
var whoisServer = "whois.crsnic.net";
var domainName = "testdomain";
var tld = "net";
var socketClient = new SocketClient(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp));
var resolver = new WhoisResolver(socketClient);
var result = resolver.Lookup($"{domainName}.{tld}", whoisServer);
Everytime my program tries to read from the stream i get 2 types of exception randomly:
-Unable to read data from the transport connection : An existing connection was forcibly closed by the remote host.
-Tried to read from closed socket.
When i use TcpClient whith buffered stream and stream reader/writer everything works fine.
As stated in the comment above the problem was with the way the message length was read. I thought that the message header was the first 4 bytes but when I read it, it were over 20000000 bytes of message lenght. Seems obvious but the exceptions were pretty misleading.
Related
I have two simple classes, a NetworkListener and NetworkClient. Upon reading from a TcpClient's network stream that I pass into NetworkClient, I was given the following error:
System.IO.IOException: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine..
Stacktrace:
System.IO.IOException: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine..
---> System.Net.Sockets.SocketException (10053): An established connection was aborted by the software in your host machine.
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
--- End of inner exception stack trace ---
at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 count)
I just don't understand this because the client did not abort the connection, and it only happens on the second iteration. Is there something I'm missing here, or something weird going on with my code?
I just don't know what steps to take next in debugging this...
The line it errors on is,
var bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
Below I have put the full code of both the two classes.
NetworkListener:
public class NetworkListener : INetworkListener
{
private readonly INetworkClientRepository _clientRepository;
private readonly INetworkClientFactory _clientFactory;
private readonly TcpListener _listener;
public NetworkListener(IPEndPoint endPoint, INetworkClientRepository clientRepository, INetworkClientFactory clientFactory)
{
_clientRepository = clientRepository;
_clientFactory = clientFactory;
_listener = new TcpListener(endPoint.Address, endPoint.Port);
}
public void Start(int backlog = 100)
{
_listener.Start(backlog);
}
public async Task ListenAsync()
{
while (true)
{
await AcceptClient(await _listener.AcceptTcpClientAsync());
}
}
private async Task AcceptClient(TcpClient client)
{
try
{
var networkClient = _clientFactory.CreateClient(client);
_clientRepository.AddClient(networkClient);
await networkClient.ListenAsync();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
NetworkClient:
public class NetworkClient : INetworkClient
{
private readonly TcpClient _tcpClient;
private readonly INetworkPacketHandler _packetHandler;
private readonly NetworkStream _networkStream;
public NetworkClient(TcpClient tcpClient, INetworkPacketHandler packetHandler)
{
_tcpClient = tcpClient;
_packetHandler = packetHandler;
_networkStream = _tcpClient.GetStream();
}
public async Task ListenAsync()
{
while (true)
{
var byteArray = GetByteArrayFromNetworkStream();
using var br = new BinaryReader(new MemoryStream(byteArray));
if (br.BaseStream.Length < Constants.PacketMinLength)
{
continue;
}
var messageLength = BinaryPrimitives.ReadInt32BigEndian(br.ReadBytes(4));
var packetData = br.ReadBytes(messageLength);
using var br2 = new BinaryReader(new MemoryStream(packetData));
var packetId = BinaryPrimitives.ReadInt16BigEndian(br2.ReadBytes(2));
await _packetHandler.HandleAsync(this, new NetworkPacket(packetId, packetData));
}
}
private byte[] GetByteArrayFromNetworkStream()
{
var buffer = new byte[2048];
var memoryStream = new MemoryStream();
var bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
while (bytesRead > 0)
{
memoryStream.Write(buffer, 0, buffer.Length);
bytesRead = _networkStream.Read(buffer, 0, buffer.Length);
}
return memoryStream.ToArray();
}
public async Task WriteToStreamAsync(byte[] data)
{
await _networkStream.WriteAsync(data, 0, data.Length);
}
public void Dispose()
{
_tcpClient.Close();
}
}
The error message is misleading; in this case, it can actually mean that the other end of the connection closed the socket, not your application.
My suspicion would be that the client connecting to your server is sending some data and then closing the connection.
I'm creating a Server Socket in C# for my Unity Application. I have created Asynchronous Server Socket from below link
Asynchronous Server Socket
Using which I can successfully able to connect with client and receive data from client. But after few data received the socket is not receiving data even though the client is sending the data (I'm testing client and server in same machine).
Also there was no exception thrown in try catch either. So I'm not able to identify the root cause.
Please find my code below
public class NetworkStateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 1024;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public List<byte> receivedBytes = new List<byte>();
}
private readonly ManualResetEvent allDone = new ManualResetEvent(false);
private readonly ManualResetEvent receiveDone = new ManualResetEvent(false);
private readonly string receiveDelimitter = "<EOF>";
socketThread = new Thread(new ThreadStart(StartSocketServer));
socketThread.Priority = System.Threading.ThreadPriority.BelowNormal;
socketThread.IsBackground = true;
socketThread.Start();
protected void Receive(Socket socket)
{
ReceiveData(socket);
receiveDone.WaitOne();
}
private void ReceiveData(Socket socket)
{
try
{
// Create the state object.
NetworkStateObject state = new NetworkStateObject();
state.workSocket = socket;
// Begin receiving the data from the remote device.
socket.BeginReceive(state.buffer, 0, NetworkStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
NetworkStateObject state = (NetworkStateObject)ar.AsyncState;
Socket socket = state.workSocket;
// Read data from the remote device.
int bytesRead = socket.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
//state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
var bytesArray = state.buffer;
if (bytesRead < NetworkStateObject.BufferSize)
{
Array.Resize(ref bytesArray, bytesRead);
}
var bytesList = new List<byte>(bytesArray);
state.receivedBytes.AddRange(bytesList);
var receivedBytes = state.receivedBytes;
var bytesCount = receivedBytes.Count;
if (receivedBytes.Count > 1)
{
var receivedString = Encoding.ASCII.GetString(receivedBytes.ToArray(), 0, bytesCount);
if (receivedString.IndexOf(receiveDelimitter, StringComparison.CurrentCulture) > -1)
{
var message = receivedString.Replace(receiveDelimitter, String.Empty);
message = Regex.Unescape(message);
socketBaseDelegate.ReceivedMessage(message);
state.receivedBytes.Clear();
receiveDone.Set();
}
else
{
// Get the rest of the data.
socket.BeginReceive(state.buffer, 0, NetworkStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
}
}
else
{
receiveDone.Set();
}
}
catch (Exception e)
{
Debug.Log(e.ToString());
}
}
public void ReceivedMessage(string data)
{
socketInputParser.Parse(data);
asynchronousSocketListener.ReceiveMessage();
}
In the above code the ReceiveCallback is not triggered after some time.
Even though client sends data.
else
{
// Get the rest of the data.
socket.BeginReceive(state.buffer, 0, NetworkStateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
This part shouldn't be conditional. You should always begin receiving again once the receive callback is complete. Unless of course the connection is terminated.
At the fist try, I created a basic TCP server as flowing:
public class Tcp
{
private TcpListener listener { get; set; }
private bool accept { get; set; } = false;
public void StartServer(string ip, int port)
{
IPAddress address = IPAddress.Parse(ip);
listener = new TcpListener(address, port);
listener.Start();
accept = true;
StartListener();
Console.WriteLine($"Server started. Listening to TCP clients at {ip}:{port}");
}
public async void StartListener() //non blocking listener
{
listener.Start();
while (true)
{
try
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
HandleClient(client);
}
finally { }
}
}
private void HandleClient(TcpClient client)
{
try
{
NetworkStream networkStream = client.GetStream();
byte[] bytesFrom = new byte[20];
networkStream.Read(bytesFrom, 0, 20);
string dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
string serverResponse = "Received!";
Byte[] sendBytes = Encoding.ASCII.GetBytes(serverResponse);
networkStream.Write(sendBytes, 0, sendBytes.Length);
networkStream.Flush();
}
catch(Exception ex)
{
}
}
}
I wrote a client test code that is sending and recording number of requests per second
public class Program
{
private volatile static Dictionary<int, int> connections = new Dictionary<int, int>();
private volatile static int fail = 0;
private static string message = "";
public static void Main(string[] args)
{
ServicePointManager.DefaultConnectionLimit = 1000000;
ServicePointManager.Expect100Continue = false;
for (int i = 0; i < 512; i++)
{
message += "T";
}
int taskCount = 10;
int requestsCount = 1000;
var taskList = new List<Task>();
int seconds = 0;
Console.WriteLine($"start : {DateTime.Now.ToString("mm:ss")} ");
for (int i = 0; i < taskCount; i++)
{
taskList.Add(Task.Factory.StartNew(() =>
{
for (int j = 0; j < requestsCount; j++)
{
Send();
}
}));
}
Console.WriteLine($"threads stablished : {DateTime.Now.ToString("mm: ss")}");
while (taskList.Any(t => !t.IsCompleted)) { Thread.Sleep(5000); }
Console.WriteLine($"Compelete : {DateTime.Now.ToString("mm: ss")}");
int total = 0;
foreach (KeyValuePair<int, int> keyValuePair in connections)
{
Console.WriteLine($"{keyValuePair.Key}:{keyValuePair.Value}");
total += keyValuePair.Value;
seconds++;
}
Console.WriteLine($"succeded:{total}\tfail:{fail}\tseconds:{seconds}");
Console.WriteLine($"End : {DateTime.Now.ToString("mm: ss")}");
Console.ReadKey();
}
private static void Send()
{
try
{
TcpClient tcpclnt = new TcpClient();
tcpclnt.ConnectAsync("192.168.1.21", 5678).Wait();
String str = message;
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
stm.Write(ba, 0, ba.Length);
byte[] bb = new byte[100];
int k = stm.Read(bb, 0, 100);
tcpclnt.Close();
lock (connections)
{
int key = int.Parse(DateTime.Now.ToString("hhmmss"));
if (!connections.ContainsKey(key))
{
connections.Add(key, 0);
}
connections[key] = connections[key] + 1;
}
}
catch (Exception e)
{
lock (connections)
{
fail += 1;
}
}
}
}
when I test it on a local machine, I get the maximum number of 4000 requests per second and when I upload it to local Lan it decreases to 200 requests per second.
The question is:
how can I improve the server performance?
what is the correct way of load testing socket servers?
You may have a "non-blocking listener", but when any particular client connects, it devotes itself to just that client until that client has sent a message and a response has been sent back to it. That's not going to scale well.
I'm usually not a fan of async void, but it's in keeping with your current code:
public async void StartListener() //non blocking listener
{
listener.Start();
while (true)
{
TcpClient client = await listener.AcceptTcpClientAsync().ConfigureAwait(false);
HandleClient(client);
}
}
private async void HandleClient(TcpClient client)
{
NetworkStream networkStream = client.GetStream();
byte[] bytesFrom = new byte[20];
int totalRead = 0;
while(totalRead<20)
{
totalRead += await networkStream.ReadAsync(bytesFrom, totalRead, 20-totalRead).ConfigureAwait(false);
}
string dataFromClient = System.Text.Encoding.ASCII.GetString(bytesFrom);
string serverResponse = "Received!";
Byte[] sendBytes = Encoding.ASCII.GetBytes(serverResponse);
await networkStream.WriteAsync(sendBytes, 0, sendBytes.Length).ConfigureAwait(false);
networkStream.Flush(); /* Not sure necessary */
}
I've also fixed the bug I mentioned in the comments about ignoring the return value from Read and removed the "hide errors from me making bugs impossible to spot in the wild" error handling.
If you're not guaranteed that your clients will always send a 20 byte message to this code, then you need to do something else so that the server knows how much data to read. This is usually done by either prefixing the message with its length or using some form of sentinel value to indicate the end. Note that even with length-prefixing, you're not guaranteed to read the whole length in one go and so you'd need to also use a read loop, as above, to discover the length first.
If switching everything to async isn't giving you the scale you need, then you need to abandon using NetworkStream and start working at the Socket level, and specifically with the async methods designed to work with SocketAsyncEventArgs:
The SocketAsyncEventArgs class is part of a set of enhancements to the System.Net.Sockets.Socket class that provide an alternative asynchronous pattern that can be used by specialized high-performance socket applications... An application can use the enhanced asynchronous pattern exclusively or only in targeted hot areas (for example, when receiving large amounts of data).
The main feature of these enhancements is the avoidance of the repeated allocation and synchronization of objects during high-volume asynchronous socket I/O...
In the new System.Net.Sockets.Socket class enhancements, asynchronous socket operations are described by reusable SocketAsyncEventArgs objects allocated and maintained by the application...
My goal is to create a TcpClient that stays connected, allowing messages to be sent to the server, as well as having a timed monitor that sends a special message to the server during a certain idle frequency. Haven't made it too far before getting stumped.
With the current code, the first message sent using a console tester app receives fine, but upon sending another an exception is thrown with the message "The operation is not allowed on non-connected sockets."
I have tried removing the stream.Dispose() line and it will hang on stream.Read(...) during the second attempt. I have also tried making NetworkStream stream a member of the class and set it to client.GetStream() in the constructor and it will hang on stream.Read(...) during the second attempt.
public class TcpClientTest
{
private TcpClient client = new TcpClient();
private string hostName;
private int port;
public TcpClientTest(string hostName, int port)
{
this.hostName = hostName;
this.port = port;
client.Connect(hostName, port);
}
public byte[] SendMessage(string name, string message)
{
if (client == null) throw new Exception("Client connection has not been established");
Person person = new Person();
person.Name = name; person.Message = message;
byte[] messageBytes = (System.Text.Encoding.Unicode.GetBytes(Newtonsoft.Json.JsonConvert.SerializeObject(person)));
const int bytesize = 1024 * 1024;
try
{
NetworkStream stream = client.GetStream();
if (stream != null)
{
stream.Write(messageBytes, 0, messageBytes.Length); // Write the bytes
messageBytes = new byte[bytesize]; // Clear the message
// Receive the stream of bytes
stream.Read(messageBytes, 0, messageBytes.Length);
}
// Clean up
stream.Flush();
stream.Dispose();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
return messageBytes; // Return response
}
}
// In console tester app
static void Main(string[] args)
{
TcpClientTest client = new TcpClientTest("127.0.0.1", 1234);
string exit = "2";
do
{
if (exit == "1") client.SendMessage("TEST", "TEST");
Console.WriteLine("1 Send Message 2 Exit");
exit = Console.ReadLine();
} while (exit != "2");
}
I am porting an AS3 client to C# and am having huge problems with completing the login procedure to the server.
I have no access to the server, just the protocol, and have no idea if the server is expecting any particular behavior with regards the sockets.
I have managed to connect to the server and go through the login process. I have then been able to send a message requesting data which results in a whole series of messages being sent to my client.
In the AS3 version of the client I receive messages in three distinct buffers.
In my C# I only get the first 2 buffers followed by a short period of time and then the connection is reset.
The protocol is binary. First 2 bytes tells me the length of the message, 2nd 2 bytes message type. Rest is data.
On the first read I get a 91 byte policy file which I discard. After that I receive data that I am able to process and the first 20 odd messages are fine. The 3rd buffer though never arrives.
Any ideas? Is it my implementation of AsyncSocket at fault or is there some flag I should be using on my socket?
Any pointers would be much appreciated.
public abstract class AsyncSocket
{
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 4096;
public byte[] buffer = new byte[BufferSize];
public byte[] messageBuffer = new byte[0];
}
public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e);
public delegate void ConnectedHandler(object sender, EventArgs e);
public event MessageReceivedHandler MessageReceived;
public event ConnectedHandler Connected;
private IPAddress[] addresses;
private int port;
private WaitHandle addressesSet;
private Socket socket;
private int failedConnectionCount;
private StateObject state;
public AsyncSocket(IPAddress address, int port) : this(new[] { address }, port) { }
public AsyncSocket(IPAddress[] addresses, int port) : this(port)
{
this.addresses = addresses;
}
public AsyncSocket(string hostNameOrAddress, int port) : this(port)
{
addressesSet = new AutoResetEvent(false);
Dns.BeginGetHostAddresses(hostNameOrAddress, GetHostAddressesCallback, null);
}
private void GetHostAddressesCallback(IAsyncResult result)
{
addresses = Dns.EndGetHostAddresses(result);
((AutoResetEvent)addressesSet).Set();
}
private AsyncSocket(int port)
{
this.port = port;
this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
this.Encoding = Encoding.Default;
this.state = new StateObject();
state.workSocket = socket;
}
public Encoding Encoding { get; set; }
public Account Account { get; set; }
public void Connect()
{
if (addressesSet != null)
addressesSet.WaitOne();
Interlocked.Exchange(ref failedConnectionCount, 0);
socket.BeginConnect(addresses, port, ConnectCallback, socket);
}
private void ConnectCallback(IAsyncResult result)
{
try
{
Socket client = (Socket)result.AsyncState;
client.EndConnect(result);
if (Connected != null)
{
Connected(this, new EventArgs());
}
Receive(client);
}
catch
{
Interlocked.Increment(ref failedConnectionCount);
if (failedConnectionCount >= addresses.Length)
{
return;
}
}
}
public void Send(string data)
{
byte[] bytes = Encoding.GetBytes(data);
Send(bytes);
}
public void Send(MsgHead msg)
{
byte[] bytes = msg.write();
Send(bytes);
}
public void Send(byte[] bytes)
{
int messageLength = BitConverter.ToUInt16(bytes, 0);
int messageType = BitConverter.ToUInt16(bytes, 2);
Console.Out.WriteLine("Sending:len:{0} msg:{1}", messageLength, messageType);
socket.BeginSend(bytes, 0, bytes.Length, 0, new AsyncCallback(WriteCallback), socket);
}
private void WriteCallback(IAsyncResult result)
{
Socket client = (Socket)result.AsyncState;
int bytesSent = client.EndSend(result);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
}
private void Receive(Socket client)
{
try
{
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Account.Window.Exit(string.Format("Error on receive: {0}",e.Message));
}
}
private void ReceiveCallback(IAsyncResult result)
{
StateObject state = (StateObject)result.AsyncState;
Socket client = state.workSocket;
SocketError errorCode;
int bytesRead = client.EndReceive(result, out errorCode);
if (errorCode != SocketError.Success)
{
Account.Window.Exit(string.Format("Disconnected, {0}", errorCode.ToString()));
return;
}
if (bytesRead == 0)
{
Account.Window.Exit("Disconnected, zero bytes");
return;
}
state.messageBuffer = state.messageBuffer.Concat(state.buffer.Take(bytesRead).ToArray()).ToArray();
int messageLength = BitConverter.ToUInt16(state.messageBuffer, 0);
if (messageLength > 4096)
{
state.messageBuffer = state.messageBuffer.Skip(91).ToArray();
messageLength = state.messageBuffer.Length == 0 ? 0 : BitConverter.ToUInt16(state.messageBuffer, 0);
}
while (messageLength > 0 && state.messageBuffer.Length >= messageLength)
{
int messageType = BitConverter.ToUInt16(state.messageBuffer, 2);
Console.Out.WriteLine("Received:len:{0} msg:{1}", messageLength, messageType);
if (MessageReceived != null)
{
MessageReceived(this, new MessageReceivedEventArgs(state.messageBuffer.Take(messageLength).ToArray()));
}
state.messageBuffer = state.messageBuffer.Skip(messageLength).ToArray();
messageLength = state.messageBuffer.Length == 0 ? 0 : BitConverter.ToUInt16(state.messageBuffer, 0);
}
Receive(client);
}
}
Sometimes TCP Framing can be tricky. You never know how many receive calls you might get for the three buffers you are expecting. Here are a few things to check:
One problem that could occur in the ReceiveCallback() method, if you only receive 1 byte, your attempt to decode messageLength into a 2-byte Int16 will fail.
Be sure the MessageBuffer methods are all working properly.
Are the 2 bytes for MessageLength included in the value of the MessageLength? If not, be sure to Skip those 2 bytes before decoding the message bytes.
Safer to discard the full number of Message bytes just in case it's not always 91 bytes long.
I would dispatch all "full" message buffers into another method, and discard or handle there, to keep the ReceiveCallback() method nice and concise.