Ответы на вопросы первых:
Q: Нужно ли преобразовать все в строку ... В общем, что я хочу, чтобы отправить переменную с одного компьютера на двух других чтобы процесс начинался одновременно на всех компьютерах.
A: Нет, нет необходимости конвертировать все в строку при отправке с использованием Socket
. Вы можете отправить byte[]
, которого вы, скорее всего, захотите.
Q: То, что я хочу добиться того, чтобы послать логическую переменную с сервера к клиенту с гнездами
A: Вы имеете в виду boolean
или byte
? Потому что основной тип переменной, который вы получите , получит от Socket
значение byte
. Вы всегда можете изменить byte
к bool
со стороны отправителя/приемника, делая как
bool val = byteToCheck > 0 ? true : false
A2: А так как ваша Sever является Console
приложений, я рекомендую взять взгляд на гекс string
для byte[]
преобразования. Таким образом, вы могли бы написать string
, но интерпретировали его как byte[]
. Проверьте this. Вся эта идея здесь довольно проста. То есть: вы вводите string
, но он будет отправлен как byte[]
. И так как это byte[]
, у вас может быть любое значение.
И вот я представляю мое решение для обработки ваших (1) несколько клиентов, (2) Async
подключения & принимает & получить, но с (3) отправить синхронизации, а также (4) преобразование из hex string
в byte[]
(конструкция &), и, что не менее важно (5) рабочий код с пользовательским вводом (для вас, чтобы изменить эту часть) для тестирования!
Я решил бы решить эту проблему, используя простой класс Socket
, так как это решение, с которым я больше всего знаком. Но вы всегда можете сделать это, если используете свой TcpListener.Server
(который является базовой сетью класса Socket
). И, как вы пожелаете, я бы сделал это с ASync
.
Есть несколько шагов, необходимых для достижения того, что вы хотите, как в вашем сервере и клиентом:
сервера
сделать ваш Socket
как класс поле, а не поле метода, поскольку вы будете использовать, если повсюду, и вам нужно несколько методов для достижения того, чего вы хотите. И инициализируйте его, как только вы начнете свою основную процедуру.
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
}
Поскольку сервер будет обслуживать много клиентов, я рекомендую вам использовать ASync
, а не Sync
для процесса. Инициализировать ваш Socket
с помощью BeginAccept
, а не с помощью Accept
, положить acceptCallback
в вашем 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
}
Определение acceptCallback
, в котором вы будете идти, когда вы принимаете Socket
. Положите там 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
}
}
Я обычно список моих клиентов сокетов, и сделать что-то на распоряжение клиента (что в списке нет его) - но это зависит от потребностей. В этом случае вам это кажется нужным. И не забудьте создать буферы и т. Д. Это для буферизации входящих данных.
Старт, чтобы принять что-то получил от клиента, с помощью другого ASync
BeginReceive
на клиенте Socket
(и теперь вам нужно receiveCallback
). Затем, очень важно, повторите BeginAccept
, чтобы принять других клиентов!
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());
}
}
Определите ваш receiveCallback
, то есть, когда вы получаете что-то от своего клиента. Эта часть может быть довольно сложной из-за сбоев! Но в основном, то, что вам нужно сейчас, это просто EndReceive
и снова очень важно, чтобы повторить BeginReceive
от того же клиента, чтобы вы могли получить следующее сообщение!
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());
}
}
И предположим, что вы хотите, чтобы ответить ваш отправитель после получения сообщения, просто сделать это в 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
}
И после того, как положить немного больше вещей в вашей основной программе, вы сделали (!) - еСЛИ вы не просите для отправки клиенту, как byte[]
а теперь, если вы хотите отправить что-то всем вашим клиентам, как byte[]
, вам просто нужно указать всех ваших клиентов (см. Шаг 4-5). См this и преобразовать result
string
выше (не забудьте ввести его в шестнадцатеричном формате string
, как требуется) byte[]
затем отправить его всем клиентам с помощью своего клиента список сокетов (здесь, где это необходимо!):
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");
}
И здесь вы более или менее выполняете свой сервер.Далее ваш клиент
Клиент:
Аналогично, поставить Socket
класс в контексте класса, а не контекста метода и инициализировать его, как только вы начинаете вашу программу
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
}
Тогда подключите к ASync
BeginConnect
. Я бы обычно пошел дальше LoopConnect
просто для обработки отказа, как это.
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;
}
}
Похожие концепции, что вы делаете на сервер BeginAccept
вам нужно определить endConnectCallback
для ASync
BeginConnect
вы используете. Но здесь, , в отличие от сервера, которому необходимо перезвонить BeginAccept
, после того, как вы подключены, вам не нужно делать никаких новых BeginConnect
, так как вы должны только подключить один раз.
Вы можете объявить buffer
и т.д. Затем, после подключения, не забудьте следующий ASync
BeginReceive
для обработки поиска сообщений части (по аналогии с сервером)
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());
}
}
Естественно, вы необходимо определить ваш receiveCallback
, как и то, что вы сделали для сервера. И да, это, как вы догадались, почти идентично тому, что вы делали для сервера!
Вы можете делать все, что хотите, своими данными. Обратите внимание, что данные, которые вы получаете, фактически находятся в byte[]
, а не string
. Таким образом, вы можете сделать с ней все. Но , например, ради, я просто буду использовать string
для отображения.
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());
}
}
И совсем уж в последний раз ... Да, опять-таки, как вы уже догадались, вам просто нужно сделать что-то на вашей основной программе - предположим, что вы хотите использовать его для передачи данных. Поскольку вы используете Console
, но вы хотите, чтобы он отправлял вещи как byte[]
, вам необходимо выполнить преобразование (см. Объяснение на сервере 9.). А потом ты полностью закончил!
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");
}
Результаты:
Здесь вы идете!Я проверил это, посылая string
для показа, но я уже положил на то, что нужно, если вы хотите изменить его byte[]
Код для теста:
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());
}
}
}
}
Последние замечания (Edit)
Поскольку приведенный выше код выполняется с использованием Console Application
он должен быть запущен с static main void
ключевого слова. И, таким образом, клиент Socket
, определенный выше, имеет тип static
. Это может препятствовать тому, чтобы клиент Socket
определялся несколько раз, как каждый раз, когда он «определен», поскольку он имеет тот же class
, который называется Program
, он будет ссылаться на тот же номер Socket
(хотя это может быть не всегда так, по крайней мере, согласно эксперимент OP: он может успешно запускать несколько клиентов на одном компьютере).
Тем не менее, преодолеть это не так уж сложно. Просто поместите клиентское приложение на платформу, которая не инициирована как класс static
(такой как WinForms
), и все вышеприведенные коды будут работать в обычном режиме. В качестве альтернативы, если он должен быть запущен с использованием Console Applications
, и возникает проблема, просто скопируйте клиентское приложение и переопределите его с использованием другого namespace
или другого имени class
, чтобы избежать определения идентичных Socket
из-за идентичных namespace
или class
.
Но наиболее важной частью решения этой проблемы является использование Async
и Sync
мудро для решения данной проблемы.
Продолжение этой темы можно найти here
к моему знанию, вы можете, но метод, который я не знаю, посмотрите на это. постскриптум то, что я могу вспомнить, - это преобразовать данные, которые вы хотите отправить в байт [] - http://www.codeproject.com/Articles/2477/Multi-threaded-Client-Server-Socket-Class – KGCybeX
извинения, только что понял, что это находится на C++, просто найдите сайт codeproject для передачи сетевого сервера клиента для C#, я использовал его с этого сайта, надеюсь, что это поможет – KGCybeX
@ Спасибо за ваш супераналитический ответ. Я собираюсь попробовать все твои шаги. : P –