Red de conocimiento informático - Problemas con los teléfonos móviles - VC implementa la comunicación UDP más simple

VC implementa la comunicación UDP más simple

/ypxmaomao/blog/item/1bd9ba95e3aa224cd0135ebf.html

[Información del artículo] Autor: Zhang Xiaoming, Yang Jianhua, Qian Minghai Hora: 2003-06-28 Fuente: Editor de PCVC: Ark [Introducción del artículo] En el entorno Windows 95, basado en el protocolo TCP/IP, se utilizó Winsock para completar la transmisión de voz de un extremo a otro.

Resumen: en el entorno Windows 95, basado en El protocolo TCP/IP, Winsock, se utilizó para completar la transmisión de voz de un extremo a otro. Utilizando tecnología de doble socket, se explican los puntos clave del uso de las funciones principales y el método de aplicación basado en el mecanismo de selección asincrónico. Al mismo tiempo se proporcionan los programas de ejemplo correspondientes.

1. Introducción

Como sistema operativo para microcomputadoras, Windows 95 se ha integrado completamente en funciones de red y comunicación. No solo puede establecer una "red de igual a igual". en un entorno puro de Windows 95, pero también admite múltiples protocolos, como TCP/IP, IPX/SPX, NETBUI, etc. En el conjunto de protocolos TCP/IP, TPC es un protocolo orientado a la conexión que proporciona a los usuarios servicios confiables de flujo de bytes full-duplex. Tiene funciones como confirmación, control de flujo, multiplexación y sincronización, y es adecuado para la transferencia de datos. El protocolo UDP no tiene conexión, cada paquete lleva una dirección de destino completa y cada paquete se transmite de forma independiente en el sistema. No puede garantizar el orden de los paquetes, no recupera ni retransmite errores de paquetes y, por lo tanto, no garantiza la confiabilidad de la transmisión. Sin embargo, proporciona servicios de datagramas con alta eficiencia de transmisión y es adecuado para transmisión de voz, imágenes y mensajes de difusión en tiempo real. , etc. Transmisión de red.

La interfaz Winsock proporciona un nuevo método para la comunicación entre procesos. No solo se puede utilizar para la comunicación entre procesos en la misma máquina, sino que también admite funciones de comunicación de red. Con la introducción de Windows 95. Winsock se ha integrado oficialmente en los sistemas Windows, incluidas las interfaces de programación de 16 y 32 bits. Las herramientas de desarrollo Winsock también se pueden encontrar en compiladores de C como Borland C++ 4.0 y Visual C++ 2.0. Consisten principalmente en un archivo de encabezado llamado winsock.h y la biblioteca de vínculos dinámicos winsock.dll o wsodk32.dll. Se utilizan dos bibliotecas de enlaces dinámicos para aplicaciones Win16 y Win32 respectivamente.

Este artículo utiliza el protocolo UDP para implementar comunicación de red en tiempo real basada en los requisitos de transmisión de voz full-duplex. Utilice el entorno de compilación VisualC++2.0 y su biblioteca de vínculos dinámicos se denomina wsock32.dll.

2. Puntos clave para el uso de las funciones principales

Al establecer enchufes duales, se puede lograr fácilmente una comunicación de red full-duplex.

1. Función de establecimiento de socket:

Socket SOCKET (familia int, tipo int, protocolo int)

Para protocolo UDP, escrito como:

p>

SOCKRET s;

s=socket(AF_INET,SOCK_DGRAM,0

o s=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP)

Para establecer dos sockets, se debe lograr la vinculación repetida de direcciones, es decir, cuando un socket se ha vinculado a una dirección local, para que otro socket reutilice la dirección, se debe llamar a la función bind (debe llamarse). antes de vincular el segundo socket, configure la opción de socket SO_REUSEADDR para el socket a través de la función setsockopt(). El estado de configuración de la opción del socket se puede obtener mediante la función getsockopt(). Cabe señalar que los números de puerto correspondientes a los dos sockets no pueden ser iguales. Además, también implica la configuración del búfer de socket. Según las regulaciones, el rango de configuración de cada área es: no menos de 512 bytes y más de 8k bytes. Según las necesidades, en este artículo se seleccionan 4k bytes.

