4

I am using sockets in c# in order to send data from server to client machine. In fact I have created a capturer which capature kinect stream from 3 different machine. I want to generate a server and client communication in order to send signal from server to the rest devices in order to begin and stop the recording process. I want with left click to send a message to begin recording and with right click to stop recording. My code is the following:

public Form1()
{
    InitializeComponent();
    this.MouseClick += mouseClick1;


    Thread thread = new Thread(() => StartServer(message));
    thread.Start();  // server is begining
}

private void mouseClick1(object sender, MouseEventArgs e)
{

    if (e.Button == MouseButtons.Left)
    {
       message = "start";  
        try
        {
                obj = new Capturer(dirPath + name + "_" + surname, 20);
        }
        catch (Exception e1)
        {
            Console.WriteLine("The process failed: {0}", e1.ToString());
        }
        if (clientSockets.Count > 0)
        {
            Thread.Sleep(10);
            foreach (Socket socket1 in clientSockets)
                socket1.Send(Encoding.ASCII.GetBytes(message)); //send everything to all clients as bytes
        }
        else
        {

            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
        }

    }
    else if (e.Button == MouseButtons.Right)
    {
        message = "stop";
        obj.flag2 = true;

        if (clientSockets.Count > 0)
        {
            Thread.Sleep(10);
            foreach (Socket socket1 in clientSockets)
                socket1.Send(Encoding.ASCII.GetBytes(message)); //send everything to all clients as bytes
        }
        else
        {

            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
        }
    }
}

public void StartServer(String message) {

    serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    serverSocket.Bind(new IPEndPoint(IPAddress.Any, PORT_NO));
    serverSocket.Listen(4); //the maximum pending client, define as you wish
    serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null);
    string result = "";
    do
    {
        result ="asdf";
    } while (result.ToLower().Trim() != "exit");
}
private const int BUFFER_SIZE = 4096;
private static byte[] buffer = new byte[BUFFER_SIZE]; //buffer size is limited to BUFFER_SIZE per message
private static List<Socket> clientSockets = new List<Socket>(); //may be needed by you 
private static void acceptCallback(IAsyncResult result)
{ //if the buffer is old, then there might already be something there...
    Socket socket = null;
    try
    {

        socket = serverSocket.EndAccept(result); // The objectDisposedException will come here... thus, it is to be expected!
        //Do something as you see it needs on client acceptance
        clientSockets.Add(socket);
        socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);


        string msg = "start";
        //socket.Send(Encoding.ASCII.GetBytes(msg));

        if (clientSockets.Count > 0)
        {
            Thread.Sleep(10);
            foreach (Socket socket1 in clientSockets)
                socket1.Send(Encoding.ASCII.GetBytes(msg)); //send everything to all clients as bytes
        }
        else
        {

            serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); //to receive another client
        }
    }
    catch (Exception e)
    { // this exception will happen when "this" is be disposed...        
        //Do something here             
        Console.WriteLine(e.ToString());
    }
}

const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0; //this is not fool proof, obviously, since actually you must have multiple of this for multiple clients, but for the sake of simplicity I put this
private static void receiveCallback(IAsyncResult result)
{
    Socket socket = null;
    try
    {
        socket = (Socket)result.AsyncState; //this is to get the sender
        if (socket.Connected)
        { //simple checking
            int received = socket.EndReceive(result);
            if (received > 0)
            {
                byte[] data = new byte[received]; //the data is in the byte[] format, not string!
                Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to http://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
                //DO SOMETHING ON THE DATA int byte[]!! Yihaa!!
                Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else                     

                //Message retrieval part
                //Suppose you only want to declare that you receive data from a client to that client
                string msg = "I receive your message on: " + DateTime.Now;
                socket.Send(Encoding.ASCII.GetBytes(msg)); //Note that you actually send data in byte[]
                Console.WriteLine("I sent this message to the client: " + msg);

                receiveAttempt = 0; //reset receive attempt
                //socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
            }
            else if (receiveAttempt < MAX_RECEIVE_ATTEMPT)
            { //fail but not exceeding max attempt, repeats
                ++receiveAttempt; //increase receive attempt;
                socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket); //repeat beginReceive
            }
            else
            { //completely fails!
                Console.WriteLine("receiveCallback fails!"); //don't repeat beginReceive
                receiveAttempt = 0; //reset this for the next connection
            }
        }
    }
    catch (Exception e)
    { // this exception will happen when "this" is be disposed...
        Console.WriteLine("receiveCallback fails with exception! " + e.ToString());
    }
}

How can I parse the flag into the server, in order to send that value in the clients? Is it possible to change here the static type of server functions? As it is now the code begin the server which send just the string "start" to the clients. How can I send the string message of a bool flag? My issue lies in the static type of my callback functions. Is it possible to add as an argument the message for example in the AsyncCallBack:

serverSocket.BeginAccept(new AsyncCallback(acceptCallback), null); 
Ian
  • 30,182
  • 19
  • 69
  • 107
