Red de conocimiento informático - Material del sitio web - Cómo usar socket para implementar la función de TcpListener.pending

Cómo usar socket para implementar la función de TcpListener.pending

Implementación asincrónica TCP de programación de sockets basada en C#

1. Resumen

Esta publicación de blog explica la implementación asincrónica basada en el protocolo de comunicación TCP.

2. Plataforma experimental

Visual Studio 2010

3. Principios de implementación de comunicación asincrónica y métodos comunes

3.1 Establecer una conexión

p >

En modo síncrono, el método Aceptar se usa en el servidor para acceder a la solicitud de conexión y el método Conectar se usa en el cliente para conectarse al servidor. Por el contrario, en el modo asíncrono, el servidor puede usar el método BeginAccept y el método EndAccept para completar la tarea de conectarse al cliente, y el cliente usa el método BeginConnect y el método EndConnect para conectarse al servidor.

BeginAceptar intento de conexión entrante en modo asíncrono, lo que permite otras acciones sin tener que esperar a que se establezca la conexión antes de continuar ejecutando el programa posterior. Antes de llamar a BeginAccept, debe utilizar el método Listen para escuchar las solicitudes de conexión. El prototipo de función de BeginAccept es:

BeginAccept(AsyncCallback AsyncCallback, estado de Ojbect)

Parámetros:

AsyncCallBack: representa la función de devolución de llamada

estado: representa información de estado y debe garantizar que el estado contenga el identificador del socket

El proceso básico de usar BeginAccept es:

(1) Crear un punto final local y vincular un nuevo socket al punto final local;

(2) Escuchar en el puerto para detectar nuevas solicitudes de conexión;

(3) Solicite iniciar una nueva conexión y pasar una instancia de Socket o StateOjbect.

Código de referencia:

Copiar código

//Definir dirección IP

IPAddress local = IPAddress.Parse("127.0,0, 1");

IPEndPoint iep = new IPEndPoint(local,13000);

//Crear el objeto de socket del servidor

Socket server = new Socket(AddressFamily .InterNetwork,SocketType.Stream,ProtocolType.Tcp);

server.Bind(iep);

server.Listen(20);

server.BeginAccecpt (nuevo AsyncCallback(Accept),server);

Copiar código

Cuando se llama al método BeginAccept(), una vez que se produce una nueva conexión, se llamará a la función de devolución de llamada y el función de devolución de llamada Debe incluir el método EndAccept() utilizado para finalizar la operación de conexión de acceso.

La lista de parámetros de este método es Socket EndAccept(IAsyncResult iar)

El siguiente es un ejemplo de la función de devolución de llamada:

Copiar código

void Accept (IAsyncResult iar)

{

//Restaurar el socket original pasado

Socket MyServer = (Socket)iar.AsyncState;

//Llama al método EndAccept en el socket original y devuelve el nuevo socket

Socket service = MyServer.EndAccept(iar);

}

Copiar código

En este punto, el lado del servidor está listo. El cliente debe conectarse al host de forma remota a través del método BeginConnect y EndConnect. Al llamar al método BeginConnect, debe registrar la función de devolución de llamada correspondiente y pasar al menos una instancia de Socket al parámetro de estado para garantizar que el socket original se pueda usar en el método EndConnect. La siguiente es una llamada a BeginConnect:

Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp)

IPAddress ip=IPAddress.Parse("127.0. 0.1");

IPEndPoint iep=nuevo IPEndPoint(ip,13000);

socket.BeginConnect(es decir, nuevo AsyncCallback(Connect),socket);

EndConnect es un método de bloqueo que se utiliza para completar la conexión asincrónica del método BeginConnect y la solicitud del host remoto. Después de registrar la función de devolución de llamada, debe recibir el IASyncReuslt devuelto por el método BeginConnect como parámetro. La siguiente es una demostración de código:

Copiar código

void Connect(IAsyncResult iar)

{

Socket client=(Socket) iar.AsyncState;

prueba

{

client.EndConnect(iar);

}

captura (Excepción e)

{

Console.WriteLine(e.ToString());

}

finalmente

{

}

}

Copiar código

Además de utilizar el método anterior para establecer una conexión, puede también utilice el método en la clase TcpListener Realice el establecimiento de conexión. A continuación se muestra el uso del lado del servidor del método BeginAccetpTcpClient en la clase TcpListener para manejar un intento de conexión entrante.

El siguiente es el código que utiliza el método BeginAccetpTcpClient y el método EndAccetpTcpClient:

Copiar código

public static void DoBeginAccept(TcpListener listner)

{

//Comenzar a escuchar conexiones desde el cliente

Console.WriteLine("Esperando una conexión");

//Recibir conexiones

/ /Comenzar a prepararse para recibir Ingrese una nueva conexión Una vez que se intenta una nueva conexión, se llama a la función de devolución de llamada DoAcceptTcpCliet

listner.BeginAcceptTcpClient(new AsyncCallback(DoAcceptTcpCliet), listner);

}

//Procesar la conexión del cliente