2. Función de enlace de socket

int bind(SOCKET s,struct sockaddr_in*name,int namelen)

s es la interfaz que acaba de crear el socket, nombre puntos a un puntero a la estructura que describe el objeto de comunicación, namelen es la longitud de la estructura. Los componentes de esta estructura incluyen: dirección IP (correspondiente a nombre.sin_addr.s_addr), número de puerto (nombre.sin_puerto) y tipo de dirección (nombre.sin_familia, generalmente asignado a AF_INET, que indica una dirección de Internet).

(1) Cómo completar la dirección IP: en comunicación full-duplex, para convertir la dirección de notación de puntos correspondiente al nombre de usuario en una dirección IP en formato de entero largo de 32 bits, use inet_addr () función .

(2) Los números de puerto se utilizan para representar diferentes procesos (aplicaciones) en la misma computadora. Hay dos métodos de asignación: 1) El proceso puede permitir que el sistema asigne automáticamente un número de puerto al socket. especifique el número de puerto como 0 antes de llamar al enlace. Los números de puerto asignados automáticamente por el sistema están entre 1024 y 5000, y cualquier puerto TCP o UDP entre 1 y 1023 está reservado. El sistema no permite que ningún proceso utilice el puerto reservado a menos que su ID de usuario efectivo sea cero (superusuario).

2) El proceso puede especificar un puerto específico para el socket. Esto es útil para servidores que necesitan asignar un puerto común a un socket. El rango especificado está entre 1024 y 65536. Se puede especificar arbitrariamente.

En este programa, los números de puerto de los dos sockets se especifican como 2000 y 2001. El primero corresponde al socket de envío y el segundo corresponde al socket de recepción.

Para convertir el número de puerto de un número sin signo de 16 bits (número de tipo u_short) del orden de bytes del host al orden de bytes de la red, utilice la función htons().

Con base en las dos funciones anteriores, se puede proporcionar el fragmento de programa para establecer y vincular sockets duales.

//Establecer variables globales relevantes

SOCKET sr,ss;

HPSTR sockBufferS,sockBufferR

HANDLE hSendData,hReceiveData; /p>

DWROD dwDataSize=1024*4;

struct sockaddr_in therel.there2

#DEFINE LOCAL_HOST_ADDR 200.200.200.201

#DEFINE REMOTE_HOST - ADDR 200.200.200.202

#DEFINE LOCAL_HOST_PORT 2000

#DEFINE LOCAL_HOST_PORT 2001

//Función de establecimiento de socket

BOOL make_skt (HWND hwnd )

{

struct sockaddr_in aquí,aquí1

ss=socket(AF_INET,SOCK_DGRAM,0

sr=socket); (AF_INET,SOCK_DGRAM,0);

if((ss==INVALID_SOCKET)||(sr==INVALID_SOCKET))

{

Cuadro de mensaje(hwnd) , "¡Falló el establecimiento del socket!", "", MB_OK

return(FALSE

}

here.sin_family= AF_INET; >

here.sin_addr.s_addr=inet_addr(LOCAL_HOST_ADDR);

here.sin_port=htons(LICAL_HOST_PORT);

//otro socket

herel .sin_family=AF_INET;

herel.sin_addr.s_addr(LOCAL_HOST_ADDR);

herel.sin_port=htons(LOCAL_HOST_PORT1);

SocketBuffer ();// Configuración de bloqueo para el búfer de socket

setsockopt(ss,SOL_SOCKET,SO_SNDBUF,(char FAR*)sockBufferS,dwDataSize

if(bind(ss,(LPSOCKADDR)&here,sizeof); (aquí)))

{

MessageBox(hwnd, "¡Error en el enlace del socket!", "", MB_OK);

MessageBox(hwnd, " ¡Error al enviar el enlace del socket!", "", MB_OK); p>

return(FALSE);

}

setsockopt(sr SQL_SOCKET,SO_RCVBUF|SO_REUSEADDR,(char FAR*)

sockBufferR,dwDataSize)

if(bind(sr,(LPSOCKADDR)&here1,sizeof(here1)))

{

MessageBox(hwnd,"¡La recepción del enlace del socket definitivamente falló!", "", MB_OK

return(FALSE

}

); return(VERDADERO);

}

//Búfer de socket

Configuración

void sockBuffer(void)

{

hSendData=GlobalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize

if(!hSendData); )

{

MessageBox(hwnd, "¡Error en la ubicación del búfer de envío!", NULL,

MB_OK|MB_ICONEXCLAMATION

); return;

}

if((sockBufferS=GlobalLock(hSendData)==NULL)

{

MessageBox( hwnd, " ¡Error en el bloqueo del búfer de socket!", NULL,

MB_OK|MB_ICONEXCLAMATION);

GlobalFree(hRecordData[0];

return ;

}

hReceiveData=globalAlloc(GMEM_MOVEABLE|GMEM_SHARE,dwDataSize);

if(!hReceiveData)

{

MessageBox( hwnd, ""¡Error al recibir la ubicación del buffer del socket!", NULL

MB_OK|MB_ICONEXCLAMATION);

return;

}

if ((sockBufferT=Globallock(hReceiveData))=NULL)

MessageBox(hwnd, "¡Error en el bloqueo del búfer del socket!", NULL,

MB_OK|MB_ICONEXCLAMATION

);

GlobalFree(hRecordData[0]);

retorno

}

{

p>

3. Funciones de envío y recepción de datos;

int sendto(SOCKET s.char*buf,int len,int flags,struct sockaddr_in to,int

tolen); >int recvfrom(SOCKET s.char*buf,int len,int flags,struct sockaddr_in

fron,int*fromlen)

Entre ellos, el parámetro flags Generalmente se toma como 0.

La función recvfrom () en realidad lee un paquete de datos enviado por la función sendto () cuando los bytes de datos leídos son menores que el número especificado de recibidos, todos los datos se reciben y se devuelven. de bytes recibidos; cuando se leen más datos que el valor especificado, los datos sobrantes se descartarán en el modo de mensaje de datos. En el modo de transmisión, recvfrom() lee los datos restantes. Para enviar y recibir datos, se debe establecer un búfer de envío de datos y un búfer de recepción de datos. Regulación: Un datagrama en la capa IP no puede exceder los 64K (incluido el encabezado de datos). Cuando el búfer se configura demasiado o es demasiado grande, el establecimiento del socket a menudo falla debido a memoria insuficiente. Después de reducir el búfer, el error desapareció. Después de los experimentos, en este artículo se seleccionaron bytes de 4K.

Además, también debes prestar atención a la escritura de los últimos parámetros en estas dos funciones. El último parámetro de sendto() es un valor entero, mientras que el último parámetro de recvfrom() es un puntero. a un valor entero.

4. Función de cierre de socket: closesocket(SOCKET s)

Cuando finaliza la comunicación, el socket especificado debe cerrarse para liberar los recursos relacionados con él.

Al cerrar el socket, primero se deben liberar los distintos buffers bloqueados. El fragmento del programa es:

void CloseSocket(void)

{

GlobalUnlock(hSendData

GlobalFree(hSenddata); /p>

GlobalUnlock(hReceiveData);

GlobalFree(hReceiveDava

if(WSAAysncSelect(ss,hwnd,0,0)=SOCKET_ERROR)

{

MessageBos(hwnd, "¡Error al cerrar el socket de envío!", "", MB_OK

return;

}

);

if(WSAAysncSelect(sr,hwnd,0,0)==SOCKET_ERROR)

{

MessageBox(hwnd, "¡El socket receptor no pudo cerrarse!", " " , MB_OK);

retorno

}

WSACleanup();

closesockent(ss); >closesockent(sr);

return;

}

3. Funciones de programación de Winsock y mecanismo de selección asincrónica

1 Bloqueo y su método de procesamiento

En la comunicación de red, debido a la congestión de la red o al volumen excesivo de datos enviados al mismo tiempo, a menudo sucede que los datos intercambiados no se pueden transmitir en un corto período de tiempo y, por lo tanto, las funciones de envío y Los datos recibidos no pueden regresar, este fenómeno se llama bloqueo. Winsock proporciona dos formas de manejar funciones que pueden bloquear: formas de bloqueo y sin bloqueo. En el modo de bloqueo, la función que envía y recibe datos no puede regresar hasta que se complete la transmisión o se produzca un error después de ser llamada. Durante el período de bloqueo, la función bloqueada no llamará continuamente a la función del sistema GetMessage () para mantener el progreso normal del bucle de mensajes. Para el modo sin bloqueo, la función regresa inmediatamente después de ser llamada. Cuando se completa la transferencia, Winsock envía un mensaje previamente acordado al programa.

Al programar, debes intentar utilizar métodos sin bloqueo. Debido a que en el modo de bloqueo, el usuario puede intentar cerrar el programa mientras espera durante mucho tiempo. Debido a que el bucle de mensajes aún está funcionando, la ventana del programa puede cerrarse de esta manera, cuando la función regresa de la biblioteca de enlaces dinámicos de Winsock. el programa principal ha sido eliminado de la memoria y obviamente es extremadamente peligroso.

2 Uso de la función de selección asincrónica WSAAsyncSelect()

Winsock configura automáticamente el socket en modo sin bloqueo a través de WSAAsyncSelect(). La clave para usar Windows Sockets para implementar la programación de red de Windows es que proporciona acceso asincrónico basado en mensajes a eventos de red, que se utiliza para registrar eventos de red que interesan a la aplicación. Solicita a la DLL de Windows Sockets que envíe un mensaje a la ventana cuando detecta un evento de red que ocurre en el socket. Para el protocolo UDP, estos eventos de red son principalmente:

FD_READ espera recibir una notificación cuando el socket recibe datos (es decir, cuando está listo para leer);

FD_WRITE espera recibir una notificación cuando; el socket puede recibir una notificación cuando se envía el número (es decir, cuando la escritura está lista);

FD_CLOSE espera recibir una notificación cuando se cierra el socket

La variable de mensaje wParam indica el socket donde ocurrió el evento de red, variable El byte bajo de 1Param describe el evento de red que ocurrió y el byte alto contiene el código de error.

Por ejemplo, agregue una rama al bucle de mensajes de la función de ventana:

int ok=sizeof(SOCKADDR

case wMsg;

switch(1Param); )

{

case FD_READ:

//Leer datos del socket

if(recvfrom(sr.lpPlayData[j] , dwDataSize,0,(struct sockaddr FAR*)&there1,

(int FAR*)&ok)==SOCKET_ERROR0

{

MessageBox)hwnd," ¡Error en la recepción de datos!", "", MB_OK);

return(FALSE);

}

case FD_WRITE:

/ / Escribir datos en el socket

}

break;

Al preparar el programa, la función WSAAsyncSelect() debe colocarse de manera flexible en la ubicación correspondiente. según sea necesario. En el bucle de mensajes, se pueden encontrar otras explicaciones en la literatura [1]. Además, cabe señalar que el cuadro de mensaje en el fragmento de programa anterior está configurado principalmente para facilitar la depuración del programa y ya no aparecerá en el producto oficial. Al mismo tiempo, de acuerdo con el diseño tolerante a errores del programa, se debe establecer una función especial de procesamiento tolerante a fallas. Esta función manejará varios errores que puedan ocurrir en el programa y se establecerán varias medidas de manejo diferentes en función del grado de daño del error. De esta manera, se puede garantizar una comunicación fluida y confiable entre ambas partes.

4. Conclusión

Este artículo es uno de los contenidos importantes del proyecto de transmisión de red multimedia en la actualidad, combinado con tarjetas de voz full-duplex de hardware y otros equipos, full-duplex. La voz se ha realizado con éxito. El diseño de todo el sistema de transmisión multimedia se describirá en otro artículo.