Escriba conocimientos esenciales sobre funciones como la visualización del tráfico de red en C#. (por favor explique)

Primero puede obtener información específica sobre P2P, "Tutorial de ejemplo de programación de red C#". Este libro habla sobre el desarrollo de protocolos de red.

Lo siguiente es lo que encontré en línea. un vistazo a la información sobre el desarrollo P2P

El uso de Raw Socket de C# para implementar el monitoreo de paquetes de red se puede usar para la programación P2P

Cuando se habla de programación de sockets, se puede pensar en QQ e IE. , pero no mal. También hay muchas herramientas de red como P2P, NetMeeting y otras aplicaciones implementadas en la capa de aplicación, que también se implementan mediante sockets. Socket es una interfaz de programación de red implementada en la capa de aplicación de red Windows Socket incluye un conjunto de componentes del sistema que aprovechan al máximo las características basadas en mensajes de Microsoft Windows. La versión 1.1 de la especificación Socket se lanzó en enero de 1993 y fue ampliamente utilizada en el sistema operativo Windows 9x que apareció posteriormente. La versión 2.2 de la especificación de socket (su versión en la plataforma Windows es Winsock2.2, también llamada Winsock2) se lanzó en mayo de 1996. Windows NT 5.0 y versiones posteriores de los sistemas Windows admiten Winsock2. En Winsock2, se admiten múltiples protocolos de transmisión. modelo de E/S superpuesto, control de calidad del servicio, etc.

Este artículo le presenta algo de programación de Windows Sockets sobre sockets sin formato (Raw Socket) implementados en C#, así como la tecnología de monitoreo de paquetes de red implementada sobre esta base. En comparación con Winsock1, lo más obvio de Winsock2 es que admite el tipo de socket Raw Socket. Al usar Raw Socket, la tarjeta de red se puede configurar en modo promiscuo. En este modo, podemos recibir paquetes IP en la red, incluido el destino. Por supuesto, en lugar de paquetes IP nativos, a través de sockets sin formato, podemos controlar más libremente múltiples protocolos en Windows y controlar el mecanismo de transmisión subyacente de la red.

En el ejemplo de este artículo, implementé la clase RawSocket en el espacio de nombres nbyte.BasicClass, que contiene la tecnología central para nuestra implementación de monitoreo de paquetes.

Antes de implementar esta clase, debe escribir una estructura de encabezado IP para almacenar temporalmente información sobre el paquete de red:

[StructLayout(LayoutKind.Explicit)]

estructura pública IPHeader

p>

{

[FieldOffset(0)] byte público ip_verlen; //Longitud del encabezado de I4 dígitos + número de versión de IP de 4 dígitos

[FieldOffset(1)] byte público ip_tos; //tipo de servicio de 8 bits

[FieldOffset(2)] public ushort ip_totallength; //longitud total del paquete de 16 bits (bytes)

[FieldOffset( 4 )] public ushort ip_id; //identificador de 16 bits

[FieldOffset(6)] public ushort ip_offset; //indicador de 3 bits

[FieldOffset(8)] public byte ip_ttl ; //TTL de tiempo de vida de 8 bits

[FieldOffset(9)] public byte ip_protocol; //protocolo de 8 bits (TCP, UDP, ICMP, etc.)

[FieldOffset( 10)] public ushort ip_checksum; //suma de verificación del encabezado IP de 16 bits

[FieldOffset(12)] public uint ip_srcaddr; //dirección IP de origen de 32 bits

[FieldOffset( 16)] public uint ip_destaddr; // dirección IP de destino de 32 bits

}

De esta manera, cuando llega cada paquete, se puede forzar la conversión de tipo. se utilizará para convertir el flujo de datos en el paquete para cada objeto IPHeader.

Comencemos a escribir la clase RawSocket Al principio, definimos varios parámetros, que incluyen:

private bool error_occurred //Si el socket genera un error al recibir el paquete

p>

public bool KeepRunning; //Si continuar

private static int len_receive_buf; //La longitud del flujo de datos obtenido

byte [] recibir_buf_bytes; Bytes al

Socket privado socket = null; //Declarar el socket

También hay una constante:

