I create a class which connects with other computer in LAN. When I induce a method Connect first time all is ok, but after work class the variable lose a reference to this object.
But if I try do create this object again and I induce a method Connect I have an error which is saying "Only one using each socket adress(protocol / network address / port) is permitted"
this error is when I try to do
partner.Connect(partnerIPEndPoint);.
What is bad in my class? I would appreciate if anybody could help me.
Thanks in advance.
Here is my class:
using System;
using System.Net;
using System.Net.Sockets;
using System.Diagnostics;
namespace AudioNetwork
{
class ConnectToServer : IDisposable
{
public int BeepAmonut { get; set; }
public int Speed { get; set; }
public IPAddress MyIPAddress { get; set; }
public IPAddress PartnerIPAddress { get; set; }
private NetworkStream stream;
private TcpClient partner;
public void Connect()
{
IPEndPoint myIPEndPoint = new IPEndPoint(MyIPAddress, 800);
IPEndPoint partnerIPEndPoint = new IPEndPoint(PartnerIPAddress, 800);
partner = new TcpClient(myIPEndPoint);
partner.Connect(partnerIPEndPoint);
stream = partner.GetStream();
Messanger(stream);
}
protected virtual void Messanger(NetworkStream myStream)
{
byte[] data = { 2, 1 };
myStream.Write(data, 0, data.Length);
Play(2000);
}
public void Play(int time)
{
Stopwatch sw = new Stopwatch();
sw.Start();
while (sw.ElapsedMilliseconds <= 2100)
{
if (sw.ElapsedMilliseconds >= time)
{
break;
}
}
}
public void Dispose()
{
GC.SuppressFinalize(this);
stream.Dispose();
stream.Close();
partner.Client.Dispose();
partner.Close();
}
}
class ConnectToClient : ConnectToServer
{
protected override void Messanger(NetworkStream myStream)
{
byte[] data = new byte[256];
int bytes = myStream.Read(data, 0, data.Length);
if (data[0] == 2)
{
Play(1980);
}
}
}
}
You assign a hardcoded port to the local endpoint, and don't close it.
When you open the first connection, you have a local socket on port 800 connected to the remote socket at port 800.
You then forget to close this connection (as you never call Dispose on steam or partner - I don't see your Dispose function being called anywhere)
When you're trying to open the second connection on the local port 800, it is already taken by the fist connection, which leads to the error you're encountering.
The solution is to let the OS assign an automatic port number for the local socket. That way:
When you open the first connection, you have a local socket on an auto-assigned port number connected to the remote socket at port 800.
When you open the second connection, even if the first one isn't closed yet, the OS will assign a different port number to the second socket and connect it to the remote socket at port 800.
In code, that would be simply:
partner = new TcpClient();
And get rid of all that MyIPAddress stuff.
Related
Edit: realized I need an implementation of this on C#:
https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT
I'll try to find one, if anyone has one I'll appreciate it, thanks!
I might have just made up that name on the spot.
What I'm trying to do is have a computer that is behind NAT and will need to provide a service connect to a server.
Then, I would connect to that server using a third computer and interact with the TCP stream initiated by the first computer, in a way that would work just as if I had connected straight to it.
Found a way to create a forward proxy, which works great:
blog.brunogarcia.com/2012/10/simple-tcp-forwarder-in-c.html
using System;
using System.Net;
using System.Net.Sockets;
namespace TcpProxy
{
class Program
{
static void Main(string[] args)
{
new TcpForwarderSlim().Start(
new IPEndPoint(IPAddress.Parse("127.0.0.1"), int.Parse("69")),
new IPEndPoint(IPAddress.Parse("91.198.174.192"), int.Parse("80")));
}
}
public class TcpForwarderSlim
{
private readonly Socket _mainSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public void Start(IPEndPoint local, IPEndPoint remote)
{
_mainSocket.Bind(local);
_mainSocket.Listen(10);
while (true)
{
var source = _mainSocket.Accept();
var destination = new TcpForwarderSlim();
var state = new State(source, destination._mainSocket);
destination.Connect(remote, source);
source.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
private void Connect(EndPoint remoteEndpoint, Socket destination)
{
var state = new State(_mainSocket, destination);
_mainSocket.Connect(remoteEndpoint);
_mainSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, OnDataReceive, state);
}
private static void OnDataReceive(IAsyncResult result)
{
var state = (State)result.AsyncState;
try
{
var bytesRead = state.SourceSocket.EndReceive(result);
if (bytesRead > 0)
{
state.DestinationSocket.Send(state.Buffer, bytesRead, SocketFlags.None);
state.SourceSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, 0, OnDataReceive, state);
}
}
catch
{
state.DestinationSocket.Close();
state.SourceSocket.Close();
}
}
private class State
{
public Socket SourceSocket { get; private set; }
public Socket DestinationSocket { get; private set; }
public byte[] Buffer { get; private set; }
public State(Socket source, Socket destination)
{
SourceSocket = source;
DestinationSocket = destination;
Buffer = new byte[8192];
}
}
}
}
Could anyone point me to the right direction?
UPNP can open a port on a properly configured router.
STUN, ICE, and TURN use intermediary servers to connect when ports can't be opened. There are open source server implementations.
UDP can use UDP Hole Punching.
This answer provides a more detailed explaination but not much C# help.
I am very new to sockets with C# and I have been working on trying to get a socket to work with localhost. Here is the main UDP code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class NetUDP
{
private UdpClient udp;
private IPEndPoint endPont;
public NetUDP(int port_local, int port_global, string ip_global, string ip_local)
{
Console.WriteLine(IPAddress.Any.ToString());
udp = new UdpClient(IPAddress.Any.ToString(), port_global);
endPont = new IPEndPoint(IPAddress.Parse(ip_global), port_global);
udp.Connect(ip_global, port_global);
}
public NetUDP()
{
}
public void UDPinit(int port_local, int port_global, string ip_global, string ip_local)
{
Console.WriteLine();
udp = new UdpClient("127.0.0.1", port_global);
endPont = new IPEndPoint(IPAddress.Parse("127.0.0.1"), port_global);
udp.Connect("127.0.0.1", port_global);
}
public void sendDataUDP(string info)
{
Byte[] SendByte = Encoding.ASCII.GetBytes(info);
udp.Send(SendByte, SendByte.Length);
}
public string getDataUDP()
{
Byte[] get = udp.Receive(ref endPont);
return Encoding.ASCII.GetString(get);
}
public void closeUDP()
{
udp.Close();
}
}
}
The problem is every time I do this code when I try to retrieve info from another UDP C# program (on the same machine) it freezes. I know it keeps on looking for info coming it's way, but id there a way to put a timeout on the receive command or do I have to make an event handler to prevent this freezing? The ports connect just fine and do not show any form of error or exception. Thank you for your help in advance!
class Program
{
static void Main(string[] args)
{
var serverEndPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8500);
NetUDP udp = new NetUDP(serverEndPoint);
UdpClient localClient = new UdpClient();
Task.Delay(20000);
var dgram = Encoding.ASCII.GetBytes("Hello Server");
localClient.Send(dgram, dgram.Length, serverEndPoint); // Send "Hello Server" to Server
Console.Read();
}
}
class NetUDP
{
private UdpClient udp;
private IPEndPoint endPont;
public NetUDP() { }
public NetUDP(IPEndPoint serverEndPoint)
{
udp = new UdpClient(serverEndPoint); // Bind the EndPoint
// Receive any IPAddress send to the 8555 port
IPEndPoint receiveEndPoint = new IPEndPoint(IPAddress.Any, 8555);
Task.Run(() =>
{
try
{
while (true)
{
// This Receive will block until receive an UdpClient
// So, run in another thread in thread pool
var bytes = udp.Receive(ref receiveEndPoint);
Console.WriteLine(Encoding.ASCII.GetString(bytes));
}
}
catch (Exception ex)
{
// TODO
}
finally
{
udp.Close();
}
});
}
}
Here is and UdpChat demo in github: UdpChat
When i tryed to connect from PC to android device, i caught a socket exception "No Connection could be made because the target machine actively refused it".
Code of a server at android device:
private void networkOperations()
{
Thread networkThread = new Thread (null, doBackgroundThreadProcessing, "Network");
networkThread.start();
}
private Runnable doBackgroundThreadProcessing = new Runnable()
{
public void run() {
backgroundThreadProcessing();
}
};
private void backgroundThreadProcessing()
{
Socket s = null;
ServerSocket ss = null;
try {
ss = new ServerSocket(8867);
} catch (IOException e) {
e.printStackTrace();
}
while(!Thread.currentThread().isInterrupted())
{
try {
if (s == null)
s = ss.accept();
BufferedReader input = new BufferedReader(new InputStreamReader(s.getInputStream()));
String st = null;
st = input.readLine();
} catch (IOException e) {
e.printStackTrace();
}
}
}
networkOperations() is calling in the onCreate() method of the Main Activity.
Code of a client at PC(C#):
class Connection
{
public Connection(String ip, String port)
{
Ip = IPAddress.Parse(ip);
Port = Convert.ToInt32(port);
}
public IPAddress Ip
{
get;
set;
}
public Int32 Port
{
get;
set;
}
public Boolean ConnectionError;
public Socket S;
public void ConnectToServer()
{
IPEndPoint ipe = new IPEndPoint(Ip, Port);
S = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
try
{
S.Connect(ipe);
}
catch (SocketException e)
{
}
}
Can anyone help me with this problem please? Thanks.
Based on the comments, there are two things you should pay attention. First, you are contacting a device in your local network from your local network using the public address. I'm not sure that all of the routers will be able to do this properly, but nevertheless you need to forward the 8867 port if you want public access. Try it, it may help.
Search your router here and you will find an explanation how to do that:
http://portforward.com/
The phone should certainly be available when accessing the phone from outside the network after forwarding. Of course, your phone should always have the same local address. Reserve it in the router settings or set it manually on the phone.
For testing purposes, try to use the phones local IP address from your PC.
I'm trying to put together a class to handle Ipc between processes using anonymous pipes provided by System.Io.Pipes.
The problem I'm having is that when I test the class using a single process the pipes set up correctly and I can send data between client and server without a problem. However, when I split the client and server into separate processes ( on the same machine ), the client is unable to connect to the end of the server pipe.
The error System.Io.Exception Invalid pipe handle is raised when call
_outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);
The full code of the class is pasted below.
Essentially its work like this;
Server Process. Create anonymous pipe set for inbound data - call this Pipe A
Server Process. Starts Client process and passes PipeHandle via command argument
Client Process. Connects to end of Pipe A
Client Process. Create anonymous pipe set for inbound data (Pipe B)
5 Client process. Passes pipe handle back to Server using Pipe A
Server Process. Connects to end of Pipe B
So now we have two anonymous pipes, pointing in opposite directions between Server and Client.
Here is the full code of my IPC class
public class MessageReceivedEventArgs : EventArgs
{
public string Message { get; set; }
}
public class IpcChannel : IDisposable
{
private AnonymousPipeServerStream _inboundPipeServerStream;
private StreamReader _inboundMessageReader;
private string _inboundPipeHandle;
private AnonymousPipeClientStream _outboundPipeServerStream;
private StreamWriter _outboundMessageWriter;
public delegate void MessageReceivedHandler(object sender, MessageReceivedEventArgs e);
public event MessageReceivedHandler MessageReceived;
private Thread _clientListenerThread;
private bool _disposing = false;
public IpcChannel()
{
SetupServerChannel();
}
public IpcChannel(string serverHandle)
{
SetupServerChannel();
// this is the client end of the connection
// create an outbound connection to the server
System.Diagnostics.Trace.TraceInformation("Connecting client stream to server : {0}", serverHandle);
SetupClientChannel(serverHandle);
IntroduceToServer();
}
private void SetupClientChannel(string serverHandle)
{
_outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, serverHandle);
_outboundMessageWriter = new StreamWriter(_outboundPipeServerStream)
{
AutoFlush = true
};
}
private void SetupServerChannel()
{
_inboundPipeServerStream = new AnonymousPipeServerStream(PipeDirection.In);
_inboundMessageReader = new StreamReader(_inboundPipeServerStream);
_inboundPipeHandle = _inboundPipeServerStream.GetClientHandleAsString();
_inboundPipeServerStream.DisposeLocalCopyOfClientHandle();
System.Diagnostics.Trace.TraceInformation("Created server stream " + _inboundPipeServerStream.GetClientHandleAsString());
_clientListenerThread = new Thread(ClientListener)
{
IsBackground = true
};
_clientListenerThread.Start();
}
public void SendMessage(string message)
{
System.Diagnostics.Trace.TraceInformation("Sending message {0} chars", message.Length);
_outboundMessageWriter.WriteLine("M" + message);
}
private void IntroduceToServer()
{
System.Diagnostics.Trace.TraceInformation("Telling server callback channel is : " + _inboundPipeServerStream.GetClientHandleAsString());
_outboundMessageWriter.WriteLine("CI" + _inboundPipeServerStream.GetClientHandleAsString());
}
public string ServerHandle
{
get
{
return _inboundPipeHandle;
}
}
private void ProcessControlMessage(string message)
{
if (message.StartsWith("CI"))
{
ConnectResponseChannel(message.Substring(2));
}
}
private void ConnectResponseChannel(string channelHandle)
{
System.Diagnostics.Trace.TraceInformation("Connecting response (OUT) channel to : {0}", channelHandle);
_outboundPipeServerStream = new AnonymousPipeClientStream(PipeDirection.Out, channelHandle);
_outboundMessageWriter = new StreamWriter(_outboundPipeServerStream);
_outboundMessageWriter.AutoFlush = true;
}
private void ClientListener()
{
System.Diagnostics.Trace.TraceInformation("ClientListener started on thread {0}", Thread.CurrentThread.ManagedThreadId);
try
{
while (!_disposing)
{
var message = _inboundMessageReader.ReadLine();
if (message != null)
{
if (message.StartsWith("C"))
{
ProcessControlMessage(message);
}
else if (MessageReceived != null)
MessageReceived(this, new MessageReceivedEventArgs()
{
Message = message.Substring(1)
});
}
}
}
catch (ThreadAbortException)
{
}
finally
{
}
}
public void Dispose()
{
_disposing = true;
_clientListenerThread.Abort();
_outboundMessageWriter.Flush();
_outboundMessageWriter.Close();
_outboundPipeServerStream.Close();
_outboundPipeServerStream.Dispose();
_inboundMessageReader.Close();
_inboundMessageReader.Dispose();
_inboundPipeServerStream.DisposeLocalCopyOfClientHandle();
_inboundPipeServerStream.Close();
_inboundPipeServerStream.Dispose();
}
}
In a single process, it can be used like this;
class Program
{
private static IpcChannel _server;
private static IpcChannel _client;
static void Main(string[] args)
{
_server = new IpcChannel();
_server.MessageReceived += (s, e) => Console.WriteLine("Server Received : " + e.Message);
_client = new IpcChannel(_server.ServerHandle);
_client.MessageReceived += (s, e) => Console.WriteLine("Client Received : " + e.Message);
Console.ReadLine();
_server.SendMessage("This is the server sending to the client");
Console.ReadLine();
_client.SendMessage("This is the client sending to the server");
Console.ReadLine();
_client.Dispose();
_server.Dispose();
}
Thanks in advance for any suggestions.
You didn't post the server code, but anyway. In the server:
You need to specify that the client's pipe handle is inheritable when you create it.
When you launch the client you need to specify that inheritable handles will be inherited.
If you miss either of these steps then the pipe handle will be invalid in the client process.
Also, your step 4 won't work. If you create a pipe handle in the client it won't mean anything to the server when you pass it back. You can make this work using the DuplicateHandle function, but it's much easier to create all the handles in the server and inherit them in the client.
The key point is that handles are per-process, not system-wide.
Starting child process do not forget to set UseShellExecute = false or the handle will not be inherited.
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.