Application crash when receiving data from Socket.Receive TCP C# - c#

i have an chat room application that has two parts server and client.
server part can receive data from multi clients. So far so good.
But when one of the client leaves, the software gets an error!
this is server source code:
I commented on the line where the software gets an error !!!
Socket _server;
private void StartServer()
{
_server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_server.Bind(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 100010));
_server.Listen(1);
while (true) {
Socket client = _server.Accept();
Thread rd = new Thread(ReceiveData);
rd.Start(client);
}
}
public void ReceiveData(object skt)
{
Socket socket = (Socket)skt;
while (true) {
byte[] buffer = new byte[1024];
int r = socket.Receive(buffer);// when a client leave here get an error !!!
if (r > 0)
Console.WriteLine(socket.RemoteEndPoint.Address + ": " + Encoding.Unicode.GetString(b));
}
}
Error:
An unhandled exception of type 'System.Net.Sockets.SocketException' occurred in System.dll
Additional information: An existing connection was forcibly closed by the remote host
How can i fix it ?

You should just handle the exception:
try
{
// code that uses the socket
}
catch (SocketException e) when (e.SocketErrorCode is SocketError.ConnectionAborted)
{
// code to handle the situation gracefully
}
Or if you are using an older compiler:
try
{
// code that uses the socket
}
catch (SocketException e)
{
if(e.SocketErrorCode != SocketError.ConnectionAborted)
{
// rethrow the exception
throw;
}
// code to handle the situation gracefully
}

Related

C# SocketException (0x80004005) on every two attempt

My program creates a new TCP Socket, sends a request to a server and reads the response. If the response is requested, the program sends an acknowledge and if not it sends a negative acknowledge. This send and receive part work as intended.
The problem is that when i call the method a second time it throws SocketException (0x80004005). The third attempt works just as intended but every two attempt to send request to socket will fail.
public void Send(byte[] request)
{
var buffer = new byte[1024];
var received = 0;
try
{
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
{
socket.Connect(Address, Port);
socket.ReceiveTimeout = 5000;
socket.Send(request);
while ((received = socket.Receive(buffer)) > 0)
{
var response = buffer.Take(received);
if (IsRequested(response))
{
socket.Send(ACK);
var text = Encoding.UTF8.GetString(response);
Console.WriteLine(text);
return;
}
}
socket.Send(NAK);
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
I have tried disconnecting, closing, disposing and every possible combination of the three without success.
I discovered that if I throw an exception right after sending the acknowledge, the method will work every time!
if (IsRequested(response))
{
socket.Send(ACK);
var text = Encoding.UTF8.GetString(response);
Console.WriteLine(text);
throw new Exception("Deliberate exception");
}
My question:
Why do I get SocketException (0x80004005) every two attempt if I don't throw an exception?

C# Always retry tcp connection if connection failed

below is my code to start tcp connection for my clients to server:
Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
ConnectCallback:
private void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
}
catch (Exception ex)
{
_logger.Info(ex.ToString());
}
}
But my code only do connection once while my system starts. How do we do retry connect if the first time attempts fails?
And always will do retry if connection always fails?
And maybe do retry every 30 seconds?
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), client);
If you want to keep track of failed attempts and want to keep the good ol' async pattern, I'd pass a state object:
class ConnectionState {
Socket Client {get; set;}
int FailedAttempts {get; set;} = 0;
}
Then pass that:
client.BeginConnect(remoteEP, new AsyncCallback(ConnectCallback), new ConnectionState(){ .Client = client, FailedAttempts = 0});
In the Callback:
private void ConnectCallback(IAsyncResult ar)
{
ConnectionState state = (ConnectionState)ar.AsyncState;
try
{
state.Client.EndConnect(ar);
}
catch (SocketException ex)
{
_logger.Info(ex.ToString());
if( state.FailedAttempts < MAX_ATTEMPTS )
{
state.FailedAttempts += 1;
state.Client.BeginConnect( remoteEP, new AsyncCallback(ConnectCallback), state );
// you may also check the exception for what happened exactly.
// There may be conditions where retrying does not make sense.
// See SocketException.ErrorCode
}
else
{
// You may want to handle exceeding max tries.
// - Notify User
// - Maybe throw a custom exception
}
}
}
Reference for SocketException ErrorCodes : https://learn.microsoft.com/en-us/windows/desktop/winsock/windows-sockets-error-codes-2
For setting up a time-based retry mechanism, I'd create some kind of "connection watchdog": Have a timer check client field every X seconds. If it is null and a connection-startup-attempt is not running already, start one.
Personally, I'd try and switch to TPL, though. But I consider that as an alternative and not a direct answer to your question. But I recommend it.

