I am using the following projects in order to create an asynchronous communication between server and client sockets. When I am running those projects I am sending a message from the client to the server thus I got the message:
Data : recording EOF, Sent 14 bytes to client.
What I want to achieve is to send a boolean variable from the server to the client with the sockets. Is it doable to do so, I am wondering since in the code I have the server which waiting and listens and the client which sends data, can I do the opposite? In general what I want is to send a boolean to several clients. Why am I need the End of File in order to send a string? Is it necessary to convert everything to string?
EDIT: In general what I want is to send a variable from one computer to two others in order a process to begin simultaneously in all computers. In fact to create a switcher that gives a signal to begin a process in 2-3 machines in the same time.
I tried to use the following code for server:
class Program
{
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static void Main(string[] args)
{
//---listen at the specified IP and port no.---
IPAddress localAdd = IPAddress.Parse(SERVER_IP);
TcpListener listener = new TcpListener(localAdd, PORT_NO);
Console.WriteLine("Listening...");
listener.Start();
//---incoming client connected---
TcpClient client = listener.AcceptTcpClient();
//---get the incoming data through a network stream---
NetworkStream nwStream = client.GetStream();
byte[] buffer = new byte[client.ReceiveBufferSize];
//---read incoming stream---
int bytesRead = nwStream.Read(buffer, 0, client.ReceiveBufferSize);
//---convert the data received into a string---
string dataReceived = Encoding.ASCII.GetString(buffer, 0, bytesRead);
Console.WriteLine("Received : " + dataReceived);
//---write back the text to the client---
Console.WriteLine("Sending back : " + dataReceived);
nwStream.Write(buffer, 0, bytesRead);
client.Close();
listener.Stop();
Console.ReadLine();
}
}
and for client:
class Program
{
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static void Main(string[] args)
{
//---data to send to the server---
string textToSend = DateTime.Now.ToString();
//---create a TCPClient object at the IP and port no.---
TcpClient client = new TcpClient(SERVER_IP, PORT_NO);
NetworkStream nwStream = client.GetStream();
byte[] bytesToSend = ASCIIEncoding.ASCII.GetBytes(textToSend);
//---send the text---
Console.WriteLine("Sending : " + textToSend);
nwStream.Write(bytesToSend, 0, bytesToSend.Length);
//---read back the text---
byte[] bytesToRead = new byte[client.ReceiveBufferSize];
int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
Console.WriteLine("Received : " + Encoding.ASCII.GetString(bytesToRead, 0, bytesRead));
Console.ReadLine();
client.Close();
}
}
Just in the case I am working in the same machine. I will have in total 4 machines and I want the one of them to give a singal to the rest of them to begin recording rgb stream. Thus the server should send signal to the clients to begin recording. What should I do to change the behavior of the server to send data and not listen. Is it possible to have several machines listening and waiting for a signal to be given?
EDIT:
private void mouseClick1(object sender, MouseEventArgs e)
{
Thread thread = new Thread(() => StartServer());
thread.Start();
if (e.Button == MouseButtons.Left)
{
button5.Enabled = false;
button3.Enabled = true;
try
{
obj = new Capturer();
}
catch (Exception e1)
{
Console.WriteLine("The process failed: {0}", e1.ToString());
}
}
}
private void mouseClick2(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
obj.flag2 = true;
}
}
My code as it is now with left click calls startServer() function with a new thread which is the main code in @Ians implementation and afterthat I call my object. When I click right click I change a flag and the capturer stops. How can I stop also the server or pause in order to open it again with the left click?
On page https://msdn.microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx see method
AcceptCallback
(it's called when client connects). There is lineSocket handler = listener.EndAccept(ar);
. This happens for each client when it connects, so keep these Socket instances to some list. When you want to send data to clients, useSend
method from that same example with each socket from list (or selectively if you want to send only to some clients).Answers to the questions first:
And here I present my solution to handle your (1) multiple clients, (2)
Async
connect & accept & receive, but having (3) send sync, as well as (4) conversion fromhex string
tobyte[]
(the structure & idea), and last but not least (5) working code with user input (for you to change this part) for testing!I would solve such problem using simple
Socket
class, since it is the solution I am most familiar with. But you could always do similarly if you use yourTcpListener.Server
(which is the underlying network ofSocket
class). And, as you desire, I would do that withASync
.There are several steps needed to achieve what you want in both your server and your client:
Server
Make your
Socket
as class field rather than method field, since you will use if everywhere and you need multiple methods to achieve what you want. And initialize it as soon as you started your main routine.Since the server will serve many clients, I will recommend you to use
ASync
rather thanSync
for the process. Initialize yourSocket
by usingBeginAccept
rather than usingAccept
, putacceptCallback
in yourBeginAccept
Define
acceptCallback
, which is where you will go when you accept aSocket
. PutEndAccept
there.I would typically list my client sockets, and do something on client disposal (that is unlisted it) - but this depends on the need. In this case, you seem to need it. And don't forget to create buffers, etc... This is for buffering the incoming data.
Start to accept something received from the client, using another
ASync
BeginReceive
on the clientSocket
(and now you needreceiveCallback
). Then, very important, repeat yourBeginAccept
to accept other clients!Define your
receiveCallback
, that is, when you receive something from your client. This part could be quite tricky because of failures! But basically, what you need for now is simplyEndReceive
and again, very important, to repeat theBeginReceive
from the same client such that you can receive its next message!And suppose you want to reply your sender after you receive the message, simply do this in the
if (received > 0)
part:And after putting a little more things in your main routine, you are done(!) - IF you do not ask for sending to client as
byte[]
And now, if you want to send something to all your clients as
byte[]
you simply need to list all your client (see step 4-5). See this and convert theresult
string
above (remember to type it in hexstring
format as required) tobyte[]
then send it to all the clients using your client socket list (here is where it is needed!):And here, you are more or less done with your server. Next is your client
Client:
Similarly, put the
Socket
class in the class context rather than method context and initialize it as soon as you start your programThen start to connect by
ASync
BeginConnect
. I would normally go further byLoopConnect
just for failure handling like this.Similar concept to what you do to the server
BeginAccept
you need to defineendConnectCallback
for theASync
BeginConnect
you use. But here, unlike server which needs to re-callingBeginAccept
, once you are connected, you do not need to do any newBeginConnect
since you only need to be connected once.You may want to declare
buffer
etc. Then, after you connect, don't forget the nextASync
BeginReceive
to handle the message retrieval part (similar with the server)Naturally, you need to define your
receiveCallback
, just like what you did for the server. And yes, it is as you have guessed, it is almost identical to what you did for the server!You can do anything you want with your data. Note that the data you receive is actually in
byte[]
, notstring
. So you can do anything with it. But for example's sake, I will just usestring
to display.And at the very very last... Yes, again, as you have already guessed, you just need to do something on your main routine - suppose you want to use it to send data. Because you use
Console
but you want it to send things asbyte[]
, you need to do the conversion (see the explanation in server 9.). And afterwards you are completely done!!Results:
Here you go! I tested it by sending
string
for display, but I already put up what is needed when you want to change it tobyte[]
Code for your test:
Server
Client
Last Remarks (Edit)
Since the code above is run using
Console Application
it must be run withstatic main void
keyword. And thus clientSocket
defined above is ofstatic
type. This may prevent the clientSocket
to be defined multiple times as each time it is "defined", since it is of the sameclass
namedProgram
, it will refer to the sameSocket
(though this may not always the case, at least according to the OP's experiment: he can run multiple clients successfully in the same computer).Nevertheless, to overcome this is not that hard. Simply port the client application to the platform which is non-initiated as
static
class (such asWinForms
) and all the above code would still run as per normal. Alternatively, if it must be run using theConsole Applications
, and the problem occurs, simply copy the client application and re-define it using differentnamespace
or differentclass
name to avoid defining identicalSocket
due to identicalnamespace
orclass
.But the most important part on this problem solving is the use of
Async
andSync
wisely to solve the given issue.Continuation of this topic can be found here
You can send data to a client, and it can be achieved similarly to how you do so client -> server (unless your sockets are receive only, if so switch them to send and receive). Sending a Boolean requires conversion to a byte, you can achieve this via the BitConverter class.
Why don't you make your life easier and use SignalR?
Below you can see a simple example where the server and the clients are console apps
Client (Run this .exe many times)
Server (Run this .exe before you run the clients)
In order for the above to work you need the following NuGet packages:
Client:
Microsoft.AspNet.SignalR.Client
Server
Microsoft.AspNet.SignalR
Microsoft.AspNet.SignalR.Client
Microsoft.AspNet.SignalR.SelfHost
Microsoft.Owin.Host.HttpListener
If you want the server/clients to be on different machines all you have to do is change the ServeURI properties in both projects:
You can find another similar example in WinForms here:
https://code.msdn.microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b