How to use the buffer on SocketAsyncEventArgs object - c#

We're stuck with using buffers on the SocketAsyncEventArgs object.
With the old socket method we'd cast our state object, like this:
clientState cs = (clientState)asyncResult.AsyncState;
However, the 3.5 framework is different.
With have strings arriving from the client in chunks and we can't seem to work out how the buffers work so we can process an entire string when we find a char3.
Code at the moment:
private void ProcessReceive(SocketAsyncEventArgs e)
{
string content = string.Empty;
// Check if the remote host closed the connection.
if (e.BytesTransferred > 0)
{
if (e.SocketError == SocketError.Success)
{
Socket s = e.UserToken as Socket;
//asyncResult.AsyncState;
Int32 bytesTransferred = e.BytesTransferred;
// Get the message received from the listener.
content += Encoding.ASCII.GetString(
e.Buffer, e.Offset, bytesTransferred);
if (content.IndexOf(Convert.ToString((char)3)) > -1)
{
e.BufferList = null;
// Increment the count of the total bytes receive by the server
Interlocked.Add(ref this.totalBytesRead, bytesTransferred);
}
else
{
content += Encoding.ASCII.GetString(
e.Buffer, e.Offset, bytesTransferred);
ProcessReceive(e);
}
}
else
{
this.CloseClientSocket(e);
}
}
}

I'll start by saying I've never worked with .NET 3.5 sockets, so this answer is a bit of an educated guess.
The problem you are having is that you aren't storing the content in some state on each read.
So the problems are:
Firstly
You call ProcessReceive: string content = string.Empty;
You append to content: 'content += Encoding.ASCII.GetString(e.Buffer, e.Offset, ew.BytesTransferred);`
You call ProcessReceive Again: string content = string.Empty;
The cycle continues and content always gets set to an empty string.
Secondly
To receive the remaining data you are calling ProcessReceive, this is incorrect. You need to issue another read to the underlying Socket using ReceiveAsync.
I've modified your code using this blog post.
// You'll need some state object, if you don't already have one
class AsyncServerState
{
public byte[] Buffer = new byte[8192]; // 8K buffer
public StringBuilder Content = new StringBuilder0; // Place to store the content as it's received
public SocketAsyncEventArgs ReadEventArgs = new SocketAsyncEventArgs();
public Socket Client;
}
// You'll need to setup the state where ever you process your connect
// something similar to this.
void Accept_Completed(object sender, SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
Socket client = e.AcceptSocket;
AsyncServerState state = new AsyncServerState();
state.ReadEventArgs.AcceptSocket = client;
state.ReadEventArgs.Completed += new EventHandler( IO_Completed);
state.ReadEventArgs.UserToken = state;
state.Client = client;
state.ReadEventArgs.SetBuffer(state.Buffer, 0, state.Buffer.Length);
if (!client.ReceiveAsync(state.ReadEventArgs))
{
// Call completed synchonously
ProcessReceive(state.ReadEventArgs);
}
}
ProcessAccept(e);
}
private void ProcessReceive(SocketAsyncEventArgs e)
{
var state = e.UserToken as AsyncServerState;
// Check if the remote host closed the connection.
if (e.BytesTransferred > 0)
{
if (e.SocketError == SocketError.Success)
{
// Get the message received from the listener.
sting content = Encoding.ASCII.GetString(state.Buffer, 0, e.BytesTransferred);
// Append the received data to our state
state.Content.Append(content);
// Increment the count of the total bytes receive by the server
Interlocked.Add(ref this.totalBytesRead, bytesTransferred);
if (content.IndexOf(Convert.ToString((char)3)) > -1)
{
// Final Message stored in our state
string finalContent = state.Content.ToString();
return;
}
else
{
// You need to issue another ReceiveAsync, you can't just call ProcessReceive again
if (!state.Client.ReceiveAsync(state.ReadEventArgs))
{
// Call completed synchonously
ProcessReceive(state.ReadEventArgs);
}
}
}
else
{
this.CloseClientSocket(e);
}
}
}