In a project how to set up a "client" console application and a "server" console application

Alright so this might be worded wrong or using the wrong terminology. I want to know how I would set up a console application in my local machine that would be the "server" where it would run all my background tasks/events that happen on the client windows? I would only have one console application for the "server" and up to four "client" console applications.
Each of these would do separate things. The "server" application would just do all the calculations and functions and the client would just show in nice format what I want them to show from the results from the server.
Also I wouldn't know how to set it up in a C# type of project.
For a complete tutorial, I would recommend reading this Code Project article.
For some example code, the following client / server console applications are pulled from the MSDN.
Synchronous Client Socket Example:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SynchronousSocketClient {
public static void StartClient() {
// Data buffer for incoming data.
byte[] bytes = new byte[1024];
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName())
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,11000);
// Create a TCP/IP socket.
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
sender.Connect(remoteEP);
Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
// Encode the data string into a byte array.
byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
// Send the data through the socket.
int bytesSent = sender.Send(msg);
// Receive the response from the remote device.
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes,0,bytesRec));
// Release the socket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
} catch (ArgumentNullException ane) {
Console.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
Console.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
} catch (Exception e) {
Console.WriteLine( e.ToString());
}
}
public static int Main(String[] args) {
StartClient();
return 0;
}
}
Synchronous Server Socket Example:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SynchronousSocketListener {
// Incoming data from the client.
public static string data = null;
public static void StartListening() {
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// Dns.GetHostName returns the name of the
// host running the application.
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Bind the socket to the local endpoint and
// listen for incoming connections.
try {
listener.Bind(localEndPoint);
listener.Listen(10);
// Start listening for connections.
while (true) {
Console.WriteLine("Waiting for a connection...");
// Program is suspended while waiting for an incoming connection.
Socket handler = listener.Accept();
data = null;
// An incoming connection needs to be processed.
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes,0,bytesRec);
if (data.IndexOf("<EOF>") > -1) {
break;
}
}
// Show the data on the console.
Console.WriteLine( "Text received : {0}", data);
// Echo the data back to the client.
byte[] msg = Encoding.ASCII.GetBytes(data);
handler.Send(msg);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
} catch (Exception e) {
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static int Main(String[] args) {
StartListening();
return 0;
}
}
To set this up, you would need to create a new solution in Visual Studio, and then add two console application projects to that solution, one for the client and one for the server. Once both projects are completed, you can copy and install the code provided above. Build the solution to generate a .exe file for both client and server. Locate your .exe files, run the server first, and then run the client second. You should see some output on both the server and client console windows.
EDIT: Please bear in mind that this will get you as far as running a server and client locally. When you distribute your server / client code to other machines, you will have to contend with firewalls, port forwarding, and potentially proxies depending on your network.

TCPClient read-write - how to keep in sync

I have a basic IRC client which sends commands to the server. In the spec it says that the PASS command can have 2 numeric replies ERR_NEEDMOREPARAMS ERR_ALREADYREGISTRED
When I send the command if the password is correct there will be no reply, but if it is incorrect I will get one of the two. But because my sending and recieving are independant, and async (using await-async) I have no reliable way at the moment of catching the error and stopping my send routine sending NICK and USER or any other commands.
So my question is, what is a good way to tie up the read and write so I can stop when something goes wrong instantly, and generally keep tight control of the communication at any moment.
There you go, one simple Synchronous Client Socket Example:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class SynchronousSocketClient {
public static void StartClient() {
// Data buffer for incoming data.
byte[] bytes = new byte[1024];
// Connect to a remote device.
try {
// Establish the remote endpoint for the socket.
// This example uses port 11000 on the local computer.
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName())
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,11000);
// Create a TCP/IP socket.
Socket sender = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp );
// Connect the socket to the remote endpoint. Catch any errors.
try {
sender.Connect(remoteEP);
Console.WriteLine("Socket connected to {0}",
sender.RemoteEndPoint.ToString());
// Encode the data string into a byte array.
byte[] msg = Encoding.ASCII.GetBytes("This is a test<EOF>");
// Send the data through the socket.
int bytesSent = sender.Send(msg);
// Receive the response from the remote device.
int bytesRec = sender.Receive(bytes);
Console.WriteLine("Echoed test = {0}",
Encoding.ASCII.GetString(bytes,0,bytesRec));
// Release the socket.
sender.Shutdown(SocketShutdown.Both);
sender.Close();
} catch (ArgumentNullException ane) {
Console.WriteLine("ArgumentNullException : {0}",ane.ToString());
} catch (SocketException se) {
Console.WriteLine("SocketException : {0}",se.ToString());
} catch (Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}
} catch (Exception e) {
Console.WriteLine( e.ToString());
}
}
public static int Main(String[] args) {
StartClient();
return 0;
}
}
There is no need to avoid sending NICK or USER until the PASS has been accepted or rejected - and as you point out, you can't actually know whether it's been accepted or not anyway.
You should simply send the NICK, USER and PASS commands with no delay, and then wait to see if one or any of them are rejected (in which case you'll get an error numeric) or all of them are accepted (in which case you'll get a RPL_WELCOME numeric). There is no fixed order in which the registration commands need to be sent, so it doesn't matter if you have to re-send the PASS after you've already sent the NICK and USER, for example.

Cannot reconnect with a socket after certain type of disconnect, restart works

I am connecting to a remote server in C# via a socket and sending data across, upon disconnect I try to re-establish the connection by creating a new socket and reinitialising it.
This works for me when I test by pulling out the ethernet cable and reconnecting it a few mins later, but occasionally (every few hours maybe) I get the one of two exceptions while connected and cannot reconnect...
System.Net.Sockets.SocketException: An established connection was aborted by the software in your host machine
.......
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host
If I restart the application everything works fine once again, so im curious as to why creating a new socket doesnt work. Am I missing an initialisation somewhere perhaps? Any ideas? I use the same method for connection each time:
public static bool OpenConnection(string siteName)
{
bool success;
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(serverIp), Convert.ToInt16(serverRemotePort));
try
{
client = null;
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Console.WriteLine("Try to connect to the server ..." + ip.Address.ToString());
client.Connect(ip);
Console.WriteLine("Connection established");
//Send the nameSite first
string nameSite = EncryptString(siteName, pwd, initVector) + "*";
byte[] dataSite = new byte[100];
dataSite = Encoding.ASCII.GetBytes(nameSite);
client.Send(dataSite, dataSite.Length, SocketFlags.None);
Console.WriteLine("NameSite send");
success = true;
}
catch (SocketException e)
{
success = false;
Console.WriteLine("Unable to connect to the server : " + e.StackTrace);
}
return success;
}
I try to reconnect as follows in the catch, count is incrementing with each iteration of a while loop.
if (count % 20 == 0)
{
try
{
if (OpenConnection(siteName))
connected = true;
EventLog.WriteEntry("Connection re-established.");
}
catch (SocketException socketEx)
{
Console.WriteLine("Reconnection failed. Storing data locally. \n\n " + socketEx);
EventLog.WriteEntry("Reconnection failed. Storing data locally. \n\n " + socketEx);
}
}
The constructor simply initialises the IP and Port No. Why is it that certain types of disconnect prevent me from reconnecting without a restart, any ideas?
Do you ever call Dispose() on client to clean it up?
Try adding the call to Dispose() before you null out the client in your OpenConnection method
client.Dispose();
client = null;
After looking at the documentation for Socket on the MSDN, the Disconnect method seems like it also might solve your problem. However, I'm not sure where in your code it should go since the question shows a portion of the logic.

Categories

Resources