J'utilise les projets suivants afin de créer une communication asynchrone entre les sockets serveur et client . Lorsque j'exécute ces projets, j'envoie un message du client au serveur, j'ai donc reçu le message:
Données: enregistrement EOF, envoyé 14 octets au client.
Ce que je veux réaliser, c'est envoyer une variable booléenne du serveur au client avec les sockets. Est-ce faisable, je me demande puisque dans le code j'ai le serveur qui attend et écoute et le client qui envoie des données, puis-je faire le contraire? En général, ce que je veux, c'est envoyer un booléen à plusieurs clients. Pourquoi ai-je besoin de la fin du fichier pour envoyer une chaîne? Est-il nécessaire de tout convertir en chaîne?
[~ # ~] edit [~ # ~] : En général, ce que je veux, c'est envoyer une variable d'un ordinateur à deux autres pour un processus pour commencer simultanément sur tous les ordinateurs. En fait, pour créer un commutateur qui donne un signal pour commencer un processus dans 2-3 machines en même temps.
J'ai essayé d'utiliser le code suivant pour serveur :
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();
}
}
et pour 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();
}
}
Juste au cas où je travaille dans la même machine. J'aurai au total 4 machines et je veux que l'une d'elles donne un chant aux autres pour commencer l'enregistrement du flux rgb. Ainsi, le serveur doit envoyer un signal aux clients pour commencer l'enregistrement. Que dois-je faire pour changer le comportement du serveur pour envoyer des données et ne pas écouter. Est-il possible que plusieurs machines écoutent et attendent qu'un signal soit donné?
MODIFIER:
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;
}
}
Mon code tel qu'il est maintenant avec un clic gauche appelle la fonction startServer () avec un nouveau thread qui est le code principal dans l'implémentation @Ians et ensuite j'appelle mon objet. Lorsque je clique avec le bouton droit de la souris, je change un indicateur et le capturant s'arrête. Comment puis-je également arrêter le serveur ou faire une pause afin de l'ouvrir à nouveau avec le clic gauche?
Réponses aux questions d'abord:
Q: Est-il nécessaire de tout convertir en chaîne? ... En général, ce que je veux, c'est envoyer une variable d'un ordinateur à deux autres afin qu'un processus démarre simultanément sur tous les ordinateurs.
R: Non, il n'est pas nécessaire de tout convertir en chaîne lors de l'envoi à l'aide de
Socket
. Vous pouvez envoyerbyte[]
Que vous voulez très probablement.Q: Ce que je veux réaliser, c'est envoyer une variable booléenne du serveur au client avec les sockets
R: Voulez-vous dire
boolean
oubyte
? Parce que le type de variable de base que vous obtiendrez duSocket
estbyte
. Vous pouvez toujours changerbyte
enbool
du côté émetteur/récepteur en faisant comme
bool val = byteToCheck > 0;
A2: Et comme votre serveur est une application
Console
, je recommande de jeter un œil à la conversion hexadécimalestring
enbyte[]
. De cette façon, vous pourriez écrire quelque chose dansstring
mais l'interpréter commebyte[]
. Vérifiez ceci . L'idée ici est assez simple. C'est-à-dire: vous tapezstring
, mais il sera envoyé en tant quebyte[]
. Et comme il s'agit debyte[]
, Vous pouvez y ajouter n'importe quelle valeur.
Et ici, je présente ma solution pour gérer vos (1) clients multiples, (2) Async
connecter et accepter et recevoir, mais ayant (3) envoyer la synchronisation, ainsi que (4) la conversion à partir de hex string
à byte[]
(la structure et l'idée), et le dernier mais non le moindre (5) code de travail avec entrée utilisateur (pour que vous puissiez changer cette partie) pour le test!
Je résoudrais un tel problème en utilisant une simple classe Socket
, car c'est la solution que je connais le mieux. Mais vous pourriez toujours faire de même si vous utilisez votre TcpListener.Server
(Qui est le réseau sous-jacent de la classe Socket
). Et, comme vous le souhaitez, je le ferais avec ASync
.
Il y a plusieurs étapes nécessaires pour réaliser ce que vous voulez à la fois sur votre serveur et votre client:
Serveur
Créez votre Socket
comme champ de classe plutôt que comme champ de méthode, car vous l'utiliserez partout et vous aurez besoin de plusieurs méthodes pour obtenir ce que vous voulez. Et initialisez-le dès que vous avez commencé votre routine principale.
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket serverSocket; //put here as static
static void Main(string[] args) {
//---listen at the specified IP and port no.---
Console.WriteLine("Listening...");
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
//your next main routine
}
Étant donné que le serveur servira de nombreux clients, je vous recommanderai d'utiliser ASync
plutôt que Sync
pour le processus. Initialisez votre Socket
en utilisant BeginAccept
plutôt qu'en utilisant Accept
, mettez acceptCallback
dans votre BeginAccept
static void Main(string[] args) {
//---listen at the specified IP and port no.---
Console.WriteLine("Listening...");
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);
//other stuffs
}
Définissez acceptCallback
, où vous irez lorsque vous accepterez un Socket
. Mettez-y EndAccept
.
private void acceptCallback(IAsyncResult result) { //if the buffer is old, then there might already be something there...
System.Net.Sockets.Socket socket = null;
try {
socket = serverSocket.EndAccept(result); // To get your client socket
//do something later
} catch (Exception e) { // this exception will happen when "this" is be disposed...
//do something later
}
}
Je répertorierais généralement mes sockets client et ferais quelque chose à la disposition du client (qui n'est pas répertorié) - mais cela dépend du besoin. Dans ce cas, vous semblez en avoir besoin. Et n'oubliez pas de créer des tampons, etc ... C'est pour tamponner les données entrantes.
Commencez à accepter quelque chose reçu du client, en utilisant un autre ASync
BeginReceive
sur le client Socket
(et maintenant vous avez besoin de receiveCallback
). Ensuite, très important , répétez votre BeginAccept
pour accepter d'autres clients!
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 such as listing
clientSockets.Add(socket); //may be needed later
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
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());
}
}
Définissez votre receiveCallback
, c'est-à-dire lorsque vous recevez quelque chose de votre client. Cette partie pourrait être assez délicate à cause des échecs! Mais fondamentalement, ce dont vous avez besoin pour l'instant est simplement EndReceive
et encore, très important , pour répéter le BeginReceive
de le même client pour que vous puissiez recevoir son prochain message!
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 https://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 IN byte[] data!! Yihaa!!
Console.WriteLine(Encoding.UTF8.GetString(data)); //Here I just print it, but you need to do something else
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());
}
}
Et supposons que vous souhaitiez répondre à votre expéditeur après avoir reçu le message, faites-le simplement dans la partie if (received > 0)
:
if (received > 0) {
byte[] data = new byte[received]; //the data is in the byte[] format, not string!
//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
}
Et après avoir mis un peu plus de choses dans votre routine principale, vous avez terminé (!) - [~ # ~] si [~ # ~] vous le faites ne pas demander l'envoi au client en tant que byte[]
Et maintenant, si vous voulez envoyer quelque chose à tous vos clients en tant que byte[]
, Il vous suffit de lister tous vos clients (voir étape 4 -5). Voir this et convertir le result
string
ci-dessus (n'oubliez pas de le taper au format hex string
comme requis) en byte[]
Puis envoyez-le à tous les clients en utilisant votre liste de socket client (voici où cela est nécessaire!):
static void Main(string[] args) {
//---listen at the specified IP and port no.---
Console.WriteLine("Listening...");
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);
//normally, there isn't anything else needed here
string result = "";
do {
result = Console.ReadLine();
if (result.ToLower().Trim() != "exit") {
byte[] bytes = null;
//you can use `result` and change it to `bytes` by any mechanism which you want
//the mechanism which suits you is probably the hex string to byte[]
//this is the reason why you may want to list the client sockets
foreach(Socket socket in clientSockets)
socket.Send(bytes); //send everything to all clients as bytes
}
} while (result.ToLower().Trim() != "exit");
}
Et ici, vous avez plus ou moins fini avec votre serveur. Vient ensuite votre client
Client:
De même, placez la classe Socket
dans le contexte de la classe plutôt que dans le contexte de la méthode et initialisez-la dès que vous démarrez votre programme
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket clientSocket; //put here
static void Main(string[] args) {
//Similarly, start defining your client socket as soon as you start.
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//your other main routines
}
Commencez ensuite à vous connecter par ASync
BeginConnect
. J'irais normalement plus loin en LoopConnect
juste pour une gestion des pannes comme celle-ci.
static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
int attempts = 0;
while (!clientSocket.Connected && attempts < noOfRetry) {
try {
++attempts;
IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnect, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
} catch (Exception e) {
Console.WriteLine("Error: " + e.ToString());
}
}
if (!clientSocket.Connected) {
Console.WriteLine("Connection attempt is unsuccessful!");
return;
}
}
Concept similaire à ce que vous faites au serveur BeginAccept
, vous devez définir endConnectCallback
pour le ASync
BeginConnect
que vous utilisez. Mais ici, contrairement au serveur qui doit rappeler BeginAccept
, une fois que vous êtes connecté, vous n'avez pas besoin de faire de nouvelles BeginConnect
puisque vous n'avez besoin d'être connecté qu'une seule fois .
Vous voudrez peut-être déclarer buffer
etc. Ensuite, après vous être connecté, n'oubliez pas le prochain ASync
BeginReceive
pour gérer la partie de récupération des messages (similaire avec le serveur)
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 void endConnectCallback(IAsyncResult ar) {
try {
clientSocket.EndConnect(ar);
if (clientSocket.Connected) {
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
} else {
Console.WriteLine("End of connection attempt, fail to connect...");
}
} catch (Exception e) {
Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
}
}
Naturellement, vous devez définir votre receiveCallback
, tout comme ce que vous avez fait pour le serveur. Et oui, c'est comme vous l'avez deviné, c'est presque identique à ce que vous avez fait pour le serveur!
Vous pouvez faire tout ce que vous voulez avec vos données. Notez que les données que vous recevez se trouvent en fait dans byte[]
, Pas dans string
. Vous pouvez donc tout faire avec. Mais par exemple , je vais simplement utiliser string
pour afficher.
const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0;
private static void receiveCallback(IAsyncResult result) {
System.Net.Sockets.Socket socket = null;
try {
socket = (System.Net.Sockets.Socket)result.AsyncState;
if (socket.Connected) {
int received = socket.EndReceive(result);
if (received > 0) {
receiveAttempt = 0;
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //copy the data from your buffer
//DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
//Notice that your data is not string! It is actually byte[]
//For now I will just print it out
Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
++receiveAttempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else { //completely fails!
Console.WriteLine("receiveCallback is failed!");
receiveAttempt = 0;
clientSocket.Close();
}
}
} catch (Exception e) { // this exception will happen when "this" is be disposed...
Console.WriteLine("receiveCallback is failed! " + e.ToString());
}
}
Et à la toute dernière ... Oui, encore une fois, comme vous l'avez déjà deviné, il vous suffit de faire quelque chose sur votre routine principale - supposons que vous vouliez l'utiliser pour envoyer des données. Étant donné que vous utilisez Console
mais que vous souhaitez qu'il envoie des éléments sous la forme byte[]
, Vous devez effectuer la conversion (voir l'explication dans le serveur 9.). Et après, vous avez complètement terminé !!
static void Main(string[] args) {
//Similarly, start defining your client socket as soon as you start.
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
loopConnect(3, 3); //for failure handling
string result = "";
do {
result = Console.ReadLine(); //you need to change this part
if (result.ToLower().Trim() != "exit") {
byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
//do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
clientSocket.Send(bytes);
}
} while (result.ToLower().Trim() != "exit");
}
Résultats:
Voici! Je l'ai testé en envoyant string
pour l'affichage, mais j'ai déjà mis en place ce qui est nécessaire lorsque vous voulez le changer en byte[]
Code pour votre test:
Serveur
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace TcpListenerConsoleApplication {
class Program {
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket serverSocket;
static void Main(string[] args) {
//---listen at the specified IP and port no.---
Console.WriteLine("Listening...");
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 = Console.ReadLine();
} 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 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
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
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 https://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());
}
}
}
}
Client
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace TcpClientConsoleApplication {
class Program {
const int PORT_NO = 2201;
const string SERVER_IP = "127.0.0.1";
static Socket clientSocket; //put here
static void Main(string[] args) {
//Similarly, start defining your client socket as soon as you start.
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
loopConnect(3, 3); //for failure handling
string result = "";
do {
result = Console.ReadLine(); //you need to change this part
if (result.ToLower().Trim() != "exit") {
byte[] bytes = Encoding.ASCII.GetBytes(result); //Again, note that your data is actually of byte[], not string
//do something on bytes by using the reference such that you can type in HEX STRING but sending thing in bytes
clientSocket.Send(bytes);
}
} while (result.ToLower().Trim() != "exit");
}
static void loopConnect(int noOfRetry, int attemptPeriodInSeconds) {
int attempts = 0;
while (!clientSocket.Connected && attempts < noOfRetry) {
try {
++attempts;
IAsyncResult result = clientSocket.BeginConnect(IPAddress.Parse(SERVER_IP), PORT_NO, endConnectCallback, null);
result.AsyncWaitHandle.WaitOne(TimeSpan.FromSeconds(attemptPeriodInSeconds));
System.Threading.Thread.Sleep(attemptPeriodInSeconds * 1000);
} catch (Exception e) {
Console.WriteLine("Error: " + e.ToString());
}
}
if (!clientSocket.Connected) {
Console.WriteLine("Connection attempt is unsuccessful!");
return;
}
}
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 void endConnectCallback(IAsyncResult ar) {
try {
clientSocket.EndConnect(ar);
if (clientSocket.Connected) {
clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), clientSocket);
} else {
Console.WriteLine("End of connection attempt, fail to connect...");
}
} catch (Exception e) {
Console.WriteLine("End-connection attempt is unsuccessful! " + e.ToString());
}
}
const int MAX_RECEIVE_ATTEMPT = 10;
static int receiveAttempt = 0;
private static void receiveCallback(IAsyncResult result) {
System.Net.Sockets.Socket socket = null;
try {
socket = (System.Net.Sockets.Socket)result.AsyncState;
if (socket.Connected) {
int received = socket.EndReceive(result);
if (received > 0) {
receiveAttempt = 0;
byte[] data = new byte[received];
Buffer.BlockCopy(buffer, 0, data, 0, data.Length); //There are several way to do this according to https://stackoverflow.com/questions/5099604/any-faster-way-of-copying-arrays-in-c in general, System.Buffer.memcpyimpl is the fastest
//DO ANYTHING THAT YOU WANT WITH data, IT IS THE RECEIVED PACKET!
//Notice that your data is not string! It is actually byte[]
//For now I will just print it out
Console.WriteLine("Server: " + Encoding.UTF8.GetString(data));
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else if (receiveAttempt < MAX_RECEIVE_ATTEMPT) { //not exceeding the max attempt, try again
++receiveAttempt;
socket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(receiveCallback), socket);
} else { //completely fails!
Console.WriteLine("receiveCallback is failed!");
receiveAttempt = 0;
clientSocket.Close();
}
}
} catch (Exception e) { // this exception will happen when "this" is be disposed...
Console.WriteLine("receiveCallback is failed! " + e.ToString());
}
}
}
}
Dernières remarques (Modifier)
Étant donné que le code ci-dessus est exécuté à l'aide de Console Application
, Il doit être exécuté avec le mot clé static main void
. Et donc le client Socket
défini ci-dessus est de type static
. Cela peut empêcher le client Socket
d'être défini plusieurs fois comme à chaque fois qu'il est "défini", car il est du même class
nommé Program
, il fera référence à la même Socket
(bien que cela ne soit pas toujours le cas, du moins selon l'expérience de l'OP: il peut exécuter plusieurs clients avec succès sur le même ordinateur).
Néanmoins, surmonter cela n'est pas si difficile. Portez simplement l'application client sur la plate-forme qui n'est pas initiée en tant que classe static
(telle que WinForms
) et tout le code ci-dessus fonctionnerait toujours normalement. Sinon, si elle doit être exécutée à l'aide du Console Applications
Et que le problème se produit, copiez simplement l'application cliente et redéfinissez-la en utilisant un nom différent namespace
ou différent class
pour éviter définir Socket
identique en raison de namespace
ou class
identiques.
Mais la partie la plus importante de cette résolution de problèmes est l'utilisation judicieuse de Async
et Sync
pour résoudre le problème donné.
La suite de ce sujet peut être trouvée ici
Pourquoi ne vous simplifiez-vous pas la vie et n'utilisez-vous pas SignalR?
Ci-dessous, vous pouvez voir un exemple simple où le serveur et les clients sont des applications de console
Client (exécutez ce fichier .exe plusieurs fois)
using System;
using Microsoft.AspNet.SignalR.Client;
namespace SignalRClient
{
class Program
{
private static IHubProxy HubProxy { get; set; }
const string ServerURI = "http://localhost:1234/signalr";
private static HubConnection Connection { get; set; }
static void Main(string[] args)
{
Connection = new HubConnection(ServerURI);
HubProxy = Connection.CreateHubProxy("MyHub");
HubProxy.On<string, string>("SendMessage", (name, message) => Console.WriteLine(name + ":" + message));
Connection.Start().Wait();
Console.WriteLine("Press Enter to stop client");
Console.ReadLine();
}
}
}
Serveur (exécutez ce .exe avant d'exécuter les clients)
using System;
using System.Threading.Tasks;
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Client;
using Microsoft.Owin.Hosting;
using Owin;
namespace SignalRServer
{
class Program
{
static private IDisposable SignalR { get; set; }
const string ServerURI = "http://localhost:1234";
private static IHubProxy HubProxy { get; set; }
private static HubConnection Connection { get; set; }
static void Main(string[] args)
{
SignalR = WebApp.Start(ServerURI);
Console.WriteLine("Server running at " + ServerURI);
Connection = new HubConnection(ServerURI);
HubProxy = Connection.CreateHubProxy("MyHub");
HubProxy.On<string, string>("SendMessage", (name, message) => Console.WriteLine(name + ":" + message));
Connection.Start().Wait();
string messageToSentToClients;
do
{
Console.WriteLine("Type someting to send to clients and press enter");
messageToSentToClients = Console.ReadLine();
HubProxy.Invoke("Send", "Server", messageToSentToClients);
} while (messageToSentToClients != "exit");
}
}
public class MyHub : Hub
{
public void Send(string name, string message) { Clients.All.sendMessage(name, message); }
}
class Startup
{
public void Configuration(IAppBuilder app) { app.MapSignalR(); }
}
}
Pour que ce qui précède fonctionne, vous avez besoin des packages NuGet suivants:
Client:
Microsoft.AspNet.SignalR.Client
Serveur
Microsoft.AspNet.SignalR
Microsoft.AspNet.SignalR.Client
Microsoft.AspNet.SignalR.SelfHost
Microsoft.Owin.Host.HttpListener
Si vous souhaitez que le serveur/les clients soient sur des machines différentes, tout ce que vous avez à faire est de modifier les propriétés ServeURI dans les deux projets:
//Clients
const string ServerURI = "http://SERVER_IP:PORT/signalr";
//Server
const string ServerURI = "http://SERVER_IP:PORT";
Vous pouvez trouver un autre exemple similaire dans WinForms ici:
https://code.msdn.Microsoft.com/windowsdesktop/Using-SignalR-in-WinForms-f1ec847b
Vous pouvez envoyer des données à un client, et cela peut être réalisé de la même manière que vous le faites client -> serveur (à moins que vos sockets ne soient reçus que, si c'est le cas, passez-les pour envoyer et recevoir). L'envoi d'un booléen nécessite une conversion en octet, vous pouvez y parvenir via la classe BitConverter.
Sur la page https://msdn.Microsoft.com/en-us/library/fx6588te%28v=vs.110%29.aspx voir la méthode AcceptCallback
(elle est appelée lorsque le client se connecte ). Il y a la ligne Socket handler = listener.EndAccept(ar);
. Cela se produit pour chaque client lorsqu'il se connecte, alors conservez ces instances de socket dans une liste. Lorsque vous souhaitez envoyer des données aux clients, utilisez la méthode Send
du même exemple avec chaque socket de la liste (ou de manière sélective si vous souhaitez envoyer uniquement à certains clients).
private static void Send(Socket handler, String data) {
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}