public static void DoAcceptTcpCliet(IAsyncResult iar)

{

//Restaurar el TcpListner original object

TcpListener listener = (TcpListener)iar.AsyncState;

//Completa la acción de conexión y devuelve el nuevo TcpClient

TcpClient client = listener.EndAcceptTcpClient( iar);

Console.WriteLine("Conexión exitosa");

}

Copiar código

La lógica de procesamiento del código es:

(1) Llame al método BeginAccetpTcpClient para iniciar una nueva conexión. Cuando se produce la vista de conexión, se llama a la función de devolución de llamada para completar la operación de conexión. El método DoAcceptTcpCliet anterior obtiene la información pasada por BeginAcceptTcpClient a través del atributo AsyncState de la instancia listner;

(3) Después de obtener el objeto de escucha, utilícelo para llamar al método EndAcceptTcpClient, que devuelve un nuevo TcpClient que contiene información del cliente. .

El cliente puede utilizar el método BeginConnect y el método EndConnect para intentar establecer una conexión con el servidor. No hay diferencia entre este y el primer método.

Veamos el siguiente ejemplo:

Copiar código

public void doBeginConnect(IAsyncResult iar)

{

Socket client=(Socket )iar.AsyncState;

//Comienza a conectarse al host remoto

client.BeginConnect(serverIP[0],13000,requestCallBack,client);

Console.WriteLine ("Comenzar a conectarse al servidor");

}

solicitud de anulación privadaCallBack(IAsyncResult iar)

{

intentar

p>

{

//Restaurar el objeto TcpClient original

TcpClient client=(TcpClient)iar.AsyncState;

/ /

client.EndConnect(iar);

Console.WriteLine("Conectado correctamente al servidor {0}", client.Client.RemoteEndPoint);

}

catch(Exception e)

{

Console.WriteLine(e.ToString());

}

finalmente

{

}

}

Copiar código

Las anteriores son dos formas para establecer una conexión. Puede optar por utilizarlo según sea necesario.

3.2 Envío y recepción de datos

Una vez establecida la conexión del socket, se puede realizar la comunicación de datos entre el servidor y el cliente. Los sockets asíncronos utilizan los métodos BeginSend y EndSend para ser responsables del envío de datos. Tenga en cuenta que antes de llamar al método BeginSend, asegúrese de que ambas partes hayan establecido una conexión; de lo contrario, se producirá una excepción.

Código de demostración a continuación:

Copiar código

private static void Send(Socket handler, String data)

{

// Convertir el cadena de datos a bytes de datos usando codificación ASCII

byte[] byteData = Encoding.ASCII.GetBytes(data);

// Comienza a enviar los datos al dispositivo remoto p>

handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);

}

privado estático void SendCallback( IAsyncResult ar )

{

try

{

// Recupera el socket del objeto de estado

Socket handler = (Socket)ar.AsyncState;

// Completa el envío de datos al dispositivo remoto

int bytesSent = handler.EndSend(ar);

Console.WriteLine("Enviado {0} bytes al cliente.", bytesSent);

handler.Shutdown(SocketShutdown.Both);

handler.Close();

p>

}

catch (Excepción e)

{

Console.WriteLine(e.ToString());

}

}

Copiar código

Los datos se reciben a través de los métodos BeginReceive y EndReceive:

Copiar código

recibir vacío estático privado (cliente de socket)

{

probar

{

// Crear el estado object.

StateObject state = new StateObject();

state.workSocket = client;

// Comenzar a recibir los datos desde el dispositivo remoto

cliente.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, nuevo AsyncCallback(ReceiveCallback), estado);

}

catch (Excepción e)

{

Console.WriteLine(e.ToString());

}

}

private static void ReceiverCallback(IAsyncResult ar)

{

try

{

// Recuperar el objeto de estado y el socket del cliente

// del objeto de estado asincrónico

StateObject state = (StateObject)ar.AsyncState;

Socket client = state. workSocket;

// Lee datos del dispositivo remoto

int bytesRead = client.EndReceive(ar);

if (bytesRead > 0)

{

// Puede que haya más datos, así que almacene los datos recibidos hasta el momento

state.sb.Append(Encoding.ASCII.GetString(state. buffer, 0, bytesRead));

// Obtiene el resto de los datos

client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback( RecibirCallback), estado);

}

else

{

// Todos los datos han llegado, ponlos en respuesta.

if (state.sb.Length > 1)

{

respuesta = state.sb.ToString();

}

// Señal de que se han recibido todos los bytes

recibirDone.Set();

}

}

.

catch (Excepción e)

{

Console.WriteLine(e.ToString());

}

}

Copiar código

La lógica de procesamiento del código anterior es:

(1) Primero procese la conexión

El cliente del socket de comunicación obtiene la función de devolución de llamada conectada y luego comienza a recibir datos;

(2) Cuando los datos se envían al búfer, el método BeginReceive intenta leer la longitud de buffer.length de la matriz de buffer de datos y devuelve la cantidad de datos recibidos bytesRead. Finalmente reciba e imprima los datos.

