I have the following client listener that pass the client to HandleStationClients. The constructor of HandleStationClients starts a Task with the connection in other thread for listening.
The code below runs on the main thread with async function. When a client connects the awaiting part below will continue and passes the client to new created HandleStationClients and hook the events.
Normally after wiring the events the loop will start over and wait for a new connection at the await.
The problem is that this code loop twice for each connection. So client connects and HandleStationClients will created and events will be hooked and while loop starts again and then continue to run the same process creating again a new HandleStationClients and event hooks.
After the client is processed the awaiter is not waiting but continuing for a second time.The events are fired twice. I dont know whats wrong. Any one have a clue?
while (true)
{
counter += 1;
// Wait for new connection then do rest
stationsClientSocket = await stationsServerSocket.AcceptTcpClientAsync();
stationClients.Add(stationsClientSocket, 0);
Debug.WriteLine("Client toegevoegd " + counter);
HandleStationClients stationClient = new HandleStationClients(stationsClientSocket);
stationClient.ConnectionEstabilished += stationClient_ConnectionEstabilished;
stationClient.ConnectionClosed += stationClient_ConnectionClosed;
stationClient.NewDataReceived += stationClient_NewDataReceived;
}
The HandleClient looks like
class HandleStationClients
{
public HandleStationClients(TcpClient client)
{
Task.Factory.StartNew(() => { ProcessConnection(client); });
}
#region Event definitions
public delegate void NewDataReceivedEventHandler(string newData);
public event NewDataReceivedEventHandler NewDataReceived;
public delegate void ConnectionClosedEventHandler();
public event ConnectionClosedEventHandler ConnectionClosed;
public delegate void ConnectionEstabilishedEventHandler(IPEndPoint endpoint);
public event ConnectionEstabilishedEventHandler ConnectionEstabilished;
#endregion
public async void ProcessConnection(TcpClient stationsClientSocket)
{
byte[] message = new byte[1024];
int bytesRead;
NetworkStream networkStream = stationsClientSocket.GetStream();
if (this.ConnectionEstabilished != null)
{
this.ConnectionEstabilished((IPEndPoint)stationsClientSocket.Client.RemoteEndPoint);
}
while ((true))
{
bytesRead = 0;
try
{
bytesRead = await networkStream.ReadAsync(message, 0, 1024);
}
catch (Exception ex)
{
// some error hapens here catch it
Debug.WriteLine(ex.Message);
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
ASCIIEncoding encoder = new ASCIIEncoding();
if (this.NewDataReceived != null)
{
byte[] buffer = null;
string incomingMessage = encoder.GetString(message, 0, bytesRead);
this.NewDataReceived(incomingMessage);
}
}
stationsClientSocket.Close();
// Fire the disconnect Event
this.ConnectionClosed();
}
}
It's a bad idea to start the task in the constructor. This means your task is running while you are registering the event handlers. There is a decent chance that sometime you won't get an event registered before it needs to be fired.
What you should do is wait to start the task until the event handlers are all registered. You will need to create a Start method to take care of starting the task, and have the code call it once the events are registered.
The updated class:
class HandleStationClients
{
// Added a field to store the value until the Start method
TcpClient _client;
public HandleStationClients(TcpClient client)
{
this._client = client;
// Moved the line from here...
}
public void Start()
{
// ...to here.
Task.Factory.StartNew(() => { ProcessConnection(_client); });
}
#region Event definitions
// ...
#endregion
public async void ProcessConnection(TcpClient stationsClientSocket)
{
byte[] message = new byte[1024];
int bytesRead;
NetworkStream networkStream = stationsClientSocket.GetStream();
if (this.ConnectionEstabilished != null)
{
this.ConnectionEstabilished((IPEndPoint)stationsClientSocket.Client.RemoteEndPoint);
}
while ((true))
{
bytesRead = 0;
try
{
bytesRead = await networkStream.ReadAsync(message, 0, 1024);
}
catch (Exception ex)
{
// some error hapens here catch it
Debug.WriteLine(ex.Message);
break;
}
if (bytesRead == 0)
{
//the client has disconnected from the server
break;
}
ASCIIEncoding encoder = new ASCIIEncoding();
if (this.NewDataReceived != null)
{
byte[] buffer = null;
string incomingMessage = encoder.GetString(message, 0, bytesRead);
this.NewDataReceived(incomingMessage);
}
}
stationsClientSocket.Close();
// Fire the disconnect Event
// I added a line to check that ConnectionClosed isn't null
if (this.ConnectionClosed != null)
{
this.ConnectionClosed();
}
}
}
Then you need to change the calling code as follows.
while (true)
{
counter += 1;
// Wait for new connection then do rest
stationsClientSocket = await stationsServerSocket.AcceptTcpClientAsync();
stationClients.Add(stationsClientSocket, 0);
Debug.WriteLine("Client toegevoegd " + counter);
HandleStationClients stationClient = new HandleStationClients(stationsClientSocket);
stationClient.ConnectionEstabilished += stationClient_ConnectionEstabilished;
stationClient.ConnectionClosed += stationClient_ConnectionClosed;
stationClient.NewDataReceived += stationClient_NewDataReceived;
// Call Start manually
stationClient.Start();
}
I moved the Task start away from the constructor to a Start method. problem is solved.
Related
I'm changing some old sync/threaded code to async,
basically im implementing a 'server emulator' for an old flash game,
and trying to make the packet reading async to hopefully speed things up a little.
private List<byte> currentPacket = new List<byte>();
private byte[] workBuffer = new byte[1028];
private bool dcLock = false;
public GameClient(Socket clientSocket)
{
ClientSocket = clientSocket;
RemoteIp = clientSocket.RemoteEndPoint.ToString();
if (RemoteIp.Contains(":"))
RemoteIp = RemoteIp.Substring(0, RemoteIp.IndexOf(":"));
Logger.DebugPrint("Client connected # " + RemoteIp);
kickTimer = new Timer(new TimerCallback(kickTimerTick), null, kickInterval, kickInterval);
warnTimer = new Timer(new TimerCallback(warnTimerTick), null, warnInterval, warnInterval);
minuteTimer = new Timer(new TimerCallback(minuteTimerTick), null, oneMinute, oneMinute);
connectedClients.Add(this);
SocketAsyncEventArgs e = new SocketAsyncEventArgs();
e.Completed += receivePackets;
e.SetBuffer(workBuffer, 0, workBuffer.Length);
ClientSocket.ReceiveAsync(e);
}
public static void CreateClient(object sender, SocketAsyncEventArgs e)
{
Socket eSocket = e.AcceptSocket;
e.AcceptSocket = null;
socket.AcceptAsync(e);
GameClient client = new GameClient(eSocket);
}
private void receivePackets(object sender, SocketAsyncEventArgs e)
{
// HI1 Packets are terminates by 0x00 so we have to read until we receive that terminator
if (e.SocketError == SocketError.Success && !isDisconnecting)
{
int availble = e.BytesTransferred;
if (availble >= 1)
{
for (int i = 0; i < availble; i++)
{
currentPacket.Add(e.Buffer[i]);
if (e.Buffer[i] == PacketBuilder.PACKET_TERMINATOR)
{
parsePackets(currentPacket.ToArray());
currentPacket.Clear();
}
}
}
Array.Clear(e.Buffer);
ClientSocket.ReceiveAsync(e);
return;
}
else
{
Disconnect();
}
while (dcLock) { }; // Refuse to shut down until dcLock is cleared. (prevents TOCTOU issues.)
// Stop Timers
if (inactivityTimer != null)
inactivityTimer.Dispose();
if (warnTimer != null)
warnTimer.Dispose();
if (kickTimer != null)
kickTimer.Dispose();
// Call OnDisconnect
connectedClients.Remove(this);
GameServer.OnDisconnect(this);
LoggedIn = false;
// Close Socket
ClientSocket.Close();
ClientSocket.Dispose();
return;
}
now you see
e.Completed += receivePackets;
e.SetBuffer(workBuffer, 0, workBuffer.Length);
ClientSocket.ReceiveAsync(e);
for some reason receivePackets is never being called,
if i open up NetCat and connect to 127.0.0.1:12321 and send stuff there it works,
but from in the original flash-based client of the game? nothing happens.
no call to receivePackets ever happens, even if the buffer is only 1 byte.
even after disconnecting the client...
but it worked in my sync implementation where i just called clientSocket.Receive() in a loop.. so why doesnt it work now in async verison?
I'm trying to implement wrapper class which will simply connect to TCP server and wait for data. Once data submitted from server - I will receive this data and pass it onto subscribers of my class.
All this works. Now I want to add external functionality to "reset" this class on a timer (force reconnect every so often) to keep connection alive. My idea is that Init method can be called as many times as needed to get socket reset. However, I do get various exceptions with this.
Class code:
namespace Ditat.GateControl.Service.InputListener
{
using System;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Text;
public class BaseTCPSocketListener : IInputListener
{
#region Events/Properties
public event EventHandler<Exception> OnError;
public event EventHandler<string> OnDataReceived;
private string host;
private int port;
private int delayToClearBufferSeconds = 5;
private TcpClient client;
private readonly byte[] buffer = new byte[1024];
/// <summary>
/// Will accumulate data as it's received
/// </summary>
private string DataBuffer { get; set; }
/// <summary>
/// Store time of last data receipt. Need this in order to purge data after delay
/// </summary>
private DateTime LastDataReceivedOn { get; set; }
#endregion
public BaseTCPSocketListener()
{
// Preset all entries
this.LastDataReceivedOn = DateTime.UtcNow;
this.DataBuffer = string.Empty;
}
public void Init(string config)
{
// Parse info
var bits = config.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
this.host = bits[0];
var hostBytes = this.host.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var hostIp = new IPAddress(new[] { byte.Parse(hostBytes[0]), byte.Parse(hostBytes[1]), byte.Parse(hostBytes[2]), byte.Parse(hostBytes[3]) });
this.port = int.Parse(bits[1]);
this.delayToClearBufferSeconds = int.Parse(bits[2]);
// Close open client
if (this.client?.Client != null)
{
this.client.Client.Disconnect(true);
this.client = null;
}
// Connect to client
this.client = new TcpClient();
if (!this.client.ConnectAsync(hostIp, this.port).Wait(2500))
throw new Exception($"Failed to connect to {this.host}:{this.port} in allotted time");
this.EstablishReceiver();
}
protected void DataReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get the number of bytes read.
var bytesCount = 0;
try
{
bytesCount = this.client.Client.EndReceive(result);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
this.RaiseOnErrorToClient(ex);
}
// No data received, establish receiver and return
if (bytesCount == 0)
{
this.EstablishReceiver();
return;
}
// Convert the data we have to a string.
this.DataBuffer += Encoding.UTF8.GetString(this.buffer, 0, bytesCount);
// Record last time data received
this.LastDataReceivedOn = DateTime.UtcNow;
this.RaiseOnDataReceivedToClient(this.DataBuffer);
this.DataBuffer = string.Empty;
this.EstablishReceiver();
}
private void EstablishReceiver()
{
try
{
// Set up again to get the next chunk of data.
this.client.Client.BeginReceive(this.buffer, 0, this.buffer.Length, SocketFlags.None, this.DataReceived, this.buffer);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
this.RaiseOnErrorToClient(ex);
}
}
private void RaiseOnErrorToClient(Exception ex)
{
if (this.OnError == null) return;
foreach (Delegate d in this.OnError.GetInvocationList())
{
var syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(this, ex);
}
else
{
syncer.BeginInvoke(d, new object[] { this, ex });
}
}
}
private void RaiseOnDataReceivedToClient(string data)
{
if (this.OnDataReceived == null) return;
foreach (Delegate d in this.OnDataReceived.GetInvocationList())
{
var syncer = d.Target as ISynchronizeInvoke;
if (syncer == null)
{
d.DynamicInvoke(this, data);
}
else
{
syncer.BeginInvoke(d, new object[] { this, data });
}
}
}
}
}
Client code (under button click on form)
private void ListenBaseButton_Click(object sender, EventArgs e)
{
if (this.bsl == null)
{
this.bsl = new BaseTCPSocketListener();
this.bsl.OnDataReceived += delegate (object o, string s)
{
this.DataTextBox.Text += $"Base: {DateTime.Now} - {s}" + Environment.NewLine;
};
this.bsl.OnError += delegate (object o, Exception x)
{
this.DataTextBox.Text += $"Base TCP receiver error: {DateTime.Now} - {x.Message}" + Environment.NewLine;
};
}
try
{
this.bsl.Init("192.168.33.70|10001|10");
this.DataTextBox.Text += "BEGIN RECEIVING BSL data --------------------------" + Environment.NewLine;
}
catch (Exception exception)
{
this.DataTextBox.Text += $"ERROR CONNECTING TO BSL ------------{exception.Message}" + Environment.NewLine;
}
}
Exceptions I get. First exception when button clicked 2nd time in from handler in DataReceived
The IAsyncResult object was not returned from the corresponding
asynchronous method on this class.
On following clicks I get exception from handler in EstablishReceiver
A request to send or receive data was disallowed because the socket is
not connected and (when sending on a datagram socket using a sendto
call) no address was supplied
How do I properly ensure socket closed and re-opened?
The IAsyncResult object was not returned from the corresponding
asynchronous method on this class.
This is a well known problem that happens when data callback (DataReceived()) is called for previous socket. In this case you will call Socket.EndReceive() with incorrect instance of IAsyncResult which throws above exception.
Asynchronous Client Socket Example contains possible workaround for this problem: store socket on which BeginReceive() was called in state object which is then passed to DataReceived callback:
StateObject class
public class StateObject
{
public Socket Socket { get; set; }
public byte[] Buffer { get; } = new byte[1024];
public StateObject(Socket socket)
{
Socket = socket;
}
}
EstablishReceiver() method:
private void EstablishReceiver()
{
try
{
var state = new StateObject(client.Client);
// Set up again to get the next chunk of data.
this.client.Client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, this.DataReceived, state);
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.EstablishReceiver)));
this.RaiseOnErrorToClient(ex);
}
}
DataReceived() method:
protected void DataReceived(IAsyncResult result)
{
var state = (StateObject) result.AsyncState;
// End the data receiving that the socket has done and get the number of bytes read.
var bytesCount = 0;
try
{
SocketError errorCode;
bytesCount = state.Socket.EndReceive(result, out errorCode);
if (errorCode != SocketError.Success)
{
bytesCount = 0;
}
}
catch (Exception ex)
{
this.RaiseOnErrorToClient(new Exception(nameof(this.DataReceived)));
this.RaiseOnErrorToClient(ex);
}
if (bytesCount > 0)
{
// Convert the data we have to a string.
this.DataBuffer += Encoding.UTF8.GetString(state.Buffer, 0, bytesCount);
// Record last time data received
this.LastDataReceivedOn = DateTime.UtcNow;
this.RaiseOnDataReceivedToClient(this.DataBuffer);
this.DataBuffer = string.Empty;
this.EstablishReceiver();
}
}
A request to send or receive data was disallowed because the socket is
not connected and (when sending on a datagram socket using a sendto
call) no address was supplied
Above DataReceived() method also contains the fix for the second exception. Exception is caused by calling BeginReceive() (from EstablishReceiver()) on disconnected socket. You should not call BeginReceive() on a socket if previous read brought 0 bytes.
First of all, you're closing the socket being held by the TcpClient, but not disposing the client itself. Try the following:
// Close open client
this.client?.Close(); // Disposes and releases resources
this.client = null;
The issue is that DataReceived will be called when you close the client. You simply need to identify to the method that it should not do anything because you have deliberately ended the process. You could just add a bool:
private bool ignoreCallback;
public void Init(string config)
{
// Parse info
var bits = config.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
this.host = bits[0];
var hostBytes = this.host.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
var hostIp = new IPAddress(new[] { byte.Parse(hostBytes[0]), byte.Parse(hostBytes[1]), byte.Parse(hostBytes[2]), byte.Parse(hostBytes[3]) });
this.port = int.Parse(bits[1]);
this.delayToClearBufferSeconds = int.Parse(bits[2]);
// Close open client
if (this.client?.Client != null)
{
ignoreCallback = true;
this.client.Client.Disconnect(true);
this.client = null;
}
// Connect to client
this.client = new TcpClient();
if (!this.client.ConnectAsync(hostIp, this.port).Wait(2500))
throw new Exception($"Failed to connect to {this.host}:{this.port} in allotted time");
this.EstablishReceiver();
}
protected void DataReceived(IAsyncResult result)
{
if (ignoreCallback)
{
ignoreCallback = false;
return;
}
...
I am using sockets in c# in order to send data from server to client machine. In fact I have created a capturer which capature kinect stream from 3 different machine. I want to generate a server and client communication in order to send signal from server to the rest devices in order to begin and stop the recording process. I want with left click to send a message to begin recording and with right click to stop recording. My code is the following:
public Form1()
{
InitializeComponent();
this.MouseClick += mouseClick1;
Thread thread = new Thread(() => StartServer(message));
thread.Start(); // server is begining
}
private void mouseClick1(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
message = "start";
try
{
obj = new Capturer(dirPath + name + "_" + surname, 20);
}
catch (Exception e1)
{
Console.WriteLine("The process failed: {0}", e1.ToString());
}
if (clientSockets.Count > 0)
{
Thread.Sleep(10);
foreach (Socket socket1 in clientSockets)
socket1.Send(Encoding.ASCII.GetBytes(message)); //send everything to all clients as bytes
}
else
{
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
}
}
else if (e.Button == MouseButtons.Right)
{
message = "stop";
obj.flag2 = true;
if (clientSockets.Count > 0)
{
Thread.Sleep(10);
foreach (Socket socket1 in clientSockets)
socket1.Send(Encoding.ASCII.GetBytes(message)); //send everything to all clients as bytes
}
else
{
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
}
}
}
public void StartServer(String message) {
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
serverSocket.Listen(4); //the maximum pending client, define as you wish
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);
string result = "";
do
{
result ="asdf";
} while (result.ToLower().Trim() != "exit");
}
private const int BUFFER_SIZE = 4096;
private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
private static List<Socket> clientSockets = new List<Socket>(); //may be needed by you
private static void acceptCallback(IAsyncResult result)
{ //if the buffer is old, then there might already be something there...
Socket socket = null;
try
{
socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
//Do something as you see it needs on client acceptance
clientSockets.Add(socket);
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
string msg = "start";
//socket.Send(Encoding.ASCII.GetBytes(msg));
if (clientSockets.Count > 0)
{
Thread.Sleep(10);
foreach (Socket socket1 in clientSockets)
socket1.Send(Encoding.ASCII.GetBytes(msg)); //send everything to all clients as bytes
}
else
{
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
}
}
catch (Exception e)
{ // this exception will happen when "this" is be disposed...
//Do something here
Console.WriteLine(e.ToString());
}
}
const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
private static void receiveCallback(IAsyncResult result)
{
Socket socket = null;
try
{
socket = (Socket)result.AsyncState; //this is to get the sender
if (socket.Connected)
{ //simple checking
int received = socket.EndReceive(result);
if (received > 0)
{
byte[] data = new byte[received]; //the data is in the byte[] format, not string!
Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to http://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
//DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else
//Message retrieval part
//Suppose you only want to declare that you receive data from a client to that client
string msg = "I receive your message on: " + DateTime.Now;
socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
Console.WriteLine("I sent this message to the client: " + msg);
receiveAttempt = 0; //reset receive attempt
//socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
}
else if (receiveAttempt < MAX_RECEIVE_ATTEMPT)
{ //fail but not exceeding max attempt, repeats
++receiveAttempt; //increase receive attempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
}
else
{ //completely fails!
Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
receiveAttempt = 0; //reset this for the next connection
}
}
}
catch (Exception e)
{ // this exception will happen when "this" is be disposed...
Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
}
}
How can I parse the flag into the server, in order to send that value in the clients? Is it possible to change here the static type of server functions? As it is now the code begin the server which send just the string "start" to the clients. How can I send the string message of a bool flag? My issue lies in the static type of my callback functions. Is it possible to add as an argument the message for example in the AsyncCallBack:
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);
Firstly, about the MouseClick event. Since you are having exclusive qualification for the event (that is, one left click and another right click), you could combine them both into a single event
public Form1()
{
InitializeComponent();
this.MouseClick += mouseClick1; //one event is enough
Thread thread = new Thread(() => StartServer(message));
thread.Start(); // server is begining
}
private void mouseClick1(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
try
{
obj = new Capturer(dirPath + name + "_" + surname, 20); //captures the kinect streams
}
catch (Exception e1)
{
Console.WriteLine("The process failed: {0}", e1.ToString());
}
}
else if (e.Button == MouseButtons.Right)
{
obj.flag2 = true; // flag that handle the recording, true value stops the recording, possible I want that value to be send to the client in order the same thing happen.
}
}
And it is going to be ok.
Next, answering your questions:
Q: How can I parse the flag into the server, in order to send that value
in the clients?
A: in your mouseClick1 event, simply use sync send which you do in your accept callback, change the msg with something else (say byte[] val = new byte[] { 1 };)
foreach (Socket socket1 in clientSockets) //Do this in your `if (e.Button == Mouse.Left and Mouse.Right) blocks
socket1.Send(Encoding.ASCII.GetBytes(msg));
Q: Is it possible to change here the static type of server functions?
A: Yes, definitely! it is windows form, you do not need to use static
type at all! Unlike the Console App in your previous question. I would
even suggest you to make nothing static whenever possible
Q: As it is now the code begin the server which send just the string
"start" to the clients. How can I send the string message of a bool
flag?
A: since you use socket, you cannot really send bool flag so to
speak. You will send byte[]. But you can always check in your
client implementation. If the byte[] is of certain value, simply
change it to bool. For instance, consider of sending just 1 or 0
from your server. Then in your client endReceiveCallback, you could
simply check the data coming and see if it is 1 then it is true,
and if it is 0 then it is false
Q: My issue lies in the static type of my callback functions. Is it
possible to add as an argument the message for example in the
AsyncCallBack
A: this is winform, you could get rid of all static var! And yes,
just pass it as replacement of the null in your Begin callback.
Then you pass the object too.
//Change null with something else
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), myobj);
Then in your acceptCallback, take your object by using the IAsyncResult.AsyncState
I am making a chat service for a game,
I am using a TCP listener an client for the account information, some sort of login service. I'm wondering if i can keep the socked the client connected to the server with, to check if he is still online, and keep sending him messages if he has new messages.
I already tried making a list of sockets for the login queue, but it disconnected the previous socket to to server as soon as i accepted a new socket.
byte[] usernameByte = new byte[100];
int usernameRecieved = s.Receive(usernameByte);
//guiController.setText(System.DateTime.Now + " Recieved Login...");
byte[] passByte = new byte[100];
int passRecieved = s.Receive(passByte);
//guiController.setText(System.DateTime.Now + " Recieved Password...");
string username = "";
string password = "";
for (int i = 0; i < usernameRecieved; i++)
username += (Convert.ToChar(usernameByte[i]));
for (int i = 0; i < passRecieved; i++)
password += (Convert.ToChar(passByte[i]));
if (DomainController.getInstance().checkAccount(username, password))
{
ASCIIEncoding asen = new ASCIIEncoding();
s.Send(asen.GetBytes("true"));
s.Send(asen.GetBytes("U are succesfully logged in, press enter to continue"));
guiController.setText(serverName,System.DateTime.Now+"");
guiController.setText(serverName, "Sent Acknowledgement - Logged in");
}
else
{
ASCIIEncoding asen = new ASCIIEncoding();
s.Send(asen.GetBytes("false"));
s.Send(asen.GetBytes("U are NOT logged in, press enter to continue"));
guiController.setText(serverName, System.DateTime.Now + "");
guiController.setText(serverName, "\nSent Acknowledgement - Not logged in");
}
This is the code i currently use to check the account information the user send me. Right after i send this the user dropd the connection and i move on to the next one.
I have tried making 1 list of seperate sockets and processing them one by one, but that failed because the previous socket's connection dropped, even tho it were 2 different machines that tried to connect.
Does anyone have a sollution / a way to save sockets, that I can use to make the program keep all the connections alive? so i can send a message from user 1 to user 2, and just use the socket they connected with? or do i need to add an id every time they make a connection?
EDIT
The client Code: (this is just a test client)
while (true)
{
TcpClient tcpclnt = new TcpClient();
Console.WriteLine("Connecting.....");
tcpclnt.Connect("xx.xxx.xxx.xx", 26862);
// use the ipaddress as in the server program
while(!(checkResponse(tcpclnt.GetStream())))
{
Thread.Sleep(1000);
}
Console.WriteLine("Connected");
Console.Write("Enter the string to be transmitted : ");
String str = Console.ReadLine();
if (str == "")
{
str = " ";
}
Stream stm = tcpclnt.GetStream();
ASCIIEncoding asen = new ASCIIEncoding();
byte[] ba = asen.GetBytes(str);
Console.WriteLine("Transmitting.....");
stm.Write(ba, 0, ba.Length);
Console.Write("Enter the string to be transmitted : ");
String str2 = Console.ReadLine();
if (str2 == "")
{
str2 = " ";
}
Stream stm2 = tcpclnt.GetStream();
ASCIIEncoding asen2 = new ASCIIEncoding();
byte[] ba2 = asen2.GetBytes(str2);
Console.WriteLine("Transmitting.....");
stm.Write(ba2, 0, ba2.Length);
if (str == "false")
{
blijvenWerken = false;
}
byte[] bb = new byte[100];
int k = stm.Read(bb, 0, 100);
for (int i = 0; i < k; i++)
Console.Write(Convert.ToChar(bb[i]));
byte[] bb2 = new byte[100];
int k2 = stm.Read(bb2, 0, 100);
Console.Write("\n");
for (int i = 0; i < k2; i++)
Console.Write(Convert.ToChar(bb2[i]));
Console.WriteLine("\n");
tcpclnt.Close();
Thread.Sleep(1000);
}
Server getting the sockets:
This bit of code is on the loginserver, its because i can only accept 1 socket every time to keep the connection alive, that i put queueCount on a maximum of 1.
I want to be able to make a list of Sockets that i accepted to add to a User account.
while (loginServerOn)
{
if (queueCount < 1)
{
if (loginServer.getLoginListener().Pending())
{
loginQueue.Add(loginServer.getSocket());
ASCIIEncoding asen = new ASCIIEncoding();
Socket s = loginQueue.First();
try
{
s.Send(asen.GetBytes("true"));
queueCount++;
}
catch
{
loginQueue.Remove(s);
}
}
}
}
The function that returns the accepted socket.
public Socket getSocket()
{
return myList.AcceptSocket();
}
EDIT: Essence of the question
I want to add the socked or client recieved to my Account object, so every connection has an Account its linked to, when i want to send a message to a certain account, it should send a message to the socked or client bound to that account, can you help/show me how i can achieve this?
This is still c# and sockets but my approach is different to yours.
I went with the concept of a "connectedCleint" which is similar in purpose to what you've called an account.
I have a class called ServerTerminal which is responsible for accepting and top level management of socket connections. In this i've got:
public Dictionary<long, ConnectedClient> DictConnectedClients =
new Dictionary<long, ConnectedClient>();
So this is my list of connected clients indexed by the sockethandle.
To accept connections i've got a routine:
public void StartListen(int port)
{
socketClosed = false;
IPEndPoint ipLocal = new IPEndPoint(IPAddress.Any, port);
listenSocket = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
//bind to local IP Address...
//if ip address is allready being used write to log
try
{
listenSocket.Bind(ipLocal);
}
catch (Exception excpt)
{
// Deal with this.. write your own log code here ?
socketClosed = true;
return;
}
//start listening...
listenSocket.Listen(100); // Max 100 connections for my app
// create the call back for any client connections...
listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);
}
So when a client connects it then fires off:
private void OnClientConnection(IAsyncResult asyn)
{
if (socketClosed)
{
return;
}
try
{
Socket clientSocket = listenSocket.EndAccept(asyn);
ConnectedClient connectedClient = new ConnectedClient(clientSocket, this, _ServerTerminalReceiveMode);
//connectedClient.MessageReceived += OnMessageReceived;
connectedClient.Disconnected += OnDisconnection;
connectedClient.dbMessageReceived += OndbMessageReceived;
connectedClient.ccSocketFaulted += ccSocketFaulted;
connectedClient.StartListening();
long key = clientSocket.Handle.ToInt64();
if (DictConnectedClients.ContainsKey(connectedClient.SocketHandleInt64))
{
// Already here - use your own error reporting..
}
lock (DictConnectedClients)
{
DictConnectedClients[key] = connectedClient;
}
// create the call back for any client connections...
listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);
}
catch (ObjectDisposedException excpt)
{
// Your own code here..
}
catch (Exception excpt)
{
// Your own code here...
}
}
The crucial part of this for you is:
// create the call back for any client connections...
listenSocket.BeginAccept(new AsyncCallback(OnClientConnection), null);
This sets up the serverterminal to receive new connections.
Edit:
Cut down version of my connectedclient:
public class ConnectedClient
{
private Socket mySocket;
private SocketIO mySocketIO;
private long _mySocketHandleInt64 = 0;
// These events are pass through; ConnectedClient offers them but really
// they are from SocketIO
public event TCPTerminal_ConnectDel Connected
{
add
{
mySocketIO.Connected += value;
}
remove
{
mySocketIO.Connected -= value;
}
}
public event TCPTerminal_DisconnectDel Disconnected
{
add
{
mySocketIO.Disconnected += value;
}
remove
{
mySocketIO.Disconnected -= value;
}
}
// Own Events
public event TCPTerminal_TxMessagePublished TxMessageReceived;
public delegate void SocketFaulted(ConnectedClient cc);
public event SocketFaulted ccSocketFaulted;
private void OnTxMessageReceived(Socket socket, TxMessage myTxMessage)
{
// process your message
}
private void OnMessageSent(int MessageNumber, int MessageType)
{
// successful send, do what you want..
}
public ConnectedClient(Socket clientSocket, ServerTerminal ParentST)
{
Init(clientSocket, ParentST, ReceiveMode.Handler);
}
public ConnectedClient(Socket clientSocket, ServerTerminal ParentST, ReceiveMode RecMode)
{
Init(clientSocket, ParentST, RecMode);
}
private void Init(Socket clientSocket, ServerTerminal ParentST, ReceiveMode RecMode)
{
ParentServerTerminal = ParentST;
_myReceiveMode = RecMode;
_FirstConnected = DateTime.Now;
mySocket = clientSocket;
_mySocketHandleInt64 = mySocket.Handle.ToInt64();
mySocketIO = new SocketIO(clientSocket, RecMode);
// Register for events
mySocketIO.TxMessageReceived += OnTxMessageReceived;
mySocketIO.MessageSent += OnMessageSent;
mySocketIO.dbMessageReceived += OndbMessageReceived;
}
public void StartListening()
{
mySocketIO.StartReceiving();
}
public void Close()
{
if (mySocketIO != null)
{
mySocketIO.Close();
mySocketIO = null;
}
try
{
mySocket.Close();
}
catch
{
// We're closing.. don't worry about it
}
}
public void SendMessage(int MessageNumber, int MessageType, string Message)
{
if (mySocket != null && mySocketIO != null)
{
try
{
mySocketIO.SendMessage(MessageNumber, MessageType, Message);
}
catch
{
// mySocketIO disposed inbetween check and call
}
}
else
{
// Raise socket faulted event
if (ccSocketFaulted != null)
ccSocketFaulted(this);
}
}
}
}
Some useful links:
This is where I started:
http://vadmyst.blogspot.com.au/2008/01/how-to-transfer-fixed-sized-data-with.html
http://vadmyst.blogspot.com.au/2008/03/part-2-how-to-transfer-fixed-sized-data.html
And..
C# Sockets and Multithreading
Cause a connected socket to accept new messages right after .BeginReceive?
http://nitoprograms.blogspot.com.au/2009/04/tcpip-net-sockets-faq.html
http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod
I can't post my entire solution just now; there is a flaw in my server code I need to debug; plus there are parts which my employer may not want published. But i based my code on what Vadym had for variable length messages.
When a server gets ready to accept TCP connections, it creates a new TCP socket, Bind() it to a port and uses the Listen() method. When a connection request comes in, the Listen() method returns a new socket that the server and client use for communication. The server and client can pass data back and forth using Send() and Receive() at this point. If the client disconnects, the server's Receive() terminates with 0 bytes of data.
If you want to wait for another connection request once you've accepted the first connection (i.e., while you are interacting with the first client) this can be done. At this point, you'll need to use something like threads or asynchronous methods so you can handle more than one connection. Basically, you will be able to Accept() connection requests from your listening socket.
Mike
I got the code below from a website,and this way of serial port reading is my only option because DataReceived event often doesn't work.but this code has a problem,if I close the application while transfering the application hangs forever,but I can't see why?neither freezing nor aborting the thread work.actually aborting the thread causes the program to crash.
public class CommPort
{
SerialPort _serialPort;
Thread _readThread;
bool _keepReading;
//begin Singleton pattern
static readonly CommPort instance = new CommPort();
// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit
static CommPort()
{
}
CommPort()
{
_serialPort = new SerialPort();
_readThread = null;
_keepReading = false;
}
public static CommPort Instance
{
get
{
return instance;
}
}
//end Singleton pattern
//begin Observer pattern
public delegate void EventHandler(string param);
public EventHandler StatusChanged;
public EventHandler DataReceived;
private void StartReading()
{
if (!_keepReading)
{
_keepReading = true;
_readThread = new Thread(new ThreadStart(ReadPort));
_readThread.Start();
}
}
private void StopReading()
{
if (_keepReading)
{
_keepReading = false;
_serialPort.Close();
//_readThread.Join(); //block until exits
_readThread.Abort();
//_readThread = null;
}
}
private void ReadPort()
{
while (_keepReading)
{
if (_serialPort.IsOpen)
{
byte[] readBuffer = new byte[_serialPort.ReadBufferSize + 1];
try
{
// If there are bytes available on the serial port,
// Read returns up to "count" bytes, but will not block (wait)
// for the remaining bytes. If there are no bytes available
// on the serial port, Read will block until at least one byte
// is available on the port, up until the ReadTimeout milliseconds
// have elapsed, at which time a TimeoutException will be thrown.
int count = _serialPort.Read(readBuffer, 0, _serialPort.ReadBufferSize);
String SerialIn = System.Text.Encoding.ASCII.GetString(readBuffer, 0, count);
DataReceived(SerialIn);
}
catch (TimeoutException)
{
}
}
else
{
TimeSpan waitTime = new TimeSpan(0, 0, 0, 0, 50);
Thread.Sleep(waitTime);
}
}
}
/// <summary> Open the serial port with current settings. </summary>
public void Open()
{
Close();
try
{
_serialPort.PortName = Properties.Settings.Default.COMPort;
_serialPort.BaudRate = Properties.Settings.Default.BPS;
_serialPort.Parity = Properties.Settings.Default.Parity;
_serialPort.DataBits = Properties.Settings.Default.DataBit;
_serialPort.StopBits = Properties.Settings.Default.StopBit;
_serialPort.Handshake = Properties.Settings.Default.HandShake;
// Set the read/write timeouts
_serialPort.ReadTimeout = 50;
_serialPort.WriteTimeout = 50;
_serialPort.Open();
StartReading();
}
catch (IOException)
{
StatusChanged(String.Format("{0} does not exist", Properties.Settings.Default.COMPort));
}
catch (UnauthorizedAccessException)
{
StatusChanged(String.Format("{0} already in use", Properties.Settings.Default.COMPort));
}
catch (Exception ex)
{
StatusChanged(String.Format("{0}", ex.ToString()));
}
// Update the status
if (_serialPort.IsOpen)
{
string p = _serialPort.Parity.ToString().Substring(0, 1); //First char
string h = _serialPort.Handshake.ToString();
if (_serialPort.Handshake == Handshake.None)
h = "no handshake"; // more descriptive than "None"
StatusChanged(String.Format("{0}: {1} bps, {2}{3}{4}, {5}",
_serialPort.PortName, _serialPort.BaudRate,
_serialPort.DataBits, p, (int)_serialPort.StopBits, h));
}
else
{
StatusChanged(String.Format("{0} already in use", Properties.Settings.Default.COMPort));
}
}
/// <summary> Close the serial port. </summary>
public void Close()
{
StopReading();
StatusChanged("connection closed");
}
public bool IsOpen
{
get
{
return _serialPort.IsOpen;
}
}
}
When you close the Port in StopReading() it will cause an Exception in _serialPort.Read(...).
Not sure which one exactly but it's not a TimeOut. Your current code lets that escape and that's when your thread and your App are killed.
So add a catch(Exceptiopn ex) around the while loop in ReadPort().
Two issues at first glance.
First off, you have created a singleton class that exposes an event subscription. That's dangerous, because you should keep track of any of the subscribers. Otherwise they may be never released. That's probably what's going on in your case.
Secondly, when you manage something "IDisposable", you should take care about it. So, the SerialPort should be disposed inside a IDisposable implementation within your class. However, that won't make any sense in a singleton class.
The singleton pattern should be used only for centralized passive resources, and never to host "active" objects, events, etc.
Hope this helps.