C# Language
Zócalo asíncrono
Buscar..
Introducción
Al usar sockets asíncronos, un servidor puede escuchar las conexiones entrantes y, mientras tanto, hacer alguna otra lógica, en contraste con el socket síncrono cuando están escuchando, bloquean el hilo principal y la aplicación deja de responder y se congelará hasta que el cliente se conecte.
Observaciones
Zócalo y red
¿Cómo acceder a un servidor fuera de mi propia red? Esta es una pregunta común y cuando se la pregunta se marca principalmente como tema.
Lado del servidor
En la red de su servidor necesita reenviar su enrutador a su servidor.
Por ejemplo, PC donde el servidor se está ejecutando en:
IP local = 192.168.1.115
El servidor está escuchando el puerto 1234.
Reenvíe las conexiones entrantes en el enrutador del Port 1234
al 192.168.1.115
Lado del cliente
Lo único que necesitas cambiar es la IP. No desea conectarse a su dirección de bucle de retorno sino a la IP pública de la red en la que se ejecuta su servidor. Esta IP la puedes obtener aquí .
_connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("10.10.10.10"), 1234));
Así que ahora creas una solicitud en este punto final: 10.10.10.10:1234
si hiciste que el puerto de propiedad reenvíe tu enrutador, tu servidor y el cliente se conectarán sin ningún problema.
Si desea conectarse a una IP local, no tendrá que portforwart, simplemente cambie la dirección de loopback a 192.168.1.178
o algo así.
Enviando datos:
Los datos se envían en una matriz de bytes. Debe empaquetar sus datos en una matriz de bytes y descomprimirlos en el otro lado.
Si está familiarizado con el socket, también puede intentar cifrar su matriz de bytes antes de enviar. Esto evitará que alguien robe tu paquete.
Ejemplo de Socket Asíncrono (Cliente / Servidor).
Ejemplo del lado del servidor
Crear escucha para el servidor
Comience con la creación de un servidor que maneje los clientes que se conecten y las solicitudes que se enviarán. Así que crea una clase de oyente que manejará esto.
class Listener
{
public Socket ListenerSocket; //This is the socket that will listen to any incoming connections
public short Port = 1234; // on this port we will listen
public Listener()
{
ListenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
}
Primero necesitamos inicializar el socket de escucha donde podemos escuchar cualquier conexión. Vamos a usar un Socket Tcp, por eso usamos SocketType.Stream. También especificamos a qué puerto debe escuchar el servidor.
Entonces comenzamos a escuchar cualquier conexión entrante.
Los métodos de árbol que usamos aquí son:
Este método une el socket a un IPEndPoint . Esta clase contiene el host y la información del puerto local o remoto que necesita una aplicación para conectarse a un servicio en un host.
El parámetro backlog especifica el número de conexiones entrantes que pueden ponerse en cola para su aceptación.
ListenerSocket.BeginAccept ();
El servidor comenzará a escuchar las conexiones entrantes y continuará con otra lógica. Cuando hay una conexión, el servidor vuelve a este método y ejecutará el método AcceptCallBack
public void StartListening()
{
try
{
MessageBox.Show($"Listening started port:{Port} protocol type: {ProtocolType.Tcp}");
ListenerSocket.Bind(new IPEndPoint(IPAddress.Any, Port));
ListenerSocket.Listen(10);
ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
}
catch(Exception ex)
{
throw new Exception("listening error" + ex);
}
}
Así que cuando un cliente se conecta podemos aceptarlos por este método:
Tres métodos que utilizamos aquí son:
Comenzamos la devolución de llamada con
Listener.BeginAccept()
fin ahora que tenemos que terminar esa llamada.The EndAccept()
métodoThe EndAccept()
acepta un parámetro IAsyncResult, esto almacenará el estado del método asíncrono. Desde este estado podemos extraer el socket del que provenía la conexión entrante.ClientController.AddClient()
Con el socket que obtuvimos de
EndAccept()
creamos un Cliente con un método propio (código ClientController debajo del ejemplo del servidor) .Necesitamos comenzar a escuchar nuevamente cuando el socket haya terminado con el manejo de la nueva conexión. Pase en el método que capturará esta devolución de llamada. Y también pase int en el socket del oyente para que podamos reutilizarlo para las próximas conexiones.
public void AcceptCallback(IAsyncResult ar)
{
try
{
Console.WriteLine($"Accept CallBack port:{Port} protocol type: {ProtocolType.Tcp}");
Socket acceptedSocket = ListenerSocket.EndAccept(ar);
ClientController.AddClient(acceptedSocket);
ListenerSocket.BeginAccept(AcceptCallback, ListenerSocket);
}
catch (Exception ex)
{
throw new Exception("Base Accept error"+ ex);
}
}
Ahora tenemos un zócalo de escucha, pero ¿cómo recibimos los datos enviados por el cliente que es lo que muestra el siguiente código?
Crear un receptor de servidor para cada cliente
Primero, cree una clase de recepción con un constructor que tome un Socket como parámetro:
public class ReceivePacket
{
private byte[] _buffer;
private Socket _receiveSocket;
public ReceivePacket(Socket receiveSocket)
{
_receiveSocket = receiveSocket;
}
}
En el siguiente método, primero comenzamos con darle al búfer un tamaño de 4 bytes (Int32) o el paquete contiene las partes {longitud, datos reales}. Así que los primeros 4 bytes nos reservamos para la longitud de los datos el resto para los datos reales.
A continuación utilizamos el método BeginReceive () . Este método se utiliza para comenzar a recibir de los clientes conectados y cuando reciba datos ejecutará la función ReceiveCallback
.
public void StartReceiving()
{
try
{
_buffer = new byte[4];
_receiveSocket.BeginReceive(_buffer, 0, _buffer.Length, SocketFlags.None, ReceiveCallback, null);
}
catch {}
}
private void ReceiveCallback(IAsyncResult AR)
{
try
{
// if bytes are less than 1 takes place when a client disconnect from the server.
// So we run the Disconnect function on the current client
if (_receiveSocket.EndReceive(AR) > 1)
{
// Convert the first 4 bytes (int 32) that we received and convert it to an Int32 (this is the size for the coming data).
_buffer = new byte[BitConverter.ToInt32(_buffer, 0)];
// Next receive this data into the buffer with size that we did receive before
_receiveSocket.Receive(_buffer, _buffer.Length, SocketFlags.None);
// When we received everything its onto you to convert it into the data that you've send.
// For example string, int etc... in this example I only use the implementation for sending and receiving a string.
// Convert the bytes to string and output it in a message box
string data = Encoding.Default.GetString(_buffer);
MessageBox.Show(data);
// Now we have to start all over again with waiting for a data to come from the socket.
StartReceiving();
}
else
{
Disconnect();
}
}
catch
{
// if exeption is throw check if socket is connected because than you can startreive again else Dissconect
if (!_receiveSocket.Connected)
{
Disconnect();
}
else
{
StartReceiving();
}
}
}
private void Disconnect()
{
// Close connection
_receiveSocket.Disconnect(true);
// Next line only apply for the server side receive
ClientController.RemoveClient(_clientId);
// Next line only apply on the Client Side receive
Here you want to run the method TryToConnect()
}
Así que hemos configurado un servidor que puede recibir y escuchar las conexiones entrantes. Cuando los clientes se conectan, se agregará a una lista de clientes y cada cliente tendrá su propia clase de recepción. Para hacer que el servidor escuche:
Listener listener = new Listener();
listener.StartListening();
Algunas clases que uso en este ejemplo
class Client
{
public Socket _socket { get; set; }
public ReceivePacket Receive { get; set; }
public int Id { get; set; }
public Client(Socket socket, int id)
{
Receive = new ReceivePacket(socket, id);
Receive.StartReceiving();
_socket = socket;
Id = id;
}
}
static class ClientController
{
public static List<Client> Clients = new List<Client>();
public static void AddClient(Socket socket)
{
Clients.Add(new Client(socket,Clients.Count));
}
public static void RemoveClient(int id)
{
Clients.RemoveAt(Clients.FindIndex(x => x.Id == id));
}
}
Ejemplo del lado del cliente
Conectando al servidor
En primer lugar, queremos crear una clase que se conecte con el nombre del servidor que le damos: Connector:
class Connector
{
private Socket _connectingSocket;
}
El siguiente método para esta clase es TryToConnect ()
Este método goth algunas cosas interesantes:
Crea el zócalo;
A continuación hago un bucle hasta que el zócalo está conectado.
En cada bucle, solo mantiene el subproceso durante 1 segundo, no queremos DOS del servidor XD
Con Connect () intentará conectarse al servidor. Si falla, lanzará una excepción, pero el programa mantendrá el programa conectado al servidor. Puedes usar un método Connect CallBack para esto, pero solo iré por llamar a un método cuando el Socket esté conectado.
Observe que el Cliente ahora está intentando conectarse a su PC local en el puerto 1234.
public void TryToConnect() { _connectingSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); while (!_connectingSocket.Connected) { Thread.Sleep(1000); try { _connectingSocket.Connect(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234)); } catch { } } SetupForReceiveing(); } } private void SetupForReceiveing() { // View Client Class bottom of Client Example Client.SetClient(_connectingSocket); Client.StartReceiving(); }
Enviando un mensaje al servidor.
Así que ahora tenemos una aplicación casi final o Socket. Lo único que no tenemos Jet es una clase para enviar un mensaje al servidor.
public class SendPacket
{
private Socket _sendSocked;
public SendPacket(Socket sendSocket)
{
_sendSocked = sendSocket;
}
public void Send(string data)
{
try
{
/* what hapends here:
1. Create a list of bytes
2. Add the length of the string to the list.
So if this message arrives at the server we can easily read the length of the coming message.
3. Add the message(string) bytes
*/
var fullPacket = new List<byte>();
fullPacket.AddRange(BitConverter.GetBytes(data.Length));
fullPacket.AddRange(Encoding.Default.GetBytes(data));
/* Send the message to the server we are currently connected to.
Or package stucture is {length of data 4 bytes (int32), actual data}*/
_sendSocked.Send(fullPacket.ToArray());
}
catch (Exception ex)
{
throw new Exception();
}
}
Finalmente, active dos botones uno para conectar y el otro para enviar un mensaje:
private void ConnectClick(object sender, EventArgs e)
{
Connector tpp = new Connector();
tpp.TryToConnect();
}
private void SendClick(object sender, EventArgs e)
{
Client.SendString("Test data from client");
}
La clase cliente que utilicé en este ejemplo.
public static void SetClient(Socket socket)
{
Id = 1;
Socket = socket;
Receive = new ReceivePacket(socket, Id);
SendPacket = new SendPacket(socket);
}
darse cuenta
La clase de recepción del servidor es la misma que la clase de recepción del cliente.
Conclusión
Ahora tienes un servidor y un cliente. Puedes trabajar este ejemplo básico. Por ejemplo, haga que el servidor también pueda recibir archivos u otros datos. O enviar un mensaje al cliente. En el servidor tienes una lista de clientes, de modo que cuando recibas algo con el cliente sabrás de dónde provienen.