5

I've been reading about how to check for all listening server on a specific port on LAN and finally I've wrote some code that it works as I want.
I'm using System.Threading.Tasks.Parallel to connect to all 254 IP Addresses as fast as possible
ex : 192.168.1.1 - 192.168.1.254

What I need is to set a timeout for these connection attempts, because it takes about 15-20 seconds to print : "Connection Failed " when it fails .. so How do I do that?

Here's Client Code:

static void Main(string[] args)
    {
        Console.WriteLine("Connecting to IP addresses has started. \n");

        Parallel.For(1, 255, i =>
        {
            Connect("192.168.1." + i);
        });
        Console.ReadLine();
    }

    private static void Connect(string ipAdd)
    {
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();
        IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(ipAdd), 9990);
        e.RemoteEndPoint = ipEnd;
        e.UserToken = s;
        e.Completed += new EventHandler<SocketAsyncEventArgs>(e_Completed);
        Console.WriteLine("Trying to connect to : " + ipEnd);
        s.ConnectAsync(e);
    }
    private static void e_Completed(object sender, SocketAsyncEventArgs e)
    {
        if (e.ConnectSocket != null)
        {
            StreamReader sr = new StreamReader(new NetworkStream(e.ConnectSocket));
            Console.WriteLine("Connection Established : " + e.RemoteEndPoint + " PC NAME : " + sr.ReadLine());
        }
        else
        {
            Console.WriteLine("Connection Failed : " + e.RemoteEndPoint);
        }
    }

Server Code:

static void Main(string[] args)
    {
        Socket server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint ep = new IPEndPoint(IPAddress.Any,9990);
        server.Bind(ep);
        server.Listen(100);
        Socket client = server.Accept();
        NetworkStream stream = new NetworkStream(client);
        StreamWriter sw = new StreamWriter(stream)
        sw.WriteLine(System.Environment.MachineName);
        sw.Flush();
        sw.Dispose();
        stream.Dispose();
        client.Dispose();
        server.Dispose();
    }

If there's any hint or notice that makes it better please tell me. I'm using [.Net 4.0] Sockets TCP.

Chenmunka
  • 685
  • 4
  • 21
  • 25
Murhaf Sousli
  • 12,622
  • 20
  • 119
  • 185
  • 1
    15-20 seconds seems reasonable for one connect attempt and very good for 254 attempts. My browser spends longer than that in 'spinning arrow' mode if the server is down. – Martin James Mar 16 '12 at 08:24
  • Actually, 20 seconds is far too short if the link has a dial-up modem. Luckily, such horrors have nearly all died out. – Martin James Mar 16 '12 at 08:27
  • 1
    @MartinJames it's a local area network as i mentioned .. most games and application that shows hosts available .. give the result in less than 10 seconds. like [pc anywhere, netsupport, half-life ..etc] i know they use better algorithm and ways to get that .. but i need it in a short time – Murhaf Sousli Mar 16 '12 at 08:32
  • 1
    I don't see a whole lot wrong with your English :) – Mukus Nov 18 '12 at 18:59

2 Answers2

2

I've figured out a solution.
first add all created sockets to a list of type SocketAsyncEventArgs or type ofSocket or
then use System.Timers.Timer to close all pending connection and the connected one after when the timer ticks which is after 5 seconds. (timer.Interval = 5000).

Client Code:

   //I've changed my console application to Winform
   public ServerDiscovery()
    {
        InitializeComponent();
        timer.Elapsed += timer_tick;
    }

    System.Timers.Timer timer = new System.Timers.Timer(5000);
    List<SocketAsyncEventArgs> list = new List<SocketAsyncEventArgs>();

    private void btnRefresh_Click(object sender, EventArgs e)
    {
        timer.Start();
        Parallel.For(1, 255, (i, loopState) =>
        {
            ConnectTo("192.168.1." + i);
        });
    }

    private void ConnectTo(string ipAdd)
    {
        Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        SocketAsyncEventArgs e = new SocketAsyncEventArgs();
        e.RemoteEndPoint = new IPEndPoint(IPAddress.Parse(ipAdd), 9990);
        e.UserToken = s;
        e.Completed += new EventHandler<SocketAsyncEventArgs>(e_Completed);
        list.Add(e);      // Add to a list so we dispose all the sockets when the timer ticks.
        s.ConnectAsync(e);
    }

    private void e_Completed(object sender, SocketAsyncEventArgs e)
    {
        if (e.ConnectSocket != null)     //if there's a connection Add its info to a listview
        {
            StreamReader sr = new StreamReader(new NetworkStream(e.ConnectSocket));
            ListViewItem item = new ListViewItem();
            item.Text = sr.ReadLine();
            item.SubItems.Add(((IPEndPoint)e.RemoteEndPoint).Address.ToString());
            item.SubItems.Add("Online");
            AddServer(item);
        }
    }

    delegate void AddItem(ListViewItem item);
    private void AddServer(ListViewItem item)
    {
        if (InvokeRequired)
        {
            Invoke(new AddItem(AddServer), item);
            return;
        }
        listServer.Items.Add(item);
    }

    private void timer_tick(object sender, EventArgs e)
    {
        timer.Stop();
        foreach (var s in list)
        {
            ((Socket)s.UserToken).Dispose();     //disposing all sockets that's pending or connected.
        }
    }

enter image description here

Murhaf Sousli
  • 12,622
  • 20
  • 119
  • 185
1

I couldn't find any built-in timeout mechanism. You should set a timer and abort the connections when it triggers. Something similar to this: How to configure socket connect timeout

Community
  • 1
  • 1
zmbq
  • 38,013
  • 14
  • 101
  • 171
  • 1
    I'm not sure about that.. Creating an external timer like creating a new thread.. it doesn't seem good to create 254 timers – Murhaf Sousli Mar 16 '12 at 06:33
  • 1
    maybe i would set one timer for all them .. but i still prefer a better idea – Murhaf Sousli Mar 16 '12 at 06:35
  • 2
    You only need one timer, because it's the same timeout for all connections. – zmbq Mar 16 '12 at 07:47
  • 1
    @MurhafSousli `Timer`s do not run on their own threads. See this answer to a question I asked a while back: http://stackoverflow.com/a/31833/3279 – Ben Collins Aug 11 '15 at 12:56
  • @ZX9 - in this question they are. If you have another question, you should post it as a question. – zmbq Oct 20 '16 at 06:39