200 puntos, comunicación de red C++, maestro, ¡entrarán aquellos con experiencia laboral! !
¡Amigos! ! ! Creo que debería ser el siguiente
CSocket es una clase de encapsulación de Socket de bloqueo sincrónico derivada de MFC basada en CAsyncSocket. ¿Cómo convierte CAsyncSocket en sincrónico y responde al mismo evento de Socket? De hecho, es muy simple cuando Connect() devuelve el error WSAEWOULDBLOCK, CSocket no espera en las funciones del terminal de eventos de OnConnect() y OnReceive(). Primero debe comprender cómo los eventos de Socket llegan a estas funciones de eventos. El objeto de ventana CSocketWnd vuelve a llamar a estas funciones de procesamiento de eventos, y el objeto de ventana recibe eventos del Socket y los distribuye a través de la cola de mensajes del hilo. En resumen, el evento Socket se envía primero al objeto de ventana CSocketWnd como un mensaje. Este mensaje debe distribuirse a través de la cola de mensajes del hilo. Finalmente, el objeto de ventana CSocketWnd llama a la función de devolución de llamada correspondiente (OnConnect (), etc.) después de recibirlo. estos mensajes.
Por lo tanto, después de que CSocket llama a Connect(), si devuelve un error WSAEWOULDBLOCK, inmediatamente llama a una función PumpMessage(...) para extraer mensajes, que es para recuperar el mensaje de interés de la cola de mensajes del hilo actual. encuentra las siguientes situaciones: 1 Extraído (Eliminar de la cola de mensajes), el mensaje WM_SOCKET_NOTIFY enviado por un Socket que el usuario está utilizando y el evento FD_XXX correspondiente devuelve True 2 Extraído (Eliminado de la cola de mensajes) Eliminar), el mensaje WM_SOCKET_NOTIFY. enviado por un Socket que el usuario está usando y el evento FD_Close correspondiente, devuelve True 3 Extraer (eliminar de la cola de mensajes), el mensaje WM_TIMER del temporizador establecido por PumpMessage (..), el evento TimeOut es una variable miembro de CSocket. , m_nTimeOut=2000ms, devuelve True 4. El usuario llamó a CancelBlockingCall(), estableció el código de error en WSAEINTR (interrumpido) y devolvió False 5. El usuario no ha recibido el WM_SOCKET_NOTIFY enviado por un Socket que el usuario está utilizando y los mensajes correspondientes. Eventos FD_XXX, pero si se obtienen los mensajes WM_SOCKET_NOTIFY y los mensajes correspondientes de otros Sockets en el mismo hilo, estos mensajes se agregarán a una cola auxiliar para su posterior procesamiento. 6 No se obtuvo ningún mensaje WM_SOCKET_NOTIFY, comience a verificar (sin eliminarlo). pero verificando) si hay otros mensajes en la cola de mensajes de este hilo. Si los hay, llame a la función virtual OnMessagePending() para procesar estos mensajes (los usuarios de OnMessagePending() pueden personalizar Al bloquear, el mensaje que el usuario desea procesar), de lo contrario, llame a WaitMessage () para comenzar a esperar la llegada del mensaje. La descripción del código es la siguiente: A Veamos primero Connect, porque la implementación de bloqueo de Connect es la misma que Accept, Recibir, RecibirDe, Enviar, EnviarA. todo un poco diferente Tal vez te preguntes por qué es ConnectHelper(...) en lugar de Connect(...) De hecho, ConnectHelper(...) es lo que Connect(..) realmente llama, de la siguiente manera: BOOL. CAsyncSocket: :Connect(const SOCKADDR* lpSockAddr, int nSockAddrLen) { return ConnectHelper(lpSockAddr, nSockAddrLen } //ConnectHelper(...) es una función virtual //Heredada de CAsyncSocket, Csocket ha sido redefinida //Esto es. por qué La razón por la que CSocket se hereda de Public BOOL CSocket::ConnectHelper(...) { //Una vez llamado, primero verifique si hay una operación de bloqueo actualmente en progreso //Si es así, regrese inmediatamente y establezca el código de error... ...... m_nConnectError = -1; //Tenga en cuenta que solo llama a CAsyncSocket::ConnectHelper( ... ) if( !CAsyncSocket::ConnectHelper( ... ) ) { //Debido a Connect(.. .)Exigirse realizar un protocolo de enlace de tres pasos con el servidor//Es decir, debe enviar un paquete y esperar una respuesta (
Esta es //la razón por la que se bloqueará la API de socket que involucra operaciones de conexión y envío de datos) //Por lo tanto, CAsyncSocket::ConnectHelper(...) regresará inmediatamente, //y establecerá el error en WSAEWOULDBLOCK (llamar a esta función provocará el bloqueo ) if( WSAGetLastError() == WSAEWOULDBLOCK ) { //Ingrese al bucle de mensajes para ver el mensaje FD_CONNECT de la cola de mensajes del hilo, //Reciba el mensaje FD_CONNECT (m_nConnectError se modificará en PumpMessage), regrese // o WM_TIMER/FD_CLOSE (devuelve verdadero, pero m_nConnectError no se modificará), //Continúa llamando a PumpMessage para obtener mensajes //o errores, luego devuelve socket_error while( PumpMessages( FD_CONNECT ) ) { if (m_nConnectError != -1) { WSASetLastError(m_nConnectError) ; return ( m_nConnectError == 0); } } //end while } return false; } return true } //Se configurará un temporizador en PumpMessages, el tiempo es m_nTimeOut=2000ms //Si dentro de este tiempo, no hay ningún mensaje aún recibido Si es así, devuelva BOOL CSocket::PumpMessages( UINT uStopFlag ) { //Una vez ingresada a esta función, establezca el estado actual del Socket en bloqueo BOOL bBlocking = TRUE; .... ................................................. ........................... CWinThread* pThread = AfxGetThread(); //bEl bloqueo es un indicador, // se utiliza para determinar si el usuario cancela la llamada a Connect() //es decir, si se debe llamar a CancelBlockingCall() while( bBlocking ) { //#define WM_SOCKET_NOTIFY 0x0373 //#define WM_SOCKET_DEAD 0x0374 MSG msg //Simplemente obtenga los mensajes WM_SOCKET_NOTIFY y WM_SOCKET_DEAD aquí si ( ::PeekMessage(&msg, pState->m_hSocketWindow,WM_SOCKET_NOTIFY, WM_SOCKET_DEAD, PM_REMOVE)) { if (msg.message == WM_SOCKET_NOTIFY && (SOCKET)msg .wParam == m_hSocket) { //Este es el segundo caso de PumpMessage if ( WSAGETSELECTEVENT(msg.lParam) == FD_CLOSE) { descanso;}
//Este es el primer caso de PumpMessage if(WSAGETSELECTEVENT(msg.lParam) == uStopFlag) {......; break;} } //Este es el quinto caso de PumpMessage if (msg.wParam! = 0 || msg.lParam != 0) CSocket::AuxQueueAdd(msg.message, msg.wParam, msg.lParam } //Este es el tercer caso de PumpMessage else if (::PeekMessage(&msg, pState ->m_hSocketWindow); ,WM_TIMER, WM_TIMER, PM_REMOVE)) { break;} //Este es el sexto caso de PumpMessage if (bPeek && ::PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE)) { if (OnMessagePending() ) { } else { WaitMessage(); ..... } } }//end while ////Este es el cuarto caso de PumpMessage if (!bBlocking) { WSASetLastError(WSAEINTR); return FALSE; el mensaje WM_SOCKET_NOTIFY a la cola de mensajes del subproceso Creat CSocketWnd // para procesar otros mensajes de Socket::PostMessage(pState->m_hSocketWindow, WM_SOCKET_NOTIFY, 0, 0) return TRUE; / De hecho, la implementación de CSocket determina que la aplicación no puede crear un socket en un subproceso, // y luego crear un nuevo subproceso para recibir específicamente, porque este nuevo subproceso nunca podrá obtener el evento FD_Read, // porque el El objeto CSocketWnd no se creó en este hilo, no puede recibir el mensaje enviado a CSocketWnd //(El cuerpo principal que recibe el mensaje en Windows es la ventana) //La mayor diferencia entre la implementación de Recibir y Conectar es //Conectarse continuamente llama a PumpMessage(..) //Y Recibir se llama continuamente a sí mismo int CSocket::Receive(void* lpBuf, int nBufLen, int nFlags) { if (m_pbBlocking!= NULL) { WSASetLastError(WSAEINPROGRESS) return FALSE;
int nResult; while ((nResult = CAsyncSocket::Receive(lpBuf, nBufLen, nFlags)) == SOCKET_ERROR) { if (GetLastError() == WSAEWOULDBLOCK) { //Una vez extraído FD_READ///FD_CLOSE///WM_TIMER / / Simplemente llame a CAsyncSocket::Receive(...) nuevamente if (!PumpMessages(FD_READ)) return SOCKET_ERROR; } else return SOCKET_ERROR } return nResult } Finalmente, resuma mis opiniones sobre CSocket, 1 Aunque resolvió El método para finalizar; el hilo de bloqueo es llamar a CancelBlockingCall, pero el modo de subprocesos múltiples no es adecuado para CSocket 2. CSocket y CAsyncSocket usan el modo de mensaje de Windows para integrar el procesamiento de la interfaz de primer plano y la comunicación de red de fondo en el modelo de paso de mensajes, pero es obvio que Una vez que la red backend está demasiado ocupada, es posible que el procesamiento frontend no se tenga en cuenta, por lo que CSocket solo puede hacer cosas pequeñas.
Se recomienda leer más MSDN, se indica claramente arriba y Sun Xin. ¡Vídeo VC++! !