Jose Ramon
  • 5,572
  • 25
  • 76
  • 152
  • Hey, now you are using `Async` pretty well. ;) great! – Ian Jan 13 '16 at 13:33
  • :P I had such a good teacher! @Ian is it feasibe to add an argument inside the AsyncCallback? – Jose Ramon Jan 13 '16 at 13:35
  • No no, I think you just catching fast. =D – Ian Jan 13 '16 at 13:35
  • I want to have an open thread with server which will send messages to the clients either to begin recording or stop recording, maybe and a string with the name of the recorded file. – Jose Ramon Jan 13 '16 at 13:37
  • Argument to the Async? Yes, instead of null you could use the last one as argument. – Ian Jan 13 '16 at 13:38
  • It seems like you do multi-threading too... hmm... it is not going to be as straight forward now... – Ian Jan 13 '16 at 13:39
  • It cant actually work without the multi-threading, since I want at the same time to perform other actions (recording streams). – Jose Ramon Jan 13 '16 at 13:41
  • Thus, if instead of null put a string in the definition of the acceptCallback I have to also add a second argument except IAsyncResult, right? – Jose Ramon Jan 13 '16 at 13:43
  • I tried to do something like that -> string msg = result.AsyncState.ToString(); – Jose Ramon Jan 13 '16 at 13:49
  • Yes, you are on the track actually. I put up another long answer for you to have some ideas. ;) – Ian Jan 13 '16 at 13:52

1 Answers1

2

Firstly, about the MouseClick event. Since you are having exclusive qualification for the event (that is, one left click and another right click), you could combine them both into a single event

public Form1()
{
    InitializeComponent();
    this.MouseClick += mouseClick1; //one event is enough

    Thread thread = new Thread(() => StartServer(message));
    thread.Start();  // server is begining
}

private void mouseClick1(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
        try
        {
            obj = new Capturer(dirPath + name + "_" + surname, 20); //captures the kinect streams
        }
        catch (Exception e1)
        {
            Console.WriteLine("The process failed: {0}", e1.ToString());
        }
    } 
    else if (e.Button == MouseButtons.Right)
    {
        obj.flag2 = true; // flag that handle the recording, true value stops the recording, possible I want that value to be send to the client in order the same thing happen.
    }
}

And it is going to be ok.

Next, answering your questions:

Q: How can I parse the flag into the server, in order to send that value in the clients?

A: in your mouseClick1 event, simply use sync send which you do in your accept callback, change the msg with something else (say byte[] val = new byte[] { 1 };)

foreach (Socket socket1 in clientSockets) //Do this in your `if (e.Button == Mouse.Left and Mouse.Right) blocks
    socket1.Send(Encoding.ASCII.GetBytes(msg));

Q: Is it possible to change here the static type of server functions?

A: Yes, definitely! it is windows form, you do not need to use static type at all! Unlike the Console App in your previous question. I would even suggest you to make nothing static whenever possible

Q: As it is now the code begin the server which send just the string "start" to the clients. How can I send the string message of a bool flag?

A: since you use socket, you cannot really send bool flag so to speak. You will send byte[]. But you can always check in your client implementation. If the byte[] is of certain value, simply change it to bool. For instance, consider of sending just 1 or 0 from your server. Then in your client endReceiveCallback, you could simply check the data coming and see if it is 1 then it is true, and if it is 0 then it is false

Q: My issue lies in the static type of my callback functions. Is it possible to add as an argument the message for example in the AsyncCallBack

A: this is winform, you could get rid of all static var! And yes, just pass it as replacement of the null in your Begin callback. Then you pass the object too.

//Change null with something else
serverSocket.BeginAccept(new AsyncCallback(acceptCallback), myobj);

Then in your acceptCallback, take your object by using the IAsyncResult.AsyncState

Ian
  • 30,182
  • 19
  • 69
  • 107
  • 1
    Anyway, try to figure it out using the answer I posted. ;) it is not hard, really. Sorry, I might need to debug my code first, may not respond quickly to your subsequent comments. I currently have this issue: http://stackoverflow.com/questions/34767669/same-code-but-different-results-in-windows-form-and-in-windows-service-during-s – Ian Jan 13 '16 at 14:13
  • Yes, however client seems to accept one message at time. I mean if it doenst change with the second click. – Jose Ramon Jan 13 '16 at 15:51
  • I initialize a message variable and in accept callback I send it to the client. Furthermore i have the foreach code inside the buttons. Only the initialize variable is send to the client. – Jose Ramon Jan 13 '16 at 16:07
  • Client code should receive all messages properly right? – Jose Ramon Jan 13 '16 at 16:14
  • If I comment the foreach inside sever's accept call back, nothing is received from clients. Yes I add the code in mouseclick. I will edit the question. – Jose Ramon Jan 13 '16 at 16:16
  • Ok I have initialize the message to be an empty string "nothing" then with left click I change it to start and with right click of stop. However in the client side I got only nothing. I have remove the beginAccept from the mouseclick :D – Jose Ramon Jan 13 '16 at 16:25
  • I manage to enter to the right button code where the change is happerning and the for each is located. Thus this is an indication that the issue is on client, right? – Jose Ramon Jan 13 '16 at 16:41
  • In the client's receiveCallback I can enter just one time when the program begins to run. When I am clicking (from the server code) I dont enter again in the receiveCallback code. – Jose Ramon Jan 13 '16 at 16:45
  • Ok, this sounds like there is no `BeginReceive` in the receiveCallback. Is this the case? – Ian Jan 13 '16 at 16:48
  • Netherlands so there is a great difference! – Jose Ramon Jan 13 '16 at 16:55