Related

Socket.ReceiveAsync not calling e.Completed event

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?

TcpClient exceptions when calling EndReceive and BeginReceive

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;
}
...

Send a flag from server to clients using c# mouse events

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

Chat service application

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

C# UDP packetloss though all packets arrive (WireShark)

As the title says I have a problem with UDP in C#.
I'm trying to build a library for the rcon protocol of the game DayZ.
My problem is that I dont receive every packet I should receive.
After sending a command the server replies with an split answer. The packet header contains the total packet count and the index of the current packet.
Now if I should get 17 packets I only get 8-15 packets in my application.
After testing with WireShark I know now that all packages arrive on my computer. They just dont get recognized by my application or something like that.
My Actual Question is:
Is it possible to prevent losing the packages between my network card and my application? or
Why does that happen?
Here is my current code. Its pretty dirty because I ripped it apart after not working as expected:
private Socket _udpClient;
private Thread _receiverThread;
private Thread _workerThread;
private Queue<byte[]> _packetQueue;
private PacketBuffer[] MessageBuffer;
private byte SenderSequence = 0;
private IPEndPoint connection;
public RCon(IPAddress ip, int port)
{
connection = new IPEndPoint(ip, port);
_udpClient = new Socket(connection.Address.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
_udpClient.Connect(connection);
MessageBuffer = new PacketBuffer[256];
_packetQueue = new Queue<byte[]>();
_receiverThread = new Thread(new ThreadStart(ReceiveCallback));
_receiverThread.IsBackground = true;
_receiverThread.Priority = ThreadPriority.AboveNormal;
_receiverThread.Start();
_workerThread = new Thread(new ThreadStart(WorkerCallback));
_workerThread.IsBackground = true;
_workerThread.Start();
}
public void Login(string password)
{
LoginPacket packet = new LoginPacket(password);
_udpClient.Send(packet.Bytes);
}
public void SendCommand(string command)
{
CommandPacket packet = new CommandPacket(SenderSequence, command);
SenderSequence++;
_udpClient.Send(packet.Bytes);
}
private void ReceiveCallback()
{
while (true)
{
byte[] buffer = new byte[1036];
if (_udpClient.Receive(buffer) > 0)
_packetQueue.Enqueue(buffer);
}
}
private void WorkerCallback()
{
while (true)
{
if (_packetQueue.Count > 0)
{
byte[] buffer = _packetQueue.Dequeue();
if (buffer != null)
{
try
{
Packet receivedPacket = Packet.ParseIncoming(buffer);
OnPacketReceived(new PacketReceivedEventArgs(receivedPacket));
switch (receivedPacket.Type)
{
case PacketType.Message:
OnMessageReceived(new MessageReceivedEventArgs(receivedPacket.Content));
MessageCallbackPacket packet = new MessageCallbackPacket(receivedPacket.SequenceNumber);
_udpClient.Send(packet.Bytes);
break;
case PacketType.CommandCallback:
if (MessageBuffer[receivedPacket.SequenceNumber] == null)
MessageBuffer[receivedPacket.SequenceNumber] = new PacketBuffer(receivedPacket);
else
MessageBuffer[receivedPacket.SequenceNumber].AddPacket(receivedPacket);
if (MessageBuffer[receivedPacket.SequenceNumber].IsComplete)
OnCommandCallback(new CommandCallbackEventArgs(MessageBuffer[receivedPacket.SequenceNumber].GetContent()));
break;
}
}
catch (ArgumentException) { }
catch (OverflowException) { }
catch (FormatException) { }
}
}
}
}
This is usually because you are not consuming your datagrams fast enough, so in-kernel socket buffer gets full and the network stack starts dropping newly arriving packets. Some points:
Increase the receive buffer on the socket,
Don't acquire locks on every iteration - read as much as you can, then put data into the queue,
Consider non-blocking approach instead of threads.

Categories