I am using an c# UdpClient to receive data via udp. Everything works fine, but after a while the method receiveClient does not return.
First aI thought it is a synchronisation problem so I enclosed the shared resource (lastPackage) with a lock, but this did not help.
I debugged the program (which was hard, due to the asynchron receiveing) and finally found out that the method endReceive does not return. The udp source does continue to send the packages.
Below I have printed the relevant program parts.
public class UDPSocket {
private IPEndPoint receiveEndPoint;
private UdpClient receiveClient;
private byte[] lastPackage;
private bool unhandledPackage;//shared resource
private Dictionary<string, string> agentsMap;
public UDPSocket(string sendIp, int sendPort, int receivePort){
receiveEndPoint = new IPEndPoint (IPAddress.Any, receivePort);
receiveClient = new UdpClient (receiveEndPoint);
singleton.receiveClient.BeginReceive(new AsyncCallback(singleton.ReceiveCallback), null);
unhandledPackage = false;
agentsMap = new Dictionary<string, string> ();
}
public void Update () {//is called once per second
if (unhandledPackage) {
string rawString = Encoding.UTF8.GetString(lastPackage);
agentsMap = Parser.parseString(rawString);
unhandledPackage = false;
receiveClient.BeginReceive(new AsyncCallback(ReceiveCallback), null);
}
}
public string getEmotion(string id){
string emotion;
if(!agentsMap.TryGetValue(id, out emotion)){
return "No connection";
}
return emotion;
}
public void OnDestroy()
{
if (receiveClient != null) {
receiveClient.Close ();
}
}
public void ReceiveCallback(IAsyncResult ar)
{
lastPackage = receiveClient.EndReceive(ar, ref receiveEndPoint);//DOES NOT RETURN FROM THIS METHOD CALL
unhandledPackage = true;
}
I have simplified the program to show only possibly relevant parts.
I would be thankfull, if somebody could help me.
Regards,
Jan
If you are using .net 4.5, simply use the async&await keywords, this way:
private static async void Foo(int port)
{
UdpClient udpClient=new UdpClient(port);
UdpReceiveResult result = await udpClient.ReceiveAsync();
}
EndReceive will block if the async operation has not finished.
I assume that receiveClient in ReceiveCallback is another receiveClient as that was used with BeginReceive. So make sure you use the same instance.
Related
Currently my android app communicates with a server via tcp socket with synchronous read and write methods
private val addressString : String = "192.168.1.200"
private val port : Int = 33322
private var socketAddress: InetSocketAddress = InetSocketAddress(addressString, port)
private var socket: Socket? = null
fun connect(){
socket = Socket()
socketAddress = InetSocketAddress(addressString, port)
try{
socket!!.connect(socketAddress)
}catch(e : Exception){
socket!!.close()
}
}
fun disconnect(){
try {
socket?.shutdownOutput()
} catch (e: Exception) {}
try {
socket?.shutdownInput()
} catch (e: Exception) {}
try {
socket?.close()
} catch (e: Exception) {}
socket = null
}
fun send(data: ByteArray): Int {
var sentByteCount: Int = -1
try {
socket!!.getOutputStream().write(data)
sentByteCount = data.size
} catch (e: Exception) {
throw e
}
return sentByteCount
}
data class Wrapper<T>(
var value:T
)
fun receive(buffer: Wrapper<ByteArray>): Int {
val size = buffer.value.size
var receivedByteCount: Int = -1
try {
receivedByteCount = socket!!.getInputStream().read(buffer.value)
} catch (e: Exception) {
throw e
}
return receivedByteCount
}
However, the server, written in C#, always communicates with another device via socket but with an asynchronous reading method
public const int BUFFER_SIZE = 4096;
private string addressString = "192.168.1.200"
private int port = 33322
private int timeout = 5000
private byte[] _buffer = new byte[BUFFER_SIZE];
private TcpClient _client;
private SocketError _socketError;
private AsyncCallback _callback = new AsyncCallback(ReceiveCallback);
public void connect()
{
_client = TCpClient()
_client.ConnectAsync(addressString, port).Wait(timeout)
}
public void receive()
{
_client.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, _socketError, _callback, null)
}
private void receiveCallback(IAsyncResult ar)
{
//callback method when it has finished reading asynchronously
}
public void send()
{
_client.GetStream().Write(_buffer, 0, _buffer.Length);
}
public void disconnect()
{
if (_client.Connected)
_client.Close();
}
What I need is to communicate between my app and the device as the Server is already doing.
I tried to find out if even in kotlin there was the possibility of creating an asynchronous connection that would give me the possibility of being able to do an asynchronous reading as it can be done in C # and I found AsynchronousSocketChannel.
From the documentation, however, it has been deduced that the possibility of using this socket is linked to the fact that the server is an AsynchronousServerSocketChannel socket, in my case it is not possible to use it as I can only create the client.
Are there any other possibilities to recreate something similar to the C # code shown above in Kotlin?
I have a simple socket listener application. It needs to be able to receive requests and give a response and also send requests itself and receive the responses for them.
As soon as my application starts, it will start receiving in a separate thread and send a response. This part works fine.
However when I send requests through the SendRequest()-Method, I need to filter incoming responses, so the correct responses go to the correct requets earlier made. I do this (as seen in code below) with the class ResponseHandler, which lets me register a request and in return notifies my registered request, as soon as the correct response came in. A placed request should however time out after 10 seconds, so I used a CountdownEvent, which waits these 10 seconds, but releases earlier, if the response came in earlier.
Problem: My CountdownEvent waits always the whole 10 seconds and only after that, the thread, where I receive messages will continue and thus receive the response. How is this possible, when I receive on a different thread?
I would think, that my program continues to receive in that separate thread, even when the CountdownEvent.Wait() is active.
Note: The awaited response really comes back instantly after I placed the request as seen with the NetworkTool WireShark. So the timeout is not correct.
Edit: In a simple WPF-Application, where the SendRequest() is called from a button, it works. Unfortunately, this means my big program is the problem.
Service:
public class Service
{
private readonly ResponseHandler _responseHandler;
private readonly SyncSocketServer _serverSocket;
private static readonly int ServerPort = 9090;
public Service()
{
_responseHandler = new ResponseHandler();
_serverSocket = new SyncSocketServer(ServerPort);
_serverSocket.StartListening();
_serverSocket.DataReceived += ServerSocket_DataReceived;
}
public void ServerSocket_DataReceived(object sender, string message)
{
// Here I left irrelevant code out: Originally, I check here,
// whether the message is a request or response and so on, and
// I only forward the message to the _responseHandler, if it is
// indeed a response. If it is a request I send an answer.
string messageId = GetIdFromMessage(message);
_responseHandler.DataReceived(messageId, message);
}
public void SendRequest(string message)
{
string messageId = Guid.NewGuid().ToString();
string request = CreateRequest(messageId, message);
_responseHandler.Register(messageId);
_serverSocket.Send(request);
string response = _responseHandler.WaitForResponse(messageId);
Debug.WriteLine("I got the correct response: " + response);
}
}
SyncSocketServer:
public class SyncSocketServer
{
public event EventHandler<string> DataReceived;
private const int BufferSize = 1024;
private const string EndDelimiter = "\n";
private Socket _listenerSocket;
private Socket _client;
private string _data;
private Byte[] _buffer;
private readonly int _port;
public SyncSocketServer(int port)
{
_port = port;
_buffer = new Byte[BufferSize];
}
public void StartListening()
{
IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[3];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, _port);
_listenerSocket = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_listenerSocket.Bind(localEndPoint);
_listenerSocket.Listen(5);
_client = _listenerSocket.Accept();
Debug.WriteLine("Local socket opened on: {0}", _listenerSocket.LocalEndPoint);
StartReceiving();
}
private void StartReceiving()
{
Thread d = new Thread(() => {
Thread.CurrentThread.IsBackground = true;
while (true)
{
_data = null;
while (true)
{
int bytesReceived = _client.Receive(_buffer);
_data += Encoding.ASCII.GetString(_buffer, 0, bytesReceived);
if (_data.IndexOf(EndDelimiter, StringComparison.OrdinalIgnoreCase) > -1)
break;
}
Debug.WriteLine("Message received:" + _data);
OnDataReceived(_data);
}
});
d.Start();
}
public void Send(string message)
{
byte[] bytesMessage = Encoding.ASCII.GetBytes(message + EndDelimiter);
_client.Send(bytesMessage);
Debug.WriteLine("Message sent: " + message);
}
protected virtual void OnDataReceived(string data)
{
EventHandler<string> handler = DataReceived;
if (handler != null)
handler(this, data);
}
}
ResponseHandler:
public class ResponseHandler
{
private const int WaitForResponseTimeout = 10000;
private readonly Dictionary<string, PendingRequest> _pendingRequests;
public ResponseHandler()
{
_pendingRequests = new Dictionary<string, PendingRequest>();
}
public void DataReceived(string messageId, string response)
{
_pendingRequests.TryGetValue(messageId, out var pendingRequest);
if (pendingRequest == null)
Debug.WriteLine("Received response for request, that has been removed");
else
{
pendingRequest.ResponseReceived(response);
_pendingRequests.Remove(messageId);
}
}
public void Register(string messageId)
{
_pendingRequests.Add(messageId, new PendingRequest());
}
public string WaitForResponse(string messageId)
{
_pendingRequests.TryGetValue(messageId, out var pendingRequest);
if (pendingRequest == null)
return null;
pendingRequest.Await();
return pendingRequest.Response;
}
private class PendingRequest
{
public string Response { get; private set; }
private readonly CountdownEvent _countdownEvent;
public PendingRequest()
{
_countdownEvent = new CountdownEvent(1);
}
public void Await()
{
// Here, the current thread gets blocked, but
// I expect, that the thread, where I receive
// would continue receiving
_countdownEvent.Wait(WaitForResponseTimeout);
}
public void ResponseReceived(stringresponse)
{
Response = response;
_countdownEvent.Signal();
}
}
}
So, your PendingRequest and ResponseHandler classes are being accessed from different threads. So, there are a couple of things you need to do, for the sanity of your program:
a) Make sure that when you are adding and removing requests from your pending requests dictionary, you get a lock, because you are simultaneously accessing a shared datastructure from different threads. Otherwise you can corrupt your datastructure.
b) Your more immediate problem is the Await() method in PendingRequest. You are calling CountdownEvent.Wait() without verifying if your response is already set. If your response is already set, it would mean that you would wait for 10 seconds before you process it. This can happen if your response arrives, even before you invoke CountdownEvent.Wait(). In that case, CountdownEvent.Signal() will just be ignored. You should change the PendingRequest.Wait() as follows:
while (Response is not set) {
CountdownEvent.Await();
}
Also, doesn't your CountdownEvent.Wait() semaphore require a mutex to be passed to it ? Remember that your Response object is being shared between threads. This is the general paradigm for using the wait() method:
mutex.lock();
while (Response is not set) {
CountdownEvent.Await(mutex);
}
// Do your stuff, since your condition is satisfied
mutext.unlock();
The problem is actually the false assumption, that firing an event, like I did below, would result in a fire and forget:
protected virtual void OnDataReceived(string data)
{
EventHandler<string> handler = DataReceived;
if (handler != null)
handler(this, data);
}
In the function StartReceiving(), where I receive data and forward it to the subscribers, it would pause at the call, that fires the event and wait for all subscribers to finish their work (which includes, of course, waiting 10 seconds for the response). This leads to the fact, that my receiver-thread, waits for the other thread.
The solution is, to implement the call, so it will do a fire and forget:
protected virtual void OnDataReceived(string data)
{
EventHandler<string> handler = DataReceived;
if (handler != null)
handler.BeginInvoke(this, data, null, null);
}
I need to make a server that accepts and keeps for a long time many connections (perspectively over 100k).
My code is below:
public delegate Task ClientConnectedEventHandler(Stream stream);
public class Listener
{
public event ClientConnectedEventHandler OnClientConnected;
private readonly TcpListener _tcpListener;
public Listener()
{
_tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, 8082));
}
public void Start()
{
_tcpListener.Start();
_tcpListener.BeginAcceptTcpClient(Accept, null);
}
private void Accept(IAsyncResult asyncResult)
{
_tcpListener.BeginAcceptTcpClient(Accept, null);
var client = _tcpListener.EndAcceptTcpClient(asyncResult);
var stream = client.GetStream();
OnClientConnected?.Invoke(stream).ContinueWith(_ => client.Close());
}
}
class Program
{
static void Main(string[] args)
{
var listener = new Listener();
var count = 0;
var infoLock = new object();
listener.OnClientConnected += async stream =>
{
lock (infoLock)
{
count++;
Console.Title = count.ToString();
}
while (true)
{
// Some logic
await Task.Delay(100);
}
};
listener.Start();
while (true)
{
Thread.Sleep(100);
}
}
}
There is no problem when the logic takes up to 300-400 ms. But if I want to keep incoming connections for a long time, count variable increments very slow after accepting 8 clients, moreover appears a trouble with huge memory usage. What I'm doing wrong and how to resolve this?
Your memory issue may be caused by not disposing unmanaged resources. Both TcpClient and NetworkStream implement IDisposable and should be wrapped in Using blocks or manually Closed/Disposed. See How to properly and completely close/reset a TcpClient connection? for starters.
I've the following problem: I created a simple HTTP server component. The server should be controlled with buttons on the GUI. I can start the server without any problems, but if I want to stop the server the whole program is killed. I think that's an error of aborting the thread but I don't know how I can solve this problem.
Here's my code:
public class HttpServer {
private int port;
public HttpServer(int port) {
this.port = port;
}
public void Listen() {
TcpListener listener = new TcpListener(IPAddress.Any, port);
listener.Start();
try {
while (true) {
TcpClient client = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(client);
Thread thread = new Thread(new ThreadStart(processor.Process));
thread.Start();
Thread.Sleep(1);
}
}
catch { }
listener.Stop();
}
}
public class HttpProcessor {
private TcpClient client;
private StreamReader reader;
private StreamWriter writer;
public HttpProcessor(TcpClient client) {
this.client = client;
this.reader = null;
this.writer = null;
}
public void Process() {
reader = new StreamReader(client.GetStream());
writer = new StreamWriter(client.GetStream());
ParseRequest();
// some method calls to process the request and generate the response
SendResponse();
client.Close();
}
}
public partial class MainForm : Form {
private HttpServer server;
private Thread servthread;
private void Form_Load(object sender, EventArgs e) {
server = new HttpServer(8080);
}
private void Button1_Click(object sender, EventArgs e) {
servthread = new Thread(new ThreadStart(server.Listen));
servthread.Start();
Thread.Sleep(1);
}
private void Button2_Click(object sender, EventArgs e) {
servthread.Abort();
}
}
Do not use Thread.Abort(), ever! Use other means of communicating to the thread that it should stop, like a WaitHandle or even a private volatile bool stopThread; flag!
If you ever feel the need to call any other methods on a Thread than Start and Join you're probably doing something wrong and you should think about your design ;-)
See this: How to: Create and Terminate Threads (C# Programming Guide)
On your comment about AcceptTcpClient being a blocking call: Yes, it is. However, as others have noted too, you could easily change your class to avoid this problem:
public class HttpServer {
private int port;
private TcpListener listener; // Make the listener an instance member
public HttpServer(int port) {
this.port = port;
this.listener = new TcpListener(IPAddress.Any, port); // Instantiate here
}
public void Listen() {
listener.Start();
try {
while (true) {
TcpClient client = listener.AcceptTcpClient();
HttpProcessor processor = new HttpProcessor(client);
Thread thread = new Thread(new ThreadStart(processor.Process));
thread.Start();
Thread.Sleep(1);
}
}
catch { }
listener.Stop();
}
public void StopListening()
{
listener.Server.Close();
}
}
Then, instead of servthread.Abort(); you'd call server.StopListening();.
You may need to wrap the listener.Stop() line in a try/catch as well, but you'll have to try.
To make everything "kinda" work "kinda" correctly:
in HttpServer move listener variable from local var to class member
in HttpServer introduce a method:
public void Stop()
{
listener.Stop();
}
Change your Button2_Click method to:
private void Button2_Click(object sender, EventArgs e)
{
server.Stop();
servthread.Join();
}
PS: I assume that this is one of your first projects, so instead of writing a long post of how to do your stuff correctly, I suggested the changes that will allow you to continue your project. Bugs and architecture issues may come or may not come)
Happy learning.
HI everyone,
I'm going to be writing some code that has to listen for TCPIP messages coming from GSM mobile phones over GPRS. In the fullness of time, I see this as running on a Virtual Private Server, and it could well be processing multiple messages every second.
I'm a bit of a network programming virgin, so I've done a bit of research on the internet, and read a few tutorials. The approach I am considering at the moment is a windows service using sockets to monitor the port. If my understanding is correct, I need one socket to listen for connections from clients, and each time someone tries to connect with the port I will be passed another socket with which to communicate with them? Does this sound about right to more experienced ears?
I'm planning on using asynchronous communication, but on of the bigger design questions is whether to use threading or not. Threading isn't something I've really played with, and I am aware of a number of pitfalls - race conditions and debugging problems being but two.
If I avoid threads, I know I have to supply an object that acts as an identifier for a particular conversation. I was thinking GUIDs for this - any opinions?
Thanks in advance for any responses...
Martin
Starting from .net framework 2.0 SP1 there are some changings in socket libraries related to asyncronous sockets.
All multithreading used under the hood. We have no need to use multithreading manually (we don't need to use even ThreadPool explicitly). All what we do - using BeginAcceptSocket for starting accepting new connections, and using SocketAsyncEventArgs after accepting new connection .
Short implementation:
//In constructor or in method Start
var tcpServer = new TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);
//In EndAcceptSocket
Socket sock= lister.EndAcceptSocket(asyncResult);
var e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted; //some data receive handle
e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
if (!sock.ReceiveAsync(e))
{//IO operation finished syncronously
//handle received data
ReceiveCompleted(sock, e);
}//IO operation finished syncronously
//Add sock to internal storage
Full implementation:
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Runtime.InteropServices;
namespace Ample
{
public class IPEndPointEventArgs : EventArgs
{
public IPEndPointEventArgs(IPEndPoint ipEndPoint)
{
IPEndPoint = ipEndPoint;
}
public IPEndPoint IPEndPoint { get; private set; }
}
public class DataReceivedEventArgs : EventArgs
{
public DataReceivedEventArgs(byte[] data, IPEndPoint ipEndPoint)
{
Data = data;
IPEndPoint = ipEndPoint;
}
public byte[] Data { get; private set; }
public IPEndPoint IPEndPoint { get; private set; }
}
/// <summary>
/// TcpListner wrapper
/// Encapsulates asyncronous communications using TCP/IP.
/// </summary>
public sealed class TcpServer : IDisposable
{
//----------------------------------------------------------------------
//Construction, Destruction
//----------------------------------------------------------------------
/// <summary>
/// Creating server socket
/// </summary>
/// <param name="port">Server port number</param>
public TcpServer(int port)
{
connectedSockets = new Dictionary<IPEndPoint, Socket>();
tcpServer = new TcpListener(IPAddress.Any, port);
tcpServer.Start();
tcpServer.BeginAcceptSocket(EndAcceptSocket, tcpServer);
}
~TcpServer()
{
DisposeImpl(false);
}
public void Dispose()
{
DisposeImpl(true);
}
//----------------------------------------------------------------------
//Public Methods
//----------------------------------------------------------------------
public void SendData(byte[] data, IPEndPoint endPoint)
{
Socket sock;
lock (syncHandle)
{
if (!connectedSockets.ContainsKey(endPoint))
return;
sock = connectedSockets[endPoint];
}
sock.Send(data);
}
//----------------------------------------------------------------------
//Events
//----------------------------------------------------------------------
public event EventHandler<IPEndPointEventArgs> SocketConnected;
public event EventHandler<IPEndPointEventArgs> SocketDisconnected;
public event EventHandler<DataReceivedEventArgs> DataReceived;
//----------------------------------------------------------------------
//Private Functions
//----------------------------------------------------------------------
#region Private Functions
//Обработка нового соединения
private void Connected(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
lock (connectedSocketsSyncHandle)
{
if (connectedSockets.ContainsKey(endPoint))
{
theLog.Log.DebugFormat("TcpServer.Connected: Socket already connected! Removing from local storage! EndPoint: {0}", endPoint);
connectedSockets[endPoint].Close();
}
SetDesiredKeepAlive(socket);
connectedSockets[endPoint] = socket;
}
OnSocketConnected(endPoint);
}
private static void SetDesiredKeepAlive(Socket socket)
{
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
const uint time = 10000;
const uint interval = 20000;
SetKeepAlive(socket, true, time, interval);
}
static void SetKeepAlive(Socket s, bool on, uint time, uint interval)
{
/* the native structure
struct tcp_keepalive {
ULONG onoff;
ULONG keepalivetime;
ULONG keepaliveinterval;
};
*/
// marshal the equivalent of the native structure into a byte array
uint dummy = 0;
var inOptionValues = new byte[Marshal.SizeOf(dummy) * 3];
BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, Marshal.SizeOf(dummy));
BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, Marshal.SizeOf(dummy) * 2);
// of course there are other ways to marshal up this byte array, this is just one way
// call WSAIoctl via IOControl
int ignore = s.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
}
//socket disconnected handler
private void Disconnect(Socket socket)
{
var endPoint = (IPEndPoint)socket.RemoteEndPoint;
lock (connectedSocketsSyncHandle)
{
connectedSockets.Remove(endPoint);
}
socket.Close();
OnSocketDisconnected(endPoint);
}
private void ReceiveData(byte[] data, IPEndPoint endPoint)
{
OnDataReceived(data, endPoint);
}
private void EndAcceptSocket(IAsyncResult asyncResult)
{
var lister = (TcpListener)asyncResult.AsyncState;
theLog.Log.Debug("TcpServer.EndAcceptSocket");
if (disposed)
{
theLog.Log.Debug("TcpServer.EndAcceptSocket: tcp server already disposed!");
return;
}
try
{
Socket sock;
try
{
sock = lister.EndAcceptSocket(asyncResult);
theLog.Log.DebugFormat("TcpServer.EndAcceptSocket: remote end point: {0}", sock.RemoteEndPoint);
Connected(sock);
}
finally
{
//EndAcceptSocket can failes, but in any case we want to accept
new connections
lister.BeginAcceptSocket(EndAcceptSocket, lister);
}
//we can use this only from .net framework 2.0 SP1 and higher
var e = new SocketAsyncEventArgs();
e.Completed += ReceiveCompleted;
e.SetBuffer(new byte[SocketBufferSize], 0, SocketBufferSize);
BeginReceiveAsync(sock, e);
}
catch (SocketException ex)
{
theLog.Log.Error("TcpServer.EndAcceptSocket: failes!", ex);
}
catch (Exception ex)
{
theLog.Log.Error("TcpServer.EndAcceptSocket: failes!", ex);
}
}
private void BeginReceiveAsync(Socket sock, SocketAsyncEventArgs e)
{
if (!sock.ReceiveAsync(e))
{//IO operation finished syncronously
//handle received data
ReceiveCompleted(sock, e);
}//IO operation finished syncronously
}
void ReceiveCompleted(object sender, SocketAsyncEventArgs e)
{
var sock = (Socket)sender;
if (!sock.Connected)
Disconnect(sock);
try
{
int size = e.BytesTransferred;
if (size == 0)
{
//this implementation based on IO Completion ports, and in this case
//receiving zero bytes mean socket disconnection
Disconnect(sock);
}
else
{
var buf = new byte[size];
Array.Copy(e.Buffer, buf, size);
ReceiveData(buf, (IPEndPoint)sock.RemoteEndPoint);
BeginReceiveAsync(sock, e);
}
}
catch (SocketException ex)
{
//We can't truly handle this excpetion here, but unhandled
//exception caused process termination.
//You can add new event to notify observer
theLog.Log.Error("TcpServer: receive data error!", ex);
}
catch (Exception ex)
{
theLog.Log.Error("TcpServer: receive data error!", ex);
}
}
private void DisposeImpl(bool manualDispose)
{
if (manualDispose)
{
//We should manually close all connected sockets
Exception error = null;
try
{
if (tcpServer != null)
{
disposed = true;
tcpServer.Stop();
}
}
catch (Exception ex)
{
theLog.Log.Error("TcpServer: tcpServer.Stop() failes!", ex);
error = ex;
}
try
{
foreach (var sock in connectedSockets.Values)
{
sock.Close();
}
}
catch (SocketException ex)
{
//During one socket disconnected we can faced exception
theLog.Log.Error("TcpServer: close accepted socket failes!", ex);
error = ex;
}
if ( error != null )
throw error;
}
}
private void OnSocketConnected(IPEndPoint ipEndPoint)
{
var handler = SocketConnected;
if (handler != null)
handler(this, new IPEndPointEventArgs(ipEndPoint));
}
private void OnSocketDisconnected(IPEndPoint ipEndPoint)
{
var handler = SocketDisconnected;
if (handler != null)
handler(this, new IPEndPointEventArgs(ipEndPoint));
}
private void OnDataReceived(byte[] data, IPEndPoint ipEndPoint)
{
var handler = DataReceived;
if ( handler != null )
handler(this, new DataReceivedEventArgs(data, ipEndPoint));
}
#endregion Private Functions
//----------------------------------------------------------------------
//Private Fields
//----------------------------------------------------------------------
#region Private Fields
private const int SocketBufferSize = 1024;
private readonly TcpListener tcpServer;
private bool disposed;
private readonly Dictionary<IPEndPoint, Socket> connectedSockets;
private readonly object connectedSocketsSyncHandle = new object();
#endregion Private Fields
}
}
It is surprisingly simple to make a multi-threaded server. Check out this example.
class Server
{
private Socket socket;
private List<Socket> connections;
private volatile Boolean endAccept;
// glossing over some code.
/// <summary></summary>
public void Accept()
{
EventHandler<SocketAsyncEventArgs> completed = null;
SocketAsyncEventArgs args = null;
completed = new EventHandler<SocketAsyncEventArgs>((s, e) =>
{
if (e.SocketError != SocketError.Success)
{
// handle
}
else
{
connections.Add(e.AcceptSocket);
ThreadPool.QueueUserWorkItem(AcceptNewClient, e.AcceptSocket);
}
e.AcceptSocket = null;
if (endAccept)
{
args.Dispose();
}
else if (!socket.AcceptAsync(args))
{
completed(socket, args);
}
});
args = new SocketAsyncEventArgs();
args.Completed += completed;
if (!socket.AcceptAsync(args))
{
completed(socket, args);
}
}
public void AcceptNewClient(Object state)
{
var socket = (Socket)state;
// proccess
}
}
A bit of advise from the guy who deals mainly with mobile networking: do your homework with regular networking connection, preferably on the localhost. This will save you a lot of time during testing and will keep you sane until you figure out the approach that works for you best.
As for some particular implementation, I always go with synchronized sockets (you will need to configure timeouts to not to get stuck if something will go wrong) and everything runs in separate threads that are synchronized with the help of events. It's much simplier than you think. Here's some useful links to get you started:
http://msdn.microsoft.com/en-us/library/3e8s7xdd.aspx
http://msdn.microsoft.com/en-us/library/ms228969.aspx
I'm writing the same application right now and I use solution like this:
http://clutch-inc.com/blog/?p=4
It's been tested right now and works perfectly. It is important to make this service only for receiving and storing messages (somewhere) without other work. I'm using NServiceBus for saving messages. Other service takes messages from queue and do the rest.
Well, the C# syntax is not fresh in my mind now but I don't think it is to much different from the Posix standard.
What you can may do is when you create your listen socket you can stipulate a value for the backlog (maximum number of simultaneous connections for that server) and create a thread pull with the same size. Thread pools are easier to use than traditional ones. The TCP you queue for you all the connections above the backlog parameter.