const int SIO_RCVALL = unchecked((int )0x98000001 );//Escuche todos los paquetes de datos

SIO_RCVALL aquí le indica a RawSocket que reciba todos los paquetes de datos y se usará en la futura función IOContrl. En el siguiente constructor, algunos Inicialización de parámetros variables:

public RawSocket() //Constructor

{

error_occurred=false;

len_receive_buf = 4096

receive_buf_bytes =; new byte[len_receive_buf];

}

La siguiente función implementa la creación de RawSocket y lo conecta con el enlace del punto final (IPEndPoint: IP y puerto nativo):

public void CreateAndBindSocket(string IP) //Crea y vincula el socket

{

socket = new Socket(AddressFamily .InterNetwork, SocketType.Raw, ProtocolType.IP);

socket.Blocking = false; //Establece el socket en un estado sin bloqueo

socket.Bind(new IPEndPoint(IPAddress.Parse( IP), 0)); Vincular socket

if (SetSocketOption()==false) error_occurred=true;

}

donde, hay 3 parámetros en la oración socket = new Socket (AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP); al crear un socket:

El primer parámetro es establecer la familia de direcciones, MSDN. La descripción es "Especificar el esquema de direccionamiento utilizado por el Socket". instancia para resolver la dirección". Cuando desee vincular el socket al punto final (IPEndPoint), debe usar el miembro de InterNetwork, es decir, usar el formato de dirección IP versión 4, que también es la mayoría de la programación de sockets actual que utiliza un direccionamiento. esquema (AddressFamily).

El tipo de socket establecido por el segundo parámetro es el tipo Raw que utilizamos. SocketType es un tipo de datos enumerados. El tipo de socket Raw admite el acceso al protocolo de transmisión básico. Al utilizar SocketType.Raw, puede comunicarse no solo mediante el Protocolo de control de transmisión (Tcp) y el Protocolo de datagramas de usuario (Udp), sino también mediante el Protocolo de control de mensajes de Internet (Icmp) y el Protocolo de administración de grupos de Internet (Igmp). Su aplicación debe proporcionar el encabezado IP completo al enviar. El datagrama recibido se devuelve con sus encabezados IP y opciones sin cambios.

El tercer parámetro establece el tipo de protocolo. La clase Socket utiliza el tipo de datos de enumeración ProtocolType para notificar a la API de Windows Socket sobre el protocolo solicitado. Aquí se utiliza el protocolo IP, por lo que se debe utilizar el parámetro ProtocolType.IP.

Hay una función SetSocketOption personalizada en la función CreateAndBindSocket, que es diferente de SetSocketOption en la clase Socket. Lo que definimos aquí es SetSocketOption con función de control IO. Su definición es la siguiente:

.

private bool SetSocketOption() //Establecer socket sin formato

{

bool ret_value = true;

prueba

{

socket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.HeaderIncluded, 1);

byte []IN = nuevo byte[4]{1, 0, 0, 0};

p>

byte []OUT = new byte[4];

// Modo de operación de bajo nivel, acepta todos los paquetes de datos, este paso es la clave, el socket debe se debe configurar en raw y solo se puede utilizar el nivel IP SIO_RCVALL

int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT);

ret_code = OUT[0] + OUT[1 ] + OUT[2] + OUT[ 3]; // Combina 4 bytes de 8 bits en un entero de 32 bits

if(ret_code != 0) ret_value = false;

}

catch(SocketException)

{

ret_value = false;

}

return ret_value;

}

Entre ellos, al configurar las opciones del socket, el socket debe contener el encabezado IP; de lo contrario, la estructura IPHeader no se puede completar y no se puede obtener la información del paquete.