Además de los métodos anteriores, también puede utilizar métodos de envío y recepción asincrónicos basados ​​en NetworkStream. La siguiente es una introducción al uso de métodos de envío y recepción asincrónicos basados ​​en NetworkStream.

NetworkStream utiliza los métodos BeginRead y EndRead para operaciones de lectura, y los métodos BeginWreite y EndWrete para operaciones de escritura. Vea los ejemplos a continuación:

Copiar código

static void. DataHandle( cliente TcpClient)

{

TcpClient tcpClient = client;

//Utilice el método GetStream de TcpClient para obtener el flujo de red

NetworkStream ns = tcpClient.GetStream();

//Comprueba si el flujo de red es legible

if(ns.CanRead)

{

//Definir búfer

byte[] lectura = nuevo byte[1024];

ns.BeginRead(read,0,read.Length,new AsyncCallback(myReadCallBack), ns);

}

else

{

Console.WriteLine("No se pueden leer los datos de transmisión de la red");

}

}

público estático void myReadCallBack(IAsyncResult iar)

{

NetworkStream ns = ( NetworkStream)iar .AsyncState;

byte[] lectura = nuevo byte[1024];

Datos de cadena = "";

int recv;

recv = ns.EndRead(iar);

datos = String.Concat(datos, Encoding.ASCII.GetString(read, 0, recv));

/ /Recibido La longitud del mensaje puede ser mayor que el tamaño total del búfer y el bucle se repite hasta que se completa la lectura

while (ns.DataAvailable)

{

ns.BeginRead(read, 0, read.Length, new AsyncCallback(myReadCallBack), ns);

}

//Imprimir

Consola. WriteLine("La información que recibiste es " + datos);

}

Copiar código

3.3 Problemas de sincronización en el bloqueo de programas y asincrónico

Proporcionada en .Net La clase EventWaitHandle se utiliza para representar eventos de sincronización de un hilo.

EventWaitHandle es un identificador de espera de eventos que permite que los subprocesos se envíen señales entre sí y esperen las señales de cada uno a través del sistema operativo para lograr la sincronización de subprocesos. Esta clase tiene 2 subclases, a saber, AutoRestEevnt (reinicio automático) y ManualRestEvent (reinicio manual). A continuación se muestran varios métodos para la sincronización de subprocesos:

(1) Método Rset: establece el estado del evento en un estado no terminado, lo que provoca que el subproceso se bloquee. El bloqueo de subprocesos aquí se refiere a permitir que otros subprocesos que necesitan esperar para bloquearse, es decir, permitir que se bloquee el subproceso que contiene el método WaitOne();

(2)Método Establecer: establece el estado del evento en terminado estado, permitiendo uno o más El hilo en espera continúa. Este método envía una señal al sistema operativo para cambiar un hilo en espera del estado de bloqueo para continuar ejecutándose, es decir, el hilo del método WaitOne ya no está bloqueado

(3) Método WaitOne: bloquea; el hilo actual hasta que el controlador de espera actual reciba una señal. Este método mantendrá este hilo en un estado bloqueado hasta que se reciba una señal, es decir, la ejecución puede continuar cuando otros procesos sin bloqueo llamen al método establecido.

Copiar código

public static void StartListening()

{

// Búfer de datos para datos entrantes

<. p> byte[] bytes = new Byte[1024];

// Establece el punto final local para el socket

// El nombre DNS de la computadora

<. p> // el oyente que se ejecuta es "host.contoso.com"

//IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());

//IPAddress ipAddress = ipHostInfo.AddressList[0];

IPAddress ipAddress = IPAddress.Parse("127.0.0.1");

IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);

// Crea un socket TCP/IP.

Oyente de socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);

// Vincula el socket al

//punto final local y escuche las conexiones entrantes

try

{

listener.Bind(localEndPoint);

listener.Listen(100);

while (true)

{

// Establece el evento en estado sin señal <. /p>

allDone.Reset();

// Inicia un socket asíncrono para escuchar las conexiones

Console.WriteLine("Esperando una conexión..." );

listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);

// Espere hasta que se establezca una conexión antes de continuar

allDone.WaitOne. ();

}

}

captura (Excepción e)

> {

Console.WriteLine(e.ToString());

}

Console.WriteLine("\nPresione ENTER para continuar...") ;

Console.Read();

}

Copiar código

La lógica del código anterior es:

(1) Probé el objeto ManualRestEvent para crear un identificador de espera y usé el método Rest para permitir que otros subprocesos se bloqueen antes de llamar al método BeginAccept;

(2) Para evitar que el socket siendo leído y escrito antes de que se complete la conexión, asegúrese de llamar a WaitOne después del método BeginAccept para poner el hilo en un estado de bloqueo.

Cuando se conecta una conexión, el sistema llamará automáticamente a la función de devolución de llamada, por lo que cuando se ejecuta el código en la función de devolución de llamada, significa que la conexión ha sido exitosa y se llama al método Set en el primera oración de la función para dejar que el hilo de espera pueda continuar ejecutándose