I am learning to use the SocketAsyncEventArgs stuff using the MSDN tutorial. I was just wondering a few things, namely how I could go about implementing the server with full-duplex capabilities.
Currently, the MSDN example teaches you to create a server that first listens, then when something is received, to send it back. Only then does the server start listening again.
The problem I am having coming up with my own solution is that the SocketAsyncEventArgs object has only one event, Completed that is fired for both sends and receives. It has no other events.
I read on some horribly translated site that I
must use two SocketAsyncEventArgs, one receives a hair.
-unknown
I find there is a disturbingly small amount of infromation on this supposedly "enhanced" socket implementation...
Heres a little bit of my code so you can see what i'm up to.
//Called when a SocketAsyncEventArgs raises the completed event
private void ProcessReceive(SocketAsyncEventArgs e)
{
Token Token = (Token)e.UserToken;
if(e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
{
Interlocked.Add(ref TotalBytesRead, e.BytesTransferred);
Console.WriteLine(String.Format("Received from {0}", ((Token)e.UserToken).Socket.RemoteEndPoint.ToString()));
bool willRaiseEvent = ((Token)e.UserToken).Socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
private void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
// done echoing data back to the client
Token token = (Token)e.UserToken;
// read the next block of data send from the client
bool willRaiseEvent = token.Socket.ReceiveAsync(e);
if (!willRaiseEvent)
{
ProcessReceive(e);
}
}
else
{
CloseClientSocket(e);
}
}
void IOCompleted(object sender, SocketAsyncEventArgs e)
{
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
ProcessSend(e);
break;
default:
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
Thanks!
The solution was to create a separate SocketAsyncEventArgs for both Send and Receive.
It takes a bit more memory but not much because there is no need to allocate a buffer to the Send args.
Related
I'm trying to implement file server that sends requested file to the client.
I implemented sending, and receiving part of Server and client, using TCP protocol.
My server sends data packet dequeued from the Packet queue until packet queue is empty, and my Client Receives until received packet number is same as desired packet number.
I used SendAsync() and ReceiveAsync() method from .Net framework.
My program Seems to have no problem when I debug it, But in the real run,
Client Seems to miss some packets sent from the server.. Even Server sent data packets, for some reason, client couldn't receive some data packets. I have no clue how this can happen.
Shouldn't TCP protocol ensure Receiving every packets sent from the server?
Do you have any clues why this happens?
Here is my code..
Receive_completed is callback method for ReceiveAsync() which is called after
ReceuveAsync is completed
and Process_receive function always reads every received bytes from ReceiveAsync(), and returns true if it has completed reading packet (Otherwise, return false, and Receive() method will be called again)
and.. ignore OperationCompleted, and _receivedProperly..
internal void Receive()//method for receiving the call
{
if (_operationState)
{
try
{
bool pending =_user.ClientSocket.ReceiveAsync(_receiveArg); // This will spawn a new thread and call Receive_completed as callback method
if (pending == false)
{
_receivePendingThread = new Thread(CallReceiveCompleted); //Start New thread
_receivePendingThread.Start();
}
}
catch (Exception exception)
{
ExceptionHandler(exception, CommunicatorError.ObjectDisposed);
}
}
}
private void Receive_completed(object sender, SocketAsyncEventArgs e)
{
if (e.LastOperation == SocketAsyncOperation.Receive)
{
bool transferCompleted = false;
try
{
if (e.BytesTransferred == 0)
{
throw new Exception("Socket Closed");
}
if (e.SocketError != SocketError.Success)
{
throw new Exception("Socket Error Occured");
}
}
catch (Exception exception)
{
ExceptionHandler(exception, CommunicatorError.SocketError);
return;
} //Checks for received bytes
//call process_receive here
transferCompleted = Process_receive(); //returns if more packets are left or not (This method Gurantees reading all received bytes from ReceiveAsync()
if (_receivedProperly&& _operationState) //Succeded process
{
if (transferCompleted && _operationState) Receive();//Receive if more bytes are left
else if (_operationState)
{// No more packets to be accepted additionally
CallReceiveCompleted();// Start Callback method
}
}
}
}
Here is the rumpus.
I am building a networked app using websocket-sharp and I have run into an issue where the server sends hundreds (not consistently the same amount) of duplicate messages to the client, heres the breakdown.
1.) The client connects (This handshake works fine).
2.) Upon client connection the server sends said client the session ID created for that connection. (This works)
3.) The client receives and stores the session ID. (This works and no duplicates are sent here)
4.) The client sends a request to the lobby system on the server to create a match. (This works and is received correctly)
5.) The server receives the match creation requests, creates the match and sends the match data back to the client (One message is actually sent but the client receives hundreds of the same message (all messages are the same size and contain the same info)).
I have reviewed my lobby system logic and there is no loop that could be sending the match creation reply multiple times, furthermore I know that the "Send" method on the server is only being called once.
I have looked through the websocket-sharp documentation, I thought maybe somehow there were hundreds of sessions being made from one connection or some non-sense along those lines but I am connecting one client and only one session exits :/
Let me know if you have any ideas how this could be happening. Thanks in advance.
See below for code.
Server:
Send Method:
public void Send (string path, string targetID, string script, string method, params object[] data) {
print ("SERVER: Sending Message");
WebSocketServiceHost host = null;
GetSocketServer ().WebSocketServices.TryGetServiceHost (path, out host);
if (host != null) {
Network.Packet packet = new Network.Packet ("Server", targetID, script, method, data);
if (targetID != "Broadcast") {
GetSocketServer ().WebSocketServices.Broadcast (Formatter.singleton.Serialize (packet));
} else if (targetID == "Service") {
host.Sessions.Broadcast (Formatter.singleton.Serialize (packet));
} else {
host.Sessions.SendToAsync (Formatter.singleton.Serialize (packet), targetID);
}
}
}
Client:
Receive Methods:
GetPersistent ().OnMessage += MessageHandler;
void MessageHandler (object sender, MessageEventArgs e) {
Debug.Log ("CLIENT: Message Recieved");
Network.singleton.Recieve (e.RawData);
}
Network: (Used by both Server and Client)
Receive Methods:
public void Recieve (byte[] data) {
object obj = Formatter.singleton.Deserialize (data);
messages.Add (messages.Count, obj);
}
IEnumerator ProcessorCoro () {
WaitForSeconds delay = new WaitForSeconds (Time.deltaTime);
while (true) {
if (messages.Count >= 1) {
Process (messages [(messages.Count - 1)]);
messages.Remove ((messages.Count - 1));
}
yield return delay;
}
}
public void Process (object data) {
Network.Packet packet = (Network.Packet)data;
switch (packet.script) {
case "Home":
Home.singleton.SendMessage (packet.method, data, SendMessageOptions.DontRequireReceiver);
break;
case "Arena":
Arena.singleton.SendMessage (packet.method, data, SendMessageOptions.DontRequireReceiver);
break;
case "Client":
Client.singleton.SendMessage (packet.method, data, SendMessageOptions.DontRequireReceiver);
break;
case "Database":
Database.singleton.SendMessage (packet.method, data, SendMessageOptions.DontRequireReceiver);
break;
}
}
At the client side, you need to ensure that you call the following line only once
GetPersistent ().OnMessage += MessageHandler;
Otherwise, you would be registering the same handler multiple times to the message reception.
Note: This behavior could be desired in case you want to attach multiple handlers to the same OnMessage event.
I currently have a TCP Server implemented using C# SAEA. What I would like to do is forward a message between 2 TCP Clients connected to the server (Client 1 and Client 2).
The Server uses receiveSendEventArgs.AcceptSocket.ReceiveAsync and
receiveSendEventArgs.AcceptSocket.SendAsync commands to send and
receive information from each of the connected clients with no
problem.
The Server is in receiveSendEventArgs.AcceptSocket.ReceiveAsync operation for both Client 1 and Client 2.
Client 1 sends one Message and the the Server Accepts the message. The Server sees that Client 2 is also connected, and so needs to get the receiveSendEventArgs reference to Client 2 to forward the message.
However, the Server takes the reference of receiveSendEventArgs of Client 2 and begins to prepare the buffer (SetBuffer) to send the message and I believe since the Socket is still in a "ReceiveSync" state for Client 2, it blows up with the following message:
"An asynchronous socket operation is already in progress using this SocketAsyncEventArgs instance."
Is there a way to switch Client 2 state on the Server from "ReceiveAsync"to "SendAsync" so that it doesn't error out when I try to SendData to Client 2? I know the Completed event is triggered when either Sending or Receiving operations complete, however, Simply Calling my IO_Completed method directly does not change the operation.
In a for loop setting up EventHandlers for Completed events for SocketAsyncEventArgs:
eventArgObjectForPool.Completed += new EventHandler(IO_Completed);
void IO_Completed(object sender, SocketAsyncEventArgs e){
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)e.UserToken;
//More business logic here ...
// determine which type of operation just completed and call the associated handler
switch (e.LastOperation)
{
case SocketAsyncOperation.Receive:
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("IO_Completed method in Receive, receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessReceive(e);
break;
case SocketAsyncOperation.Send:
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("IO_Completed method in Send, id " + receiveSendToken.TokenId);
}
ProcessSend(e);
break;
default:
//This exception will occur if you code the Completed event of some
//operation to come to this method, by mistake.
throw new ArgumentException("The last operation completed on the socket was not a receive or send");
}
}
private void StartReceive(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartReceive(), receiveSendToken id " + receiveSendToken.TokenId);
}
switch (receiveSendToken.clientInfo.currentState)
{
case MyClient.ClientState.Connecting://This occurs when we get client to connect for first time. However, it will automatically disconnect
receiveSendToken.theMediator.HandleData(receiveSendToken.theDataHolder);
// Create a new DataHolder for next message.
receiveSendToken.CreateNewDataHolder();
//Reset the variables in the UserToken, to be ready for the
//next message that will be received on the socket in this
//SAEA object.
receiveSendToken.Reset(true);
receiveSendToken.theMediator.PrepareOutgoingData();
StartSend(receiveSendToken.theMediator.GiveBack());
//******************************************************************
break;
default:
//Set the buffer for the receive operation.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetReceive, this.socketListenerSettings.BufferSize);
// Post async receive operation on the socket.
bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.ReceiveAsync(receiveSendEventArgs);
//Socket.ReceiveAsync returns true if the I/O operation is pending. The
//SocketAsyncEventArgs.Completed event on the e parameter will be raised
//upon completion of the operation. So, true will cause the IO_Completed
//method to be called when the receive operation completes.
//That's because of the event handler we created when building
//the pool of SocketAsyncEventArgs objects that perform receive/send.
//It was the line that said
//eventArgObjectForPool.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
//Socket.ReceiveAsync returns false if I/O operation completed synchronously.
//In that case, the SocketAsyncEventArgs.Completed event on the e parameter
if (!willRaiseEvent)
{
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartReceive in if (!willRaiseEvent), receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessReceive(receiveSendEventArgs);
}
break;
}
}
private void StartSend(SocketAsyncEventArgs receiveSendEventArgs)
{
DataHoldingUserToken receiveSendToken = (DataHoldingUserToken)receiveSendEventArgs.UserToken;
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartSend, id " + receiveSendToken.TokenId);
}
if (Program.watchThreads == true) //for testing
{
DealWithThreadsForTesting("StartSend()", receiveSendToken);
}
if (receiveSendToken.sendBytesRemainingCount <= this.socketListenerSettings.BufferSize)
{
Program.testWriter.WriteLine("blocking:?(" + receiveSendEventArgs.AcceptSocket.Blocking + ")");
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, receiveSendToken.sendBytesRemainingCount);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, receiveSendToken.sendBytesRemainingCount);
}
else
{
//We cannot try to set the buffer any larger than its size.
//So since receiveSendToken.sendBytesRemainingCount > BufferSize, we just
//set it to the maximum size, to send the most data possible.
receiveSendEventArgs.SetBuffer(receiveSendToken.bufferOffsetSend, this.socketListenerSettings.BufferSize);
//Copy the bytes to the buffer associated with this SAEA object.
Buffer.BlockCopy(receiveSendToken.dataToSend, receiveSendToken.bytesSentAlreadyCount, receiveSendEventArgs.Buffer, receiveSendToken.bufferOffsetSend, this.socketListenerSettings.BufferSize);
//We'll change the value of sendUserToken.sendBytesRemainingCount
//in the ProcessSend method.
}
//post asynchronous send operation
bool willRaiseEvent = receiveSendEventArgs.AcceptSocket.SendAsync(receiveSendEventArgs);
if (!willRaiseEvent)
{
if (Program.watchProgramFlow == true) //for testing
{
Program.testWriter.WriteLine("StartSend in if (!willRaiseEvent), receiveSendToken id " + receiveSendToken.TokenId);
}
ProcessSend(receiveSendEventArgs);
}
}
This wasn't what I was wanting to do, but I was able to call the following:
receiveSendEventArgs.AcceptSocket.Write(strMyBuffer);
This allowed for me to write to the same socket and I did not have any errors. I was wanting to stick with The Async Commands but succumbed to a Sync write and it did not effect my server. Perhaps there are other solutions.
I currently have a client-server application that involves a Silverlight client and a .NET server. The .NET portion uses the Tcp classes provided in System.Net.Sockets namespace whereas the Silverlight client uses raw sockets. I'm porting into this from code that currently uses the HttpListener classes because it doesn't suit my needs. The Http classes, though, have on the SL side the ability to use Begin* and End* style asynchronous methods that allow me to specify a handler once the operation has completed. I'm having trouble getting this to work with the new system. My current strategy is to include the handler as part of the UserToken. However, it seems that this token is not getting updated.
Here is some redacted code. I am able to get the two sides to talk to each other, but it seems the correct UserToken is not being sent.
public class ClientUserToken
{
public Handler Handler { get; set; }
public string Test { get; set; }
public ClientUserToken(Handler handler, string test)
{
Handler = handler;
Test = test;
}
}
public class SocketClient
{
private Socket _clientSocket;
private string _ipAddress;
private int _port;
private void OpenSocket()
{
var endPoint = new DnsEndPoint(_ipAddress, _port);
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(null, "foo");
args.RemoteEndPoint = endPoint;
args.Completed += OnSocketCompleted;
_clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_clientSocket.ConnectAsync(args);
}
void OnSocketCompleted(object sender, SocketAsyncEventArgs e)
{
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect: ProcessConnect(e); break;
case SocketAsyncOperation.Receive: ProcessReceive(e); break;
case SocketAsyncOperation.Send: ProcessSend(e); break; // this never gets called
}
}
void ProcessConnect(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
byte[] response = new byte[1024];
e.SetBuffer(response, 0, response.Length);
_clientSocket.ReceiveAsync(e);
}
}
void ProcessReceive(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success && e.BytesTransferred > 0)
{
var userToken = e.UserToken as ClientUserToken; // this token is always the one set in ProcessConnect
// process the data
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is never called, for some reason
void ProcessSend(SocketAsyncEventArgs e)
{
if (e.SocketError == SocketError.Success)
{
var userToken = e.UserToken as ClientUserToken;
if (!_clientSocket.ReceiveAsync(e))
{
ProcessReceive(e);
}
}
}
// this is the public API that users use to actually send data across
public void SendToServer(byte[] data, int len, Handler handler)
{
SocketAsyncEventArgs args = new SocketAsyncEventArgs();
args.UserToken = new ClientUserToken(handler, "bar");
args.SetBuffer(data, 0, len);
if (!_clientSocket.SendAsync(args))
{
ProcessReceive(args);
}
}
}
As the comments above suggest, in ProcessReceive, userToken.Test is always "foo" and userToken.Handler is always null.
I have so far not been able to break into ProcessSend so I can't see what SocketAsyncEventArgs it actually sent. Anyone have a clue why this event isn't firing (Completed after a Send)? Am I screwing up my SendToServer function?
I realize there may be other existing problems with synchronization and such, but I don't think that's the issue here. One thing I did try was setting up a ManualResetEvent to ensure that no one sends data to the server before the connection has been completed (ProcessConnect) but that did not solve the issue, either.
Any help will be appreciated!
EDIT: So the reason this is happening is because when I call ReceiveAsync in the ProcessConnect function, it is being used when the server is sending back the response for my data. Hence, UserToken "foo" is present in the handler. The next time the server sends data, the ReceiveAsync uses the args with the UserToken "bar". So it is kind of out of sync, for the duplex communication bit. I can't ensure that the SocketAsyncEventArgs that I sent from the client-side is the same one that is used on the response. It seems like the only solution is to have the SL client open two sockets--one for server-initiated data and the other for client-initiated requests. However, this means I'm not taking advantage of the duplex nature.
This model won't work because I'm creating a new SocketAsyncEventArgs on each send, which means that the data can come back on any of these args. I've been moving towards a model with a pool of SocketAsyncEventArgs and each client can only have one request/response at a time.
I have around 5000 modem (thin clients), and I want to communicate with them, one of a my method is like this : string GetModemData(modemID), now I have an open port in server that listens to modem and I'm using socket programming to send data to modems (calling related function), but when i want send data to multiple modem in a same time and get response from them, I don't know what should i do? I can send data to one modem and waiting for its response and then send another data to other modems (sequential), but the problem is client should be wait long time to get answer(may be some different client want to get some information from modems so they all will be wait into the Q or something like this), I think one way to solving this problem is to use multiple port and listen for each modem to related port, but it takes too many ports and also may be memory usage going up and exceed my available memory space, so some lost may be occurred (is this true?). what should to do ? I'd thinking about Parallelism, but i think its not related i should to wait for one port, because i don't know should to pass current received data to which client. I'm using asp.net.
currently I'm doing like this:
private void StartListener()
{
ModemTcpListener = new TcpListener(ModemPort);
//ClientTcpListener = new TcpListener(ClientPort);
ModemTcpListener.Start();
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
}
and in return
private void DoReadModemCallback(IAsyncResult ar)
{
try
{
bool bRet = ar.AsyncWaitHandle.WaitOne(420000);
Modem modem = ar.AsyncState as Modem;
if (!bRet || modem == null)
{
return;
}
}
catch{}
// now send data to which client?????? if i'm going to use async????
}
and :
private void DoAcceptModemCallback(IAsyncResult ar)
{
try
{
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
TcpClient tcpClient = ModemTcpListener.EndAcceptTcpClient(ar);
Modem modem= new Modem(tcpClient, "");
tcpClient.GetStream().BeginRead(modem.Buffer, 0, tcpClient.ReceiveBufferSize, new AsyncCallback(DoReadModemCallback), modem);
ModemTcpListener.BeginAcceptTcpClient(new AsyncCallback(DoAcceptModemCallback), ModemTcpListener);
Log.Write("a Modem connect ...");
}
catch (Exception ex)
{
}
}
Heres an example keeping track of all your clients. I've compacted it for readability. You should really split it up into multiple classes.
I'm using Pool (which I just created and commited) and SimpleServer. Both classes are part of a library that I'm currently building (but far from done).
Don't be afraid of having 5000 sockets open, they do not consume much resources when you are using asynchronous operations.
public class SuperServer
{
private List<ClientContext> _clients = new List<ClientContext>();
private SimpleServer _server;
private Pool<byte[]> _bufferPool;
public SuperServer()
{
// Create a buffer pool to be able to reuse buffers
// since your clients will most likely connect and disconnect
// often.
//
// The pool takes a anonymous function which should return a new buffer.
_bufferPool = new Pool<byte[]>(() => new byte[65535]);
}
public void Start(IPEndPoint listenAddress)
{
_server = new SimpleServer(listenAddress, OnAcceptedSocket);
// Allow five connections to be queued (to be accepted)
_server.Start(5);
}
// you should handle exceptions for the BeginSend
// and remove the client accordingly.
public void SendToAll(byte[] info)
{
lock (_clients)
{
foreach (var client in _clients)
client.Socket.BeginSend(info, 0, info.Length, SocketFlags.None, null, null);
}
}
// Server have accepted a new client.
private void OnAcceptedSocket(Socket socket)
{
var context = new ClientContext();
context.Inbuffer = _bufferPool.Dequeue();
context.Socket = socket;
lock (_clients)
_clients.Add(context);
// this method will eat very few resources and
// there should be no problem having 5000 waiting sockets.
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
//Woho! You have received data from one of the clients.
private void OnRead(IAsyncResult ar)
{
var context = (ClientContext) ar.AsyncState;
try
{
var bytesRead = context.Socket.EndReceive(ar);
if (bytesRead == 0)
{
HandleClientDisconnection(context);
return;
}
// process context.Inbuffer here.
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
return;
}
// use a new try/catch to make sure that we start
// read again event if processing of last bytes failed.
try
{
context.Socket.BeginReceive(context.Inbuffer, 0, context.Inbuffer.Length, SocketFlags.None, OnRead,
context);
}
catch (Exception err)
{
//log exception here.
HandleClientDisconnection(context);
}
}
// A client have disconnected.
private void HandleClientDisconnection(ClientContext context)
{
_bufferPool.Enqueue(context.Inbuffer);
try
{
context.Socket.Close();
lock (_clients)
_clients.Remove(context);
}
catch(Exception err)
{
//log exception
}
}
// One of your modems
// add your own state info.
private class ClientContext
{
public byte[] Inbuffer;
public Socket Socket;
}
}
Used classes:
Pool: http://fadd.codeplex.com/SourceControl/changeset/view/58858#1054902
SimpleServer: http://fadd.codeplex.com/SourceControl/changeset/view/58859#1054893
You need to use the asynchronous tcp/ip methods. This article shows how:
http://www.codeproject.com/KB/IP/asyncsockets.aspx
The critical piece is the BeginReceive() and related callback functions. Any more q's, please leave comments to this answer ;) BEST OF LUCK!
You need multi threading, whenever a client establishes a connection to the server start a new thread for it and start communication send/receive.
Here are some articles explaining multithreading in c#,
c-sharpcorner
codeproject
And here's a sample server application with multithreading,
http://www.dotnetspider.com/resources/2829-A-multi-readed-server-C-which-finds-prime-num.aspx