int ret_code = socket.IOControl(SIO_RCVALL, IN, OUT); es el paso más crítico de la función, porque en Windows no podemos usar la función Recibir para recibir datos en el socket sin formato. todos los paquetes IP se envían primero al núcleo del sistema y luego se transmiten al programa del usuario. Cuando se envía un paquete de socket raws (como syn), el núcleo no lo sabe y no hay ningún registro de los datos que se envían ni de la conexión. establecido, por lo tanto, cuando el host remoto responde, el núcleo del sistema descarta todos estos paquetes, para que no puedan llegar a la aplicación. Por lo tanto, no puede simplemente utilizar la función de recepción para recibir estos datagramas. Para lograr el propósito de recibir datos, debemos utilizar el rastreo para recibir todos los paquetes de datos que pasan y luego filtrarlos para dejar aquellos que satisfagan nuestras necesidades. Puede configurar SIO_RCVALL para recibir todos los paquetes de datos en la red. A continuación, introduzcamos la función IOControl. MSDN explica que configura el socket en modo de operación de bajo nivel. ¿Cuál es el método de operación de bajo nivel? De hecho, esta función es muy similar a la función WSAIoctl en la API.

La función WSAIoctl se define de la siguiente manera:

int WSAIoctl(

SOCKET s, //Un socket especificado

DWORD dwIoControlCode, //Código de operación de control

DWORD dwIoControlCode, //Código de operación de control

LPVOID lpvInBuffer, //Puntero al flujo de datos de entrada

DWORD cbInBuffer, //Tamaño del flujo de datos de entrada (número de bytes)

LPVOID lpvOutBuffer, / / Puntero al flujo de datos de salida

DWORD cbOutBuffer, //El tamaño del flujo de datos de salida (número de bytes)

LPDWORD lpcbBytesReturned, //Valor real que apunta al número de flujos de bytes de salida

LPWSAOVERLAPPED lpOverlapped, //Apunta a una estructura WSAOVERLAPPED

LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine//Apunta a la rutina ejecutada cuando se completa la operación

);

La función IOControl de C# no es tan complicada como la función WSAIoctl. Solo incluye tres parámetros: código de operación de control, flujo de bytes de entrada y flujo de bytes de salida, pero estos tres parámetros son suficientes. Vemos que en la función se define una matriz de bytes: byte []IN = nuevo byte[4]{1, 0, 0, 0}. De hecho es un DWORD o Int32 con valor 1, y lo mismo. byte []OUT = new byte[4]; Además, integra un int como valor señalado por el parámetro lpcbBytesReturned en la función WSAIoctl.

Debido a que pueden ocurrir errores al configurar las opciones de socket, debe pasar el indicador de error como un valor:

public bool ErrorOccurred

{

get

{

return error_occurred;

}

}

Datos implementados por la siguiente función Recepción de paquetes:

//Analiza el paquete de datos recibido, forma el objeto de clase de datos del evento PacketArrivedEventArgs y activa el evento PacketArrival

Recepción vacía privada insegura (byte [] buf, int len )

{

byte temp_protocol=0;

uint temp_version=0;

uint temp_ip_srcaddr=0;

uint temp_ip_destaddr=0;

short temp_srcport=0;

short temp_dstport=0;

IPAddress temp_ip;

PacketArrivedEventArgs e = new PacketArrivedEventArgs();//Nuevo evento de información de paquete de red

fixed(byte *fixed_buf = buf)

{

IPHeader * head = (IPHeader * ) fix_buf;//Integrar el flujo de datos en una estructura IPHeader

e.HeaderLength=(uint)(head->ip_verlen & 0x0F) << 2;

temp_protocol = head- >ip_protocol;

switch(temp_protocol)//Extraer tipo de protocolo

{

caso 1: e.Protocol="ICMP";

caso 2: e.Protocol="IGMP"; ruptura;

caso 6: e.Protocol="TCP";

caso 17: e.Protocol ="UDP"; romper;

predeterminado: e.Protocol= "DESCONOCIDO"; romper;

}

temp_version =(uint)(head-> ip_verlen & 0xF0) >> 4;//Extraer versión del protocolo IP

e.IPVersion = temp_version.ToString();

//La siguiente declaración extrae otros elementos en el objeto PacketArrivedEventArgs Parámetros

temp_ip_srcaddr = head->ip_srcaddr;

temp_ip_destaddr = head->ip_destaddr;

temp_ip = nueva dirección IP(temp_ip_srcaddr);

e.OriginationAddress =temp_ip.ToString();

temp_ip = nueva dirección IP(temp_ip_destaddr);

e.DestinationAddress = temp_ip.To

String();

temp_srcport = *(short *)&fixed_buf[e.HeaderLength];

temp_dstport = *(short *)&fixed_buf[e.HeaderLength+2]; p>

e.OriginationPort=IPAddress.NetworkToHostOrder(temp_srcport).ToString();

e.DestinationPort=IPAddress.NetworkToHostOrder(temp_dstport).ToString();

e .PacketLength =(uint)len;

e.MessageLength =(uint)len - e.HeaderLength;

e.ReceiveBuffer=buf;

// Asigna el encabezado IP en buf al IPHeaderBuffer en PacketArrivedEventArgs

Array.Copy(buf,0,e.IPHeaderBuffer,0,(int)e.HeaderLength);

/ / Asigna el contenido del paquete en buf al MessageBuffer en PacketArrivedEventArgs

Array.Copy(buf,(int)e.HeaderLength,e.MessageBuffer,0,(int)e.MessageLength);

}

//Activar evento PacketArrival

OnPacketArrival(e);

}

Todos lo notaron, arriba En Para la función, utilizamos el llamado código inseguro, como punteros. Se puede ver que las operaciones primitivas como punteros y operaciones de desplazamiento en C# también pueden brindar comodidad de programación a los programadores. Declare el objeto de clase PacketArrivedEventArgs en la función para pasar la información del paquete a través del evento a través de la función OnPacketArrival(e). La clase PacketArrivedEventArgs es una clase anidada en la clase RawSocket. Hereda la clase de evento del sistema (Evento) y encapsula la IP, el puerto, el protocolo y otra información contenida en el encabezado del paquete.

En la función que comienza a recibir paquetes de datos, utilizamos el método de operación asincrónica. La siguiente función abre la interfaz de escucha asincrónica:

public void Run() //Comenzar a escuchar

{

IAsyncResult ar = socket.BeginReceive(receive_buf_bytes, 0, len_receive_buf, SocketFlags.None, new AsyncCallback(CallReceive), this);

}

El socket. La función BeginReceive devuelve una interfaz de operación asincrónica y declara la función de devolución de llamada asincrónica CallReceive en la función generada BeginReceive de esta interfaz, y transmite los datos de red recibidos aceived_buf_bytes, de modo que se pueda utilizar una devolución de llamada asincrónica con un parámetro de interfaz de operación asincrónica. La función recibe continuamente paquetes de datos:

private void CallReceive(IAsyncResult ar)//Devolución de llamada asincrónica

{

intceived_bytes;

received_bytes = socket.EndReceive(ar);

Recibir(receive_buf_bytes,ceived_bytes);

if (KeepRunning) Run();

}

Esta función recibe un nuevo paquete de datos después de suspender o finalizar la lectura asincrónica. Esto garantiza que el programa pueda detectar cada paquete de datos.

Lo siguiente se logra declarando un identificador de evento proxy para comunicarse con el mundo exterior:

public delegado void PacketArrivedEventHandler(Object sender, PacketArrivedEventArgs args);

// Identificador de evento: se activa un evento cuando llega un paquete

evento público PacketArrivedEventHandler PacketArrival;//Declara la función de controlador de tiempo

De esta manera se puede obtener la información del paquete. y la función de devolución de llamada asíncrona se puede utilizar para mejorar la eficiencia de la recepción de paquetes de datos y transmitir información del paquete al mundo exterior a través de eventos de proxy. Ahora que se puede distribuir toda la información del paquete, se puede realizar el análisis del paquete de datos :) Sin embargo, la tarea de RawSocket aún no ha terminado, finalmente no espere cerrar el socket:

public void Shutdown( ) //Cerrar socket sin formato

{

if(socket != null)

{

socket .Shutdown(SocketShutdown.Both);

socket.Close();

}

}

Lo anterior presenta la clase RawSocket para obtener el paquete construyendo la información del encabezado IP en él, y realiza la recepción de paquetes de datos a través de la función de devolución de llamada asíncrona, y utiliza el identificador del agente de tiempo y la clase de evento de información de paquete personalizado para enviar la información del paquete de datos, realizando así el monitoreo de paquetes de datos de red, para que podamos Se agregan algunas funciones externas para analizar los paquetes de datos.