I have created a class that acts as a plugin for another application. It should hold functions to use in the main application. It works in general - that means i can handle usual functions like calculations and even reading files. But i have problems implementing a socket class. I know how to work with sockets in general but in this case i have a problem.
As you may see in the code, there is an internal class SockAttrib that should manage the socket creation, the listening and also the messages. Received messages are stored in a dictionary.
public class Net : Module {
private static ReadOnlyCollection<CustomMethod> customMethods;
internal class SockAttrib {
public Socket listener;
public Socket handler;
/* create the listener */
public SockAttrib(int port) {
IPHostEntry host = Dns.GetHostEntry("localhost");
IPAddress ipAddress = host.AddressList[1];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
try {
listener = new Socket(ipAddress.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
listener.Bind(localEndPoint);
listener.Listen(10);
handler = listener.Accept();
} catch (Exception e) { Console.WriteLine("socket() " + e); }
}
/* listen */
public SockAttrib() {
try {
// Incoming data from the client.
string data = "";
byte[] bytes = null;
while (true) {
bytes = new byte[1024];
int bytesRec = handler.Receive(bytes);
data += Encoding.ASCII.GetString(bytes, 0, bytesRec);
if (data.IndexOf("<EOF>") > -1)
{
messages[++idm] = data;
//return;
}
}
}
catch (Exception e) { Console.WriteLine("listen() "+e); }
}
public void Close() {
handler.Close();
}
}
/* message index */
private static int idm = 0;
/* list of messages */
private static Dictionary<int, String> messages = new Dictionary<int, String>();
public Net() {
if (customMethods != null) return;
List<CustomMethod> moduleMethods = new List<CustomMethod>();
moduleMethods.Add(new CustomMethod(typeof(int), "socket", typeof(int)));
moduleMethods.Add(new CustomMethod(typeof(int), "listen" ));
moduleMethods.Add(new CustomMethod(typeof(string), "sockread"));
customMethods = moduleMethods.AsReadOnly();
}
public ReadOnlyCollection<CustomMethod> Prototypes {
get { return customMethods; }
}
public object OnMethodInvoke(String functionName, List<object> parameters) {
if( functionName == "socket") {
int port = (int)parameters[0];
SockAttrib sa = new SockAttrib( port );
return 1;
}
if (functionName == "listen") {
SockAttrib sa = new SockAttrib();
return 1;
}
if (functionName == "sockread") {
if (idm > 0) {
String message = messages[--idm];
return message;
} else {
return "nope";
}
}
return false;
}
}
My problem is the handler. The creation of the socket works but as soon as i connect to the socket using netcat the socket stop listening and i dont get any responses. I hope its not too much code and it should also be easy readable.
Finally the module gets exported as a library (dll) so i cant really give a minimal working example without also posting the module handler.
Though your requirements are still a bit fuzzy, I will give it a try.
First I would recommend to create a class containing the core functionality of your TCP server. This makes it easier to unit-test your code and to adapt it to changing requirements.
/// <summary>
/// This class encapsulates the TCP server
/// </summary>
public class Server : IDisposable
{
private static TcpListener _listener;
private static TcpClient _client;
private static NetworkStream _stream;
private static byte[] _buffer;
private static readonly StringBuilder _receivedText = new StringBuilder();
private const string EOF = "<EOF>";
/// <summary>
/// Starts listening on the specified port
/// </summary>
/// <param name="port">The port number</param>
public Server(int port)
{
_listener = new TcpListener(IPAddress.Any, port);
_listener.Start();
_listener.BeginAcceptTcpClient(Accepted, null);
}
public void Dispose()
{
if (_client != null)
{
_client.Dispose();
}
if (_listener != null)
{
_listener.Stop();
}
}
/// <summary>
/// Returns any text that has been sent via TCP to the port specified in the constructor.
/// </summary>
/// <returns>The received text, or null if no (complete) text has been received yet.</returns>
/// <remarks>
/// The method returns the next text delimited by "<EOF>".
/// </remarks>
public string Read()
{
lock (_receivedText)
{
var receivedText = _receivedText.ToString();
var eofIndex = receivedText.IndexOf(EOF);
if (eofIndex < 0)
return null; // no (complete) text has been received yet
var result = receivedText.Substring(0, eofIndex);
_receivedText.Remove(0, eofIndex + EOF.Length);
return result;
}
}
// called whenever a client has connected to our server.
private static void Accepted(IAsyncResult ar)
{
_client = _listener.EndAcceptTcpClient(ar);
_stream = _client.GetStream();
_buffer = new byte[4096];
_stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
}
// called whenever data has arrived or if the client closed the TCP connection
private static void Read(IAsyncResult ar)
{
var bytesReceived = _stream.EndRead(ar);
if (bytesReceived == 0)
{
// TCP connection closed
_client.Close();
_client = null;
_stream.Dispose();
_stream = null;
// prepare for accepting the next TCP connection
_listener.BeginAcceptTcpClient(Accepted, null);
return;
}
lock (_receivedText)
{
_receivedText.Append(Encoding.ASCII.GetString(_buffer, 0, bytesReceived));
}
// prepare for reading more
_stream.BeginRead(_buffer, 0, _buffer.Length, Read, null);
}
}
Integrating this into your Net class should then be fairly simple:
// static or not? Depends on your "Module plugin" architecture
private static Server _server;
public object OnMethodInvoke(String functionName, List<object> parameters)
{
if (functionName == "socket")
{
if (_server != null)
{
// oops, already open
return 0;
}
int port = (int)parameters[0];
_server = new Server(port);
return 1;
}
if (functionName == "sockread")
{
if (_server != null)
{
return _server.Read() ?? "nope";
}
else
{
return "nope";
}
}
return false;
}
Related
I´m doing multiplayer and my Unity console writes this error "Cannot access a disposed object.
Object name: 'System.Net.Sockets.UdpClient'." when I close the server. I don't know what to do with it... Can anyone please advise me how to fix this?
This is my Server.cs file:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using UnityEngine;
public class Server
{
public static int MaxPlayers { get; private set; }
public static int Port { get; private set; }
public static Dictionary<int, Client> clients = new Dictionary<int, Client>();
public delegate void PacketHandler(int _fromClient, Packet _packet);
public static Dictionary<int, PacketHandler> packetHandlers;
private static TcpListener tcpListener;
private static UdpClient udpListener;
/// <summary>Starts the server.</summary>
/// <param name="_maxPlayers">The maximum players that can be connected simultaneously.</param>
/// <param name="_port">The port to start the server on.</param>
public static void Start(int _maxPlayers, int _port)
{
MaxPlayers = _maxPlayers;
Port = _port;
Debug.Log("Starting server...");
InitializeServerData();
tcpListener = new TcpListener(IPAddress.Any, Port);
tcpListener.Start();
tcpListener.BeginAcceptTcpClient(TCPConnectCallback, null);
udpListener = new UdpClient(Port);
udpListener.BeginReceive(UDPReceiveCallback, null);
Debug.Log($"Server started on port {Port}.");
}
/// <summary>Handles new TCP connections.</summary>
private static void TCPConnectCallback(IAsyncResult _result)
{
TcpClient _client = tcpListener.EndAcceptTcpClient(_result);
tcpListener.BeginAcceptTcpClient(TCPConnectCallback, null);
Debug.Log($"Incoming connection from {_client.Client.RemoteEndPoint}...");
for (int i = 1; i <= MaxPlayers; i++)
{
if (clients[i].tcp.socket == null)
{
clients[i].tcp.Connect(_client);
return;
}
}
Debug.Log($"{_client.Client.RemoteEndPoint} failed to connect: Server full!");
}
/// <summary>Receives incoming UDP data.</summary>
private static void UDPReceiveCallback(IAsyncResult _result)
{
try
{
IPEndPoint _clientEndPoint = new IPEndPoint(IPAddress.Any, 0);
byte[] _data = udpListener.EndReceive(_result, ref _clientEndPoint);
udpListener.BeginReceive(UDPReceiveCallback, null);
if (_data.Length < 4)
{
return;
}
using (Packet _packet = new Packet(_data))
{
int _clientId = _packet.ReadInt();
if (_clientId == 0)
{
return;
}
if (clients[_clientId].udp.endPoint == null)
{
// If this is a new connection
clients[_clientId].udp.Connect(_clientEndPoint);
return;
}
if (clients[_clientId].udp.endPoint.ToString() == _clientEndPoint.ToString())
{
// Ensures that the client is not being impersonated by another by sending a false clientID
clients[_clientId].udp.HandleData(_packet);
}
}
}
catch (Exception _ex)
{
Debug.Log($"Error receiving UDP data: {_ex}");
}
}
/// <summary>Sends a packet to the specified endpoint via UDP.</summary>
/// <param name="_clientEndPoint">The endpoint to send the packet to.</param>
/// <param name="_packet">The packet to send.</param>
public static void SendUDPData(IPEndPoint _clientEndPoint, Packet _packet)
{
try
{
if (_clientEndPoint != null)
{
udpListener.BeginSend(_packet.ToArray(), _packet.Length(), _clientEndPoint, null, null);
}
}
catch (Exception _ex)
{
Debug.Log($"Error sending data to {_clientEndPoint} via UDP: {_ex}");
}
}
/// <summary>Initializes all necessary server data.</summary>
private static void InitializeServerData()
{
for (int i = 1; i <= MaxPlayers; i++)
{
clients.Add(i, new Client(i));
}
packetHandlers = new Dictionary<int, PacketHandler>()
{
{ (int)ClientPackets.welcomeReceived, ServerHandle.WelcomeReceived },
{ (int)ClientPackets.playerMovement, ServerHandle.PlayerMovement },
{ (int)ClientPackets.playerShoot, ServerHandle.PlayerShoot },
{ (int)ClientPackets.playerThrowItem, ServerHandle.PlayerThrowItem }
};
Debug.Log("Initialized packets.");
}
public static void Stop()
{
tcpListener.Stop();
udpListener.Close();
}
}
A tutorial according to which I make a multiplayer game:
https://www.youtube.com/playlist?list=PLXkn83W0QkfnqsK8I0RAz5AbUxfg3bOQ5
It's because the garbage collector has disposed the udpClient and you are trying to call the Stop() method in a disposed object.
So , try replacing this method instead of your Stop method
public static void Stop()
{
try
{
tcpListener.Stop();
udpListener.Close();
}
catch {}
}
My udp class is not receiving the updated value from the stream. Device sends data continuously and when any alarm activated it add the code in stream and sends the updated value but my class is not receiving the updated value unless I restart the program.
here is my UDPListener class.
public class
{
public static int PORT_NUMBER { get; set; }
public string IpAddress { get; set; }
private readonly UdpClient udp;
public event Action<object, EventArgs> msgChanged;
IAsyncResult ar_ = null;
public UDPListener(string ipaddr, int port)
{
IpAddress = ipaddr;
PORT_NUMBER = port;
udp = new UdpClient(PORT_NUMBER);
Start();
}
public void Start()
{
StartListening();
}
public void Stop()
{
try
{
udp.Close();
}
catch { /* not necessary */ }
}
private void StartListening()
{
ar_ = udp.BeginReceive(Receive, new object());
}
private void Receive(IAsyncResult ar)
{
try
{
Thread.Sleep(150);
IPEndPoint ip = new IPEndPoint(IPAddress.Parse(IpAddress), PORT_NUMBER);
byte[] bytes = udp.EndReceive(ar, ref ip);
string message = Encoding.ASCII.GetString(bytes);
//raise event..
if (message.StartsWith("S"))
if (msgChanged != null)
msgChanged(message, EventArgs.Empty);
}
catch (Exception ex)
{
Debug.WriteLine("Error in UDPListner..." + ex.Message);
}
finally
{
StartListening();
}
}
}
Now what is happening when the program starts it will receive data "S0000.." but when alarm raises data changes to "S8000..etc" but this class continuously receiving the same "S000.." data unless I restart the class.
When I run the udp listener in while loop its works perfectly fine, it receives the updated stream and changes when alarm goes off.
here is the code for while loop udp.
while (!StopRunning)
{
Thread.Sleep(150);
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
var ep = default(IPEndPoint);
var data = udp.Receive(ref ep);
udp.Close();
string msg = Encoding.ASCII.GetString(data);
if (msgChanged != null)
msgChanged(msg, EventArgs.Empty);
}
But I cannot make use of while loop because I have to fit this program in window service.
The main difference in your UDPListener and while loop is that in loop you are creating udp variable each time you are connecting to the UDP:
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
In Receive(IAsyncResult ar) you only connect with the same client, so you still have the same data.
I think that you can rewrite your class something like this:
private void StartListening()
{
udp = new UdpClient(PORT_NUMBER, AddressFamily.InterNetwork);
ar_ = udp.BeginReceive(Receive, new object());
}
Make sure that you're disposing the udp connection after receive with Close() method:
byte[] bytes = udp.EndReceive(ar, ref ip);
udp.Close();
For the last 2 weeks I've been trying to solve a problem, apparently unsuccessfully.
I have a server, using TcpListener class, and a client, using TcpClient class.
My tests were using 2 computers in my internal home network (level 1 router).
Trying to connect, client-computer got "connection request timeout" exception.
What I've already tried:
Running the client and the server on the same computer - worked
Listening on 0.0.0.0 port 6666 (check with netstat to make sure that the server is listening)
Disable both client and server firewalls
Trying to connect my remote application with telnet (to reject "client code bug" possibilty)
Installing telnet on both computers, and connecting successfully.
Still my server does not seem to get any connection. Client computer is Windows XP and server computer is Windows 7.
Please help, I'm really stuck on this one.
Edit - Server code ->
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Utils
{
public class ClientConnection
{
public ClientConnection(IPHostEntry hostName, TcpClient client)
{
HostName = hostName;
Client = client;
}
public IPHostEntry HostName { get; set; }
public TcpClient Client { get; set; }
}
public class SocketServer : IDisposable
{
private const int DefaultRecieveSize = 1024;
protected const int ReceiveTimeoutBeforeQuiting = 5000;
private readonly Encoding _defaultEncoding = Encoding.ASCII;
private readonly TcpListener _socket;
protected Dictionary<IPAddress, ClientConnection> Clients;
private static object _locker;
private readonly AutoResetEvent _listeningStopEvent;
private readonly Thread _listeningThread;
private readonly AutoResetEvent _acceptNewClientEvent;
public delegate void ClientsListChangedEvent();
public event ClientsListChangedEvent ClientsChangedEvent;
private Timer _refreshClientsTimer;
private static int _updateClientListInterval;
#region Ctors
protected SocketServer(int port)
{
_socket = new TcpListener(IPAddress.Parse("0.0.0.0"),port);
Clients = new Dictionary<IPAddress, ClientConnection>();
_updateClientListInterval = Convert.ToInt32(ConfigurationManager.AppSettings["UpdateClientListInterval"]);
_listeningThread = new Thread(AcceptNewConnections);
_listeningStopEvent = new AutoResetEvent(false);
_acceptNewClientEvent = new AutoResetEvent(false);
_locker = new object();
_socket.Start();
}
#endregion
#region Send methods
protected void SendString(IPAddress client, string data)
{
// implementation here
}
#endregion
#region Receieve methods
protected MemoryStream GetInputDataAsMemoryStream(IPAddress client)
{
// implementation here
}
protected string GetInputDataAsMemoryString(IPAddress client)
{
// implementation here
}
protected string GetInputDataAsMemoryString(IPAddress client, int timeout)
{
// implementation here
}
#endregion
#region Listening methons
public void StopListening()
{
_listeningStopEvent.Set();
// todo : check if works
_refreshClientsTimer.Dispose();
}
public bool StartListening()
{
if (_listeningThread.ThreadState == ThreadState.Unstarted)
{
_listeningThread.Start();
// start refreshing clients list too. update clients list every 10 seconds
_refreshClientsTimer = new Timer(UpdateList, null, 0, _updateClientListInterval * 1000);
return true;
}
return false;
}
private void AddClientCallback(IAsyncResult ar)
{
// get all needed data before entering lock
if (_socket != null)
{
TcpClient client;
try
{
client = _socket.EndAcceptTcpClient(ar);
}
catch (ObjectDisposedException)
{
// probebly server has been closed. exit
return;
}
var clientEndPoint = client.Client.RemoteEndPoint;
var ip = SocketFunctions.ExtractIpFromEndPoint(clientEndPoint);
IPHostEntry hostName;
try
{
hostName = SocketFunctions.ExtractHostnameFromEndPoint(clientEndPoint);
}
catch(SocketException)
{
// error while parsing from ip to host name. Put default
hostName = new IPHostEntry();
}
// check if given ip already connected
if (!Clients.ContainsKey(ip))
{
lock (_locker)
{
Clients.Add(ip, new ClientConnection(hostName, client));
}
// notify that a new clients has been added
ClientsChangedEvent.Invoke();
}
}
// a new client has accepted
_acceptNewClientEvent.Set();
}
private void AcceptNewConnections(object obj)
{
var shouldStop = false;
while (!shouldStop)
{
_socket.BeginAcceptTcpClient(AddClientCallback, null);
AutoResetEvent[] events = { _listeningStopEvent, _acceptNewClientEvent };
var result = WaitHandle.WaitAny(events);
if (result == 0)
{
shouldStop = true;
}
}
}
#endregion
public List<KeyValuePair<string, string>> GetConnectedClients()
{
var connectedClients = new List<KeyValuePair<string, string>>();
lock (_locker)
{
foreach (var client in Clients)
{
// get only connected clients. Don`t remove - next Timer round will do it anyway and I don`t want to lock _clients
if (client.Value.Client.Connected)
{
connectedClients.Add(new KeyValuePair<string, string>(client.Key.ToString(), client.Value.HostName.HostName));
}
}
}
return connectedClients;
}
private static void CheckConnectionStatus(TcpClient client)
{
if (client.Client.Poll(0, SelectMode.SelectRead))
{
var checkConn = new byte[1];
checkConn[0] = 0;
try
{
if (client.Client.Receive(checkConn, SocketFlags.Peek) == 0)
{
throw new IOException();
}
}
catch (SocketException)
{
// for me, both exceptions mean that there was a conncetion error. Throw IOException for this case too
throw new IOException();
}
}
}
public void UpdateList(object obj)
{
// iterate each client and check its state. Remove if not connected
var tempClients = new Dictionary<IPAddress, ClientConnection>(Clients);
var changedOccurred = false;
lock (_locker)
{
foreach (var client in Clients)
{
try
{
CheckConnectionStatus(client.Value.Client);
}
catch (IOException)
{
// client is not connected anymore
tempClients.Remove(client.Key);
changedOccurred = true;
}
}
}
if (changedOccurred)
{
lock (_locker)
{
Clients = tempClients;
}
// notify that a new clients has been added
ClientsChangedEvent.Invoke();
}
}
#region IDisposble
public void Dispose()
{
if (_socket != null)
{
StopListening();
_socket.Stop();
}
}
#endregion
}
}
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.
I'm making a game in C# and I want to display the progress (movements and so on) of opponent. So I send events in game via TCP protocol to opponent.
I've already tried my application on localhost and it works but when I try to use my external address in order to communicate over the internet I get the error below in class TcpInformer.Connect():
a connection attempt failed because the connected party did not properly respond after a
period of time, or established connection failed because connected host has failed to
respond (my external IP address):(port)
I thought the problem was that I was behind NAT. But I've already set up portforwarding for port 49731 on IP 10.0.0.1 and nothing changed.
My second guess was Windows firewall but even when I stopped the firewall my app didn't start working.
My code for connecting of the two PCs is:
TcpInformer peer;
TcpHost server;
public void PrepareConnection() // for server (host)
{
playerType = PlayerType.One;
server = new TcpHost(form, this);
server.Start("10.0.0.1", 49731);
}
public void PrepareConnection2() // for client
{
playerType = PlayerType.Two;
peer = new TcpInformer(form, this);
peer.Connect("MY EXTERNAL IP", 49731);
}
// classes TcpHost and TcpInformer
public interface ITcpCommunication
{
#region Operations (3)
void ReadData();
void SendData(byte[] message);
void SendData(byte[] message, int size);
#endregion Operations
}
public class TcpInformer : ITcpCommunication
{
#region Fields (9)
private NetworkStream con_ns;
private TcpClient con_server;
private bool connected;
private Fmain form;
private SecondPlayer player;
private int port;
private string server;
private string stringData;
#endregion Fields
#region Delegates and Events (1)
// Events (1)
public event SimulationEventHandler ReadEvent;
#endregion Delegates and Events
#region Constructors (1)
public TcpInformer(Fmain form, SecondPlayer player)
{
this.form = form;
connected = false;
this.player = player;
}
#endregion Constructors
#region Methods (6)
// Public Methods (5)
///
///
///
/// e.g., server = "127.0.0.1"
/// e.g., port = 9050
public void Connect(string server, int port)
{
this.port = port;
this.server = server;
connected = true;
try
{
con_server = new TcpClient(this.server, this.port);
}
catch (SocketException ex)
{
connected = false;
MessageBox.Show("Unable to connect to server" + ex.Message);
return;
}
con_ns = con_server.GetStream();
}
public void Disconnect()
{
form.Debug("Disconnecting from server...", "Player2Net");
con_ns.Close();
con_server.Close();
}
public void ReadData()
{
if (con_ns != null)
{
if (con_ns.DataAvailable)
{
byte[] data = new byte[1200];
int received = con_ns.Read(data, 0, data.Length);
player.ProcessReceivedData(data, received);
}
}
else
{
form.Debug("Warning: con_ns is not inicialized.","player2");
}
}
public void SendData(byte[] message)
{
con_ns.Write(message, 0, message.Length);
con_ns.Flush();
}
public void SendData(byte[] message, int size)
{
if (con_ns != null)
{
con_ns.Write(message, 0, size);
}
}
// Private Methods (1)
private void Debug(string message)
{
form.Debug("Connected to: " + server + "port: " + port.ToString() + ": " + message, "Player2Net");
}
#endregion Methods
}
public class TcpHost : ITcpCommunication
{
#region Fields (9)
private ASCIIEncoding enc;
private Fmain form;
private TcpListener listener;
private SecondPlayer player;
private int port;
private Socket s;
private string server;
private bool state;
#endregion Fields
#region Delegates and Events (1)
// Events (1)
public event SimulationEventHandler ReadEvent;
#endregion Delegates and Events
#region Constructors (1)
public TcpHost(Fmain form, SecondPlayer player)
{
this.player = player;
this.form = form;
state = false;
enc = new ASCIIEncoding();
}
#endregion Constructors
#region Methods (5)
// Public Methods (5)
public void Close()
{
state = false;
s.Close();
listener.Stop();
}
public void ReadData()
{
if (state == true)
{
if (s.Available > 0) // if there's any data
{
byte[] data = new byte[1200];
int received = s.Receive(data);
player.ProcessReceivedData(data, received);
}
}
}
public void SendData(byte[] message)
{
if (state == true)
{
s.Send(message);
}
}
public void SendData(byte[] message, int size)
{
if (state == true)
{
s.Send(message, size, SocketFlags.None);
}
}
public void Start(string p_ipAddress, int listenPort)
{
//IPAddress ipAddress = IPAddress.Loopback
IPAddress ipAddress = IPAddress.Parse(p_ipAddress);
IPEndPoint ipLocalEndPoint = new IPEndPoint(ipAddress, listenPort);
//listener = new TcpListener(ipAddress, listenPort);
listener = new TcpListener(ipLocalEndPoint);
server = "[provider]";
port = listenPort;
listener.Start();
form.Debug("Server is running", "Player1Net");
form.Debug("Listening on port " + listenPort, "Player1Net");
form.Debug("Waiting for connections...", "Player1Net");
s = listener.AcceptSocket();
form.Debug("Connection accepted from " + s.RemoteEndPoint, "Player1Net");
state = true;
}
#endregion Methods
}
Is there a way how to check what is wrong?
Help is much appreciated!
I found out what was the problem. I was listening on 10.0.0.1 and trying to reach my external IP (second instance of my program) which is impossible on a computer with one connection to the internet.
I also faced the same problem in AWS VPN.
I changed the proxy.company.com (external ip) to 10.0.0.5.
And it works now.