Trying to learn Test Driven Design for the first time. I have a server that goes like this:
public class ServerEngine
{
private static Socket _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private static List<Socket> _clientSockets = new List<Socket>();
private static byte[] _buffer = new byte[1024];
public ServerEngine()
{
SetupServer();
}
private void SetupServer()
{
Console.WriteLine("Setting up the server...");
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, 100));
// Settingup the backlog
_serverSocket.Listen(1);
//Listen for connections
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
private void AcceptCallback(IAsyncResult ar)
{
// Add the accepted socket to the list of sockets
Socket socket = _serverSocket.EndAccept(ar);
_clientSockets.Add(socket);
Console.WriteLine("A client has connected");
// listen for messages coming from the new accepted socket
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
// Start accepting a new connection again
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
private void ReceiveCallback(IAsyncResult ar)
{
Socket socket = (Socket)ar.AsyncState;
try
{
var received = socket.EndReceive(ar);
var dataBuf = new byte[received];
Array.Copy(_buffer, dataBuf, received);
var text = Encoding.ASCII.GetString(dataBuf);
Console.WriteLine($"Text Received: {text}");
var response = AddToHashSet(text);
var data = Encoding.ASCII.GetBytes(response.ToString());
socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
catch (SocketException)
{
socket.Close();
Console.WriteLine("A client has disconnected.");
}
}
private void SendCallback(IAsyncResult ar)
{
Socket socket = (Socket)ar.AsyncState;
socket.EndSend(ar);
}
private HashSet<TidContainerModel> _tidContainerModels = new HashSet<TidContainerModel>(new TidContainerModelComparer());
private bool AddToHashSet(string value)
{
var tcv = new TidContainerModel
{
Tid = value,
TimeAdded = DateTime.Now,
Type = 'R',
Rssi = -444
};
return _tidContainerModels.Add(tcv);
}
}
This I call in the Program Main to setup the server. Then I created an xUnit test like so:
private Socket _clentSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private ServerEngine serverEngine = new ServerEngine();
[Fact]
public void ConnectToServerTest()
{
// Arrange
bool expected = true;
// Act
bool actual = LoopConnect();
// Assert
Assert.Equal(expected, actual);
}
private bool LoopConnect()
{
int attempts = 0;
var connected = false;
while (!_clentSocket.Connected)
{
try
{
attempts++;
var ipAddress = new IPEndPoint(IPAddress.Parse("172.16.35.71"), 100);
_clentSocket.Connect(ipAddress);
}
catch (SocketException se)
{
Console.Clear();
Console.WriteLine($"Failed connecting {se.Message}. Reconnecting attempt {attempts}");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
connected = true;
Console.Clear();
Console.WriteLine("Connected to the server");
return connected;
}
Without using the Test this program runs fine. But when I use the test, I fail with an error of:
Server.Test.ServerEngineTest.ConnectToServerTest
Source: ServerEngineTest.cs line 15
Duration: 209 ms
Message:
System.IO.IOException : The handle is invalid.
Stack Trace:
__Error.WinIOError(Int32 errorCode, String maybeFullPath)
Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
Console.Clear()
ServerEngineTest.LoopConnect() line 51
ServerEngineTest.ConnectToServerTest() line 21
If I use a console application to connect to the server, it works perfect. I want to learn how to use the xUnit with this project.
Look at your stack trace, it's telling you the problem:
Stack Trace:
__Error.WinIOError(Int32 errorCode, String maybeFullPath)
Console.GetBufferInfo(Boolean throwOnNoConsole, Boolean& succeeded)
Console.Clear()
ServerEngineTest.LoopConnect() line 51
ServerEngineTest.ConnectToServerTest() line 21
Basically, you're trying to clear the console, but you have no console allocated in your test. Which is something you should never do for two reasons:
You should be mocking your I/O to be able to test it.
You should absolutely never mix this kind of console I/O with your socket service, whatever it does. Use dependency injection to extract and contain console I/O (or even better, a proper logging interface) separate from the actual work of starting and maintaining a TCP server.
Related
I'm making a communication via a socket but I'm having problems sending data from the server.
I'm able to send a response after receiving something but I need to be able to send a message without receiving anything from the client beforehand.
What I want to do is basically send a command over the socket after a connection is established. (after AcceptCallback() was called, which I check for). In my main program I have a button which I want to be able to click anytime to send a certain message. However, every time I try this I get an error that I cannot anything over an unconnected socket.
As you can see in the code I tried to send another message after receiving something from my client (see in the ReceiveCallback() where I send "Test send"). Sending that works, but if I want to send something else afterwards it again gives the error that I can't send something over a disconnected socket.
The current code looks like this(excerpts):
class SocketServer
{
private static Socket _serverSocket;
//Konstruktor
public SocketServer(int port = 9851)
{
_serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //Socket Objekt
lep = new IPEndPoint(IPAddress.Any, 9851);
}
private static void AcceptClient()
{
try
{
_serverSocket.Bind(lep);
_serverSocket.Listen(100);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket); //auf Verbindungen warten weiter bei AcceptCallback Methode
}
catch
{
...
}
}
private static void AcceptCallback(IAsyncResult AR) //Connection accept callback
{
try
{
Socket socket = _serverSocket.EndAccept(AR);
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket);
}
catch (ObjectDisposedException e)
{
...
}
}
private static void ReceiveCallback(IAsyncResult AR)//Weiterführung Empfangen
{
Socket socket = (Socket)AR.AsyncState;
try
{
received = socket.EndReceive(AR); //Speichere Anzahl empfangener Bytes
... //trimming and saving the received data in a string
sending = "Test send";
byte[] data = Encoding.UTF8.GetBytes(sending);
socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket);
}
catch (ObjectDisposedException e)
{
...
}
}
public void SendMessage()
{
byte[] data = Encoding.UTF8.GetBytes(sending);
_serverSocket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), _serverSocket); //Beginnen des Sendevorgangs weiter bei SendCallback
...
}
private static void SendCallback(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
socket.EndSend(AR);
...
}
I've been working on this problem for over a week now. It's kind of hard to say what I tried before because I feel like I've tried everything and looked in the deepest part of the internet for help. So I'm standing before that possibilities that a)I am super blind somewhere, b) I'm overworked or c) it's actually impossible, sorry Rhino you need to scrap your project and start anew.
Anyway I'd be grateful for any kind of help. Thanks.
This is how I solved my problems:
The Problem was that I didn't use BeginSend(...) on the socket connection that I had used with EndAccept(), which was just named socket.
To be able to use this specific socket in any other method and not just the AcceptCallback(), I had to make it accessible.
I created a List to save Accepted sockets in called _clientSockets to which I added every accepted socket.
It now looks like this (only the parts that changed):
class SocketServer
{
private static Socket _serverSocket;
private static List<Sockets> _clientSockets=new List<Socket>();
private static void AcceptClient()
{
try
{
_serverSocket.Bind(lep);
_serverSocket.Listen(100);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket); //auf Verbindungen warten weiter bei AcceptCallback Methode
}
catch
{
...
}
}
private static void AcceptCallback(IAsyncResult AR) //Connection accept callback
{
try
{
Socket socket = _serverSocket.EndAccept(AR);
socket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), socket);
_serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), _serverSocket);
_clientSockets.Add(socket); //This is the important part where I add the completed socket connection to the list!
}
catch
{
...
}
}
public void SendMessage(string sdata, int socketno)
{
Socket socket = _clientSockets[socketno] //Here I give the socket from before, or whichever one I choose, to this method to proceed with
byte[] data = Encoding.UTF8.GetBytes(sdata);
socket.BeginSend(data, 0, data.Length, SocketFlags.None, new AsyncCallback(SendCallback), socket); //Beginnen des Sendevorgangs weiter bei SendCallback
...
}
private static void SendCallback(IAsyncResult AR)
{
Socket socket = (Socket)AR.AsyncState;
socket.EndSend(AR);
...
}
I need to exchange data from two machines with the highest throughput as possible. I have a server and a client.
The client needs to send several messages to the server (in the real application, 1 message every 22 ms). Instead of initializing the socket every time I need to send something, I would like to keep the connection open and to write several messages via socket that will be initialized once for all.
Here's my client in C#:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
using System.Diagnostics;
// State object for receiving data from remote device.
public class StateObject
{
// 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 StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient
{
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static Socket InitClient()
{
// Connect to a remote device.
// Establish the remote endpoint for the socket.
// The name of the
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[1];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(ipAddress.AddressFamily,
SocketType.Stream, ProtocolType.Tcp);
try
{
// Connect to the remote endpoint.
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
return client;
}
private static void SendData(Socket client) {
try {
// Send test data to the remote device.
StringBuilder a = new StringBuilder();
Send(client, "This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReleaseClient(Socket client) {
try
{
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.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));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
// All the data has arrived; put it in response.
if (state.sb.Length >= 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
Socket client = InitClient();
for (int i = 0; i < 2; i++) {
SendData(client);
}
ReleaseClient(client);
Console.ReadLine();
return 0;
}
And this is the server in Python:
import socketserver
import socket
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(87380).strip()
print("{} wrote:".format(self.client_address[1]))
l = (str(len(self.data)))
print(l)
self.request.sendall(l.encode())
HOST, PORT = socket.gethostname(), 11000
server = socketserver.TCPServer((HOST, PORT), MyTCPHandler)
server.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
server.serve_forever()
I can connect to the server, and if I'm calling this from C#:
InitClient();
SendData(client);
ReleaseClient(client);
Everything works fine. But if, like in my example, I'm trying to loop over SendData(), I can only send the first message and, when trying to send the second, the server gives me the following error:
System.Net.Sockets.SocketException (10053): An established connection was aborted by the software in your host machine
at System.Net.Sockets.Socket.BeginReceive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, AsyncCallback callback, Object state)
at AsynchronousClient.Receive(Socket client) in C:\Users\giuli\source\repos\ApplicationSending\ApplicationSending\Program.cs:line 133 (NdR the lines that corresponds to:
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);)
I cannot understand what is happening. Shouldn't it be possible to send multiple messages without having to re-initialize the socket?
This is how we establish the connection and keep sending multiple messages.
#Client.cs
private AutoResetEvent[] _autoSendReceiveEvents;
##Connection method
using (var connectArgs = new SocketAsyncEventArgs())
{
connectArgs.AcceptSocket = _socket;
connectArgs.RemoteEndPoint = _ipEndPoint;
connectArgs.Completed += OnCompleted;
var result = _socket.ConnectAsync(connectArgs);
if (result)
{
_autoConnectEvent.WaitOne();
}
var errorCode = connectArgs.SocketError;
if (errorCode != SocketError.Success)
{
CloseSocket();
throw new SocketException((int) errorCode);
}
}
##Sending Data
public void Send(string message)
{
if (!IsConnected)
{
throw new SocketException((int) SocketError.NotConnected);
}
var sendBuffer = GetBytes(message);
using (var sendReceiveArgs = new SocketAsyncEventArgs())
{
sendReceiveArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length);
sendReceiveArgs.AcceptSocket = _socket;
sendReceiveArgs.RemoteEndPoint = _ipEndPoint;
sendReceiveArgs.Completed += OnSend;
var result = _socket.SendAsync(sendReceiveArgs);
if (result)
{
_autoSendReceiveEvents[SendOperation].WaitOne();
}
}
}
##OnSend
private void OnSend(object sender, SocketAsyncEventArgs eventArgs)
{
try
{
if (eventArgs.SocketError != SocketError.Success)
{
CloseSocket();
_autoSendReceiveEvents[SendOperation].Set();
}
switch (eventArgs.LastOperation)
{
case SocketAsyncOperation.Send:
_autoSendReceiveEvents[SendOperation].Set();
break;
}
}
catch (Exception ex)
{
CloseSocket();
_autoSendReceiveEvents[SendOperation].Set();
}
}
I have attempted to create a TCP service that is able to achieve what I wish but sadly I am getting stuck at the last hurdle.
The Scenario:
A single server instance is running with 10 clients all connected, a client will send a command, and receive the response. This is all working fine. However the last scenario isn't working
When a client issues an "UPDATE" command, the server should then send a message back to all connected clients that they need to do something.
Example Comms:
1
Client A GetTime -----> Server
Client A <----- Time is... Server
2
Client A UPDATE ------> Server
Client A <------- Ack Server
Client A <------- DoUpdate Server
Client B <------- DoUpdate Server
Client C <------- DoUpdate Server
Comms 1 abover works, mainly because of the call to send and call to reeive, but for comms 2 I cannot workout how I can achieve this, at least not without opening a second port for the communication which isn't ideal.
Current attempt based on Microsoft Article
Server
class Program
{
public static int Main(String[] args)
{
AsynchronousSocketListener.StartListening();
return 0;
}
}
public class StateObject
{
// 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 StringBuilder Sb = new StringBuilder();
}
public class AsynchronousSocketListener
{
// Thread signal.
public static ManualResetEvent AllDone = new ManualResetEvent(false);
public static void StartListening()
{
// Data buffer for incoming data.
//var bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
//??IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
//??IPAddress ipAddress = ipHostInfo.AddressList[0];
//??IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 3030);
// Create a TCP/IP socket.
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try
{
listener.Bind(new IPEndPoint(IPAddress.Any, 3030));
//listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
// Set the event to nonsignaled state.
AllDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept((AcceptCallback), listener);
// Wait until a connection is made before continuing.
AllDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
AllDone.Set();
// Get the socket that handles the client request.
var listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
var state = new StateObject {WorkSocket = handler};
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReadCallback, state);
}
public static void ReadCallback(IAsyncResult ar)
{
// Retrieve the state object and the handler socket
// from the asynchronous state object.
var state = (StateObject)ar.AsyncState;
Socket handler = state.WorkSocket;
// Read data from the client socket.
int bytesRead = handler.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));
// Check for end-of-file tag. If it is not there, read
// more data.
var content = state.Sb.ToString();
if (content.IndexOf("<EOF>", StringComparison.Ordinal) > -1)
{
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
// Echo the data back to the client.
Send(handler, content);
}
else
{
// Not all data received. Get more.
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReadCallback, state);
}
}
}
private static void Send(Socket handler, String data)
{
// Convert the string data to byte data using ASCII encoding.
var byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0, SendCallback, handler);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
var handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
/*
public static int Main(String[] args)
{
StartListening();
return 0;
}
* */
}
Client Code
class Program
{
public static int Main(String[] args)
//static void Main(string[] args)
{
Console.Title = "Client ";
AsynchronousClient.StartClient();
Console.ReadLine();
return 0;
}
}
public class StateObject
{
// Client socket.
public Socket WorkSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] Buffer = new byte[BufferSize];
// Received data string.
public StringBuilder Sb = new StringBuilder();
}
public class AsynchronousClient
{
// The port number for the remote device.
private const int Port = 3030;
// ManualResetEvent instances signal completion.
private static readonly ManualResetEvent ConnectDone =
new ManualResetEvent(false);
private static readonly ManualResetEvent SendDone =
new ManualResetEvent(false);
private static readonly ManualResetEvent ReceiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String _response = String.Empty;
public static void StartClient()
{
// Connect to a remote device.
try
{
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
//IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
//??IPHostEntry ipHostInfo = Dns.Resolve("localhost");
//??IPAddress ipAddress = ipHostInfo.AddressList[0];
//??IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
//client.BeginConnect(remoteEP,
//new AsyncCallback(ConnectCallback), client);
var remoteEP = new IPEndPoint(IPAddress.Parse("127.0.0.1"), Port);
client.BeginConnect(remoteEP, ConnectCallback, client);
ConnectDone.WaitOne();
// set receive to another thread so we can constantly receive, doesn't work as intended
//var thread = new Thread(() => ReadThread(client));
//thread.Start();
// Send test data to the remote device.
Send(client, "This is a test<EOF>");
SendDone.WaitOne();
//test remove
// Receive the response from the remote device.
Receive(client);
ReceiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", _response);
// Release the socket.
//client.Shutdown(SocketShutdown.Both);
//client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
// doesn't work as expected
private static void ReadThread(object ar)
{
var client = (Socket)ar;
while (true)
{
Receive(client);
ReceiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", _response);
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
var client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint);
// Signal that the connection has been made.
ConnectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
var state = new StateObject {WorkSocket = client};
// Begin receiving the data from the remote device.
client.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
var state = (StateObject)ar.AsyncState;
Socket client = state.WorkSocket;
// Read data from the remote device.
int bytesRead = client.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));
// Get the rest of the data.
client.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReceiveCallback, state);
}
else
{
// All the data has arrived; put it in response.
if (state.Sb.Length > 1)
{
_response = state.Sb.ToString();
}
// Signal that all bytes have been received.
ReceiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
// Convert the string data to byte data using ASCII encoding.
var byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0, SendCallback, client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
var client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
SendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
/*
public static int Main(String[] args)
{
StartClient();
return 0;
}
*/
}
Previous system that worked
Server:
class Program
{
private static byte[] buffer = new byte[1024];
public static Socket _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public static List<Socket> clientSockets = new List<Socket>();
static void Main(string[] args)
{
Console.Title = "Server, " + clientSockets.Count + " clients are connected";
SetupServer();
Console.ReadLine();
}
public static void SetupServer()
{
Console.WriteLine("Setting up server...");
_serverSocket.Bind(new IPEndPoint(IPAddress.Any, 3030));
_serverSocket.Listen(10);
_serverSocket.BeginAccept(AcceptCallback, null);
Console.ReadLine();// stops cmd from closing
}
public static void AcceptCallback(IAsyncResult AR)
{
Socket socket = _serverSocket.EndAccept(AR);
if (!clientSockets.Contains(socket))
clientSockets.Add(socket);
IPEndPoint remoteIPEndPoint = socket.RemoteEndPoint as IPEndPoint;
Console.WriteLine(remoteIPEndPoint.Address);
Console.WriteLine("Client Connected");
Console.Title = "Server, " + clientSockets.Count + " clients are connected";
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, RecieveCallBack, socket);
_serverSocket.BeginAccept(AcceptCallback, null);
}
private static void RecieveCallBack(IAsyncResult AR)
{
var socket = (Socket)AR.AsyncState;
int received = socket.EndReceive(AR);
var databuff = new byte[received];
Array.Copy(buffer, databuff, received);
string s = Encoding.ASCII.GetString(databuff);
Console.WriteLine("Text Received: " + s);
string response = string.Empty;
switch (s.ToLower())
{
case "get time":
response = DateTime.Now.ToLongTimeString();
break;
case "hello":
response = "olleh";
break;
case "update clients":
response = "";
SendData("Ack", socket);
doUpdateClients();
break;
default:
response = "Invavlid Request";
break;
}
SendData(response, socket);
}
private static void SendData(string Data, Socket socket)
{
byte[] data = Encoding.ASCII.GetBytes(Data);
socket.BeginSend(data, 0, data.Length, SocketFlags.None, sendCallback, socket);
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, RecieveCallBack, socket);
}
private static void doUpdateClients()
{
// need to send an update message to all the clients
var upd = new Thread((UpdateClients));
upd.Start();
}
private static void UpdateClients()
{
Thread.Sleep(5000);
foreach (var sock in clientSockets)
{
SendData("UpdateClients", sock);
}
}
private static void sendCallback(IAsyncResult AR)
{
var socket = (Socket)AR.AsyncState;
socket.EndSend(AR);
}
//
}
}
Client:
class Program
{
private static byte[] buffer = new byte[1024];
public static Socket _clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
static void Main(string[] args)
{
Console.Title = "Client ";
LoopConnect();
//ReceiveLoopStart();
//_clientSocket.Listen(10);
SendLoop();
Console.ReadLine();
}
private static void LoopConnect()
{
while (!_clientSocket.Connected)
{
try
{
_clientSocket.Connect(IPAddress.Parse("127.0.0.1"), 3030);
}
catch (SocketException se)
{
}
}
Console.WriteLine("Connected");
}
private static void ReceiveLoopStart()
{
//_clientSocket.Bind(new IPEndPoint(IPAddress.Any, 3030));
//_clientSocket.Listen(10);
_clientSocket.BeginAccept(AcceptCallback, null);
Thread receiveThread = new Thread(ReceiveLoop);
receiveThread.Start();
}
private static void ReceiveLoop()
{
_clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, RecieveCallBack, _clientSocket);
_clientSocket.BeginAccept(AcceptCallback, null);
}
private static void RecieveCallBack(IAsyncResult AR)
{
int received = _clientSocket.EndReceive(AR);
var databuff = new byte[received];
Array.Copy(buffer, databuff, received);
string s = Encoding.ASCII.GetString(databuff);
Console.WriteLine("Text Received: " + s);
string response = string.Empty;
switch (s.ToLower())
{
case "get time":
response = DateTime.Now.ToLongTimeString();
break;
case "hello":
response = "olleh";
break;
default:
response = "Invavlid Request";
break;
}
}
public static void AcceptCallback(IAsyncResult AR)
{
Socket socket = _clientSocket.EndAccept(AR);
Console.WriteLine("Client Connected");
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, RecieveCallBack, socket);
_clientSocket.BeginAccept(AcceptCallback, null);
}
private static void SendLoop()
{
while (true)
{
Console.Write("Enter Request: ");
string req = Console.ReadLine();
var buffer = Encoding.ASCII.GetBytes(req);
_clientSocket.Send(buffer);
var tempBuff = new byte[1024];
int rec = _clientSocket.Receive(tempBuff);
var data = new byte[rec];
Array.Copy(tempBuff, data, rec);
Console.WriteLine("Received: " + Encoding.ASCII.GetString(data));
}
}
}
}
I don't have an example up my sleeve here in C# but what you need to learn is to use the select api.
You only need a single thread to do this. You use the same thread to process all sockets that are in use at any point in time.
If nobody is connected you only have the listening socket. And you then only use the select api to watch what happens on that socket. Select is a blocking call when no timeout is specified. If data is available, then that means that you can call accept. The result of accept is as you know another socket. You now use 2 sockets in select. Again select will block until one of those sockets has data. Perhaps the listening socket again, so you get another socket after calling accept. You now use 3 sockets in select. Suppose now one of the accept sockets have data available you will see that by the using the select api properly. And you can then use any of those sockets to send something over it, except of course the listening socket that is not intended for sending.
More info can be found here :
http://www.codeproject.com/Articles/20066/A-scalable-client-server-using-select-socket-funct
It uses what I explained and gives more elaborate explanations too.
I'm not getting in to your design , but it seems like you can't even reference the other clients.
why don't you save a collection of sockets like so :
private List<Socket> _handlers = new List<Socket>();
public static void AcceptCallback(IAsyncResult ar)
{
Socket handler = listener.EndAccept(ar);
var state = new StateObject {WorkSocket = handler};
handlers.Add(handler);
handler.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, ReadCallback, state);
}
And then on receive according to the message type or whatever you should notify these clients.( all of them not just the one currently passed to the ReciveCallback.
public static void ReadCallback(IAsyncResult ar)
{
if("<EOF>")
{
foreach(var h in _handlers)
{
Send(h,data);
}
}
}
I'm trying to develop a console application behaving as an asynchronous socket client in C#. You can see the code below:
public class StateObject
{
// 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 StringBuilder sb = new StringBuilder();
}
class Program
{
private static readonly string hostIp = ConfigurationManager.AppSettings["HostIp"];
private static readonly int port = Int32.Parse(ConfigurationManager.AppSettings["HostPort"]);
private static Socket client;
private static ManualResetEvent connectDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private static ManualResetEvent receiveDone = new ManualResetEvent(false);
private static Thread receiveThread;
static int Main(string[] args)
{
EventLog appLog = new EventLog();
appLog.Source = "xApp";
try
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(hostIp);
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client, "Login Message");
sendDone.WaitOne();
receiveThread = new Thread((ThreadStart)delegate
{
while (true)
{
Receive(client);
receiveDone.WaitOne();
Thread.Sleep(1);
}
});
receiveThread.Start();
}
catch (Exception ex)
{
appLog.WriteEntry(
"An exception occured: " +
" ex: " + ex.ToString() +
" stack trace: " + ex.StackTrace,
System.Diagnostics.EventLogEntryType.Error);
}
return 0;
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.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));
Console.WriteLine("Response received : {0}", state.sb.ToString());
string[] args = state.sb.ToString().Split(';');
switch (args[1])
{
case "CREATEBOOK":
ProcessInput(args);
break;
case "CONFIRMBOOK":
if (args[2] == "true")
{
ConfirmProcess();
}
break;
default:
break;
}
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
byte[] byteData = Encoding.Unicode.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
When I'm debugging I see that the code does the work as expected but the memory size used by process is increasing every moment. I think that the reason of the memory leak is the following code piece:
receiveThread = new Thread((ThreadStart)delegate
{
while (true)
{
Receive(client);
receiveDone.WaitOne();
Thread.Sleep(1);
}
});
receiveThread.Start();
But I don't have any idea about the change I have to do. Do you have any tip?
Thanks in advance,
I think the problem is in your Receive method which you are calling in a while loop. Basically you are creating a new StateObject every time you loop around.
// Create the state object.
StateObject state = new StateObject();
Try and store the state object as a class variable and reuse it. Maybe add a Reset method if you need to re-initialilze it again. This article shows a way to build a very efficient asynchronous socket which you may find useful.
I wrote a TCP Socket server and client a few months ago in my current project and they've been working flawlessly since then. But as of today I simply cannot get a connection between two machines that has been communicating without a hickup for a long time.
The setup is very straight forward: Two machines ('A' and 'B') are connected directly via an ethernet cable (no switch in between). Machine 'A' is also connected to my home network which allows for Internet access. Machine 'B' is only connected to 'A'. I am running the TCP server on 'B' and the client on 'A'.
I'm using the asynchronous .NET Socket features (BeginAccept etc on the server end and BeginConnect etc in the client end). Having debugged both ends I can only confirm that the server end never seems to get a call from the client. I haven't touched the code and it still works between other machines.
Also, there's no other comms problems between the two machines. I'm using Input Director to share one set of keyboard/mouse and normal UNC paths works fine to browse one machine from the other.
I have tested different ports and I've brought down the firewalls on both sides for good measure. I have released/renewed the connections on both sides, rebooted both machines and every other dummy option I can think of.
I confess I'm not terribly network savvy so I'd very much appreciate a few hints on what to look for next as I'm now out of ideas on what to check next. (I'm pretty sure it's gotta be something really stupid I've just overlooked. ;O)
EDIT (sample 'spike' code)
On request I adapted Microsoft's sample Socket code and compressed it a bit (below). I then tested running both server and client ends on machine 'B' which worked fine but on machine 'A' the same thing happened. The client got no contact with the server end. Here's the code:
public static class SocketServer
{
private class StateObject
{
public Socket _workSocket;
public const int BufferSize = 1024;
public readonly byte[] _buffer = new byte[BufferSize];
public readonly StringBuilder _sb = new StringBuilder();
}
private static readonly ManualResetEvent _s_allDone = new ManualResetEvent(false);
public static void StartListening()
{
var ipHostInfo = Dns.Resolve(Dns.GetHostName());
var ipAddress = ipHostInfo.AddressList[0];
var localEndPoint = new IPEndPoint(ipAddress, 11000);
var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true)
{
_s_allDone.Reset();
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(acceptCallback, listener);
_s_allDone.WaitOne();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
private static void acceptCallback(IAsyncResult ar)
{
_s_allDone.Set();
var listener = (Socket)ar.AsyncState;
var handler = listener.EndAccept(ar);
var state = new StateObject {_workSocket = handler};
handler.BeginReceive(state._buffer, 0, StateObject.BufferSize, 0, readCallback, state);
}
private static void readCallback(IAsyncResult ar)
{
var state = (StateObject)ar.AsyncState;
var handler = state._workSocket;
int bytesRead = handler.EndReceive(ar);
if (bytesRead <= 0)
return;
state._sb.Append(Encoding.ASCII.GetString(state._buffer, 0, bytesRead));
var content = state._sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content);
}
else
{
handler.BeginReceive(state._buffer, 0, StateObject.BufferSize, 0, readCallback, state);
}
}
public static int Main(String[] args)
{
StartListening();
return 0;
}
}
public static class SocketClient
{
private class StateObject
{
public Socket _workSocket;
public const int BufferSize = 256;
public readonly byte[] _receiveBuffer = new byte[BufferSize];
public readonly StringBuilder _sb = new StringBuilder();
}
private const int RemotePort = 11000;
private static readonly ManualResetEvent _s_connectDone = new ManualResetEvent(false);
private static readonly ManualResetEvent _s_sendDone = new ManualResetEvent(false);
private static readonly ManualResetEvent _s_receiveDone = new ManualResetEvent(false);
// The response from the remote device.
private static String _s_response = String.Empty;
private static void startClient()
{
try
{
_s_connectDone.WaitOne(2000); // <-- gives server time to boot when on same machine
var ipHostInfo = Dns.Resolve(Dns.GetHostName());
var ipAddress = ipHostInfo.AddressList[0];
var remoteEndPoint = new IPEndPoint(ipAddress, RemotePort);
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.BeginConnect(remoteEndPoint, connectCallback, client);
_s_connectDone.WaitOne();
send(client, "This is a test<EOF>");
_s_sendDone.WaitOne();
receive(client);
_s_receiveDone.WaitOne();
Console.WriteLine("Response received : {0}", _s_response);
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void connectCallback(IAsyncResult ar)
{
try
{
var client = (Socket)ar.AsyncState;
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}", client.RemoteEndPoint);
_s_connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void receive(Socket client)
{
try
{
var state = new StateObject {_workSocket = client};
client.BeginReceive(state._receiveBuffer, 0, StateObject.BufferSize, 0, receiveCallback, state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void receiveCallback(IAsyncResult ar)
{
try
{
var state = (StateObject)ar.AsyncState;
var client = state._workSocket;
var bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
state._sb.Append(Encoding.ASCII.GetString(state._receiveBuffer, 0, bytesRead));
client.BeginReceive(state._receiveBuffer, 0, StateObject.BufferSize, 0, receiveCallback, state);
}
else
{
if (state._sb.Length > 1)
{
_s_response = state._sb.ToString();
}
_s_receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void send(Socket client, String data)
{
var byteData = Encoding.ASCII.GetBytes(data);
client.BeginSend(byteData, 0, byteData.Length, 0, sendCallback, client);
}
private static void sendCallback(IAsyncResult ar)
{
try
{
var client = (Socket)ar.AsyncState;
var bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
_s_sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main()
{
startClient();
return 0;
}
}