Problema del programa CSocket MFC
Realmente quiero ayudarte, pero solo conozco ASP. Se lo pediré a alguien~
Pero puedes consultar esto...
. /view/536074.htm
Hay una diferencia.
Comprueba si puede ayudarte.
CAsyncSocket
Puedes notarlo Al observar el nombre de la clase, se trata de una clase de encapsulación de Socket asíncrona y sin bloqueo. CAsyncSocket::Create() tiene un parámetro que especifica qué eventos de Socket desea procesar. Después de especificar el evento que le interesa, este Socket será. Se utiliza como modo asíncrono predeterminado. Entonces, ¿cómo CAsyncSocket le entrega eventos internamente?
La función Create() de CAsyncSocket, además de crear un SOCKET, también crea un objeto de ventana CSocketWnd y utiliza WSAAsyncSelect() para asociar este SOCKET con el objeto de ventana para permitir que el objeto de ventana procese el Eventos de socket (mensajes), sin embargo, después de que CSocketWnd recibe el evento de socket, simplemente vuelve a llamar a CAsyncSocket::OnReceive(), CAsyncSocket::OnSend(), CAsyncSocket::OnAccept(), CAsyncSocket::OnConnect() y otras funciones virtuales . Por lo tanto, las clases derivadas de CAsyncSocket solo necesitan agregar código de envío y recepción en estas funciones virtuales.
Después de la simplificación, el código aproximado es:
bool CAsyncSocket::Create( long lEvent) //El parámetro lEvent especifica el evento de Socket que le preocupa
{
m_hSocket = socket(PF_INET, SOCK_STREAM, 0); //Crea el socket en sí
CSocketWnd* pSockWnd = new CSocketWnd; //Crea una ventana que responde a eventos, la ventana real se crea cuando se llama a AfxSockInit().
pSockWnd->Create(...);
WSAAsyncSelect( m_hSocket, pSockWnd->m_hWnd, WM_SOCKET_NOTIFY, lEvent //Los eventos de socket están asociados con Windows
);}
vacío estático PASCAL CAsyncSocket::DoCallBack(WPARAM wParam, LPARAM lParam)
{
Zócalo CAsyncSocket;
Socket .Attach( (SOCKET)wParam ); //wParam es el identificador del Socket que desencadenó este evento
int nErrorCode = WSAGETSELECTERROR(lParam); //lParam es la combinación de código de error y código de evento
interruptor (WSAGETSELECTEVENT(lParam))
{
caso FD_READ:
pSocket->OnReceive(nErrorCode);
ruptura;
caso FD_WRITE:
pSocket->OnSend(nErrorCode);
ruptura;
caso FD_OOB:
pSocket->OnOutOfBandData(nErrorCode);
descanso;
caso FD_ACCEPT:
pSocket->OnAccept(nErrorCode);
ruptura;
caso FD_CONNECT:
pSocket->OnConnect(nErrorCode);
ruptura;
caso FD_CLOSE:
p>pSocket->OnClose(nErrorCode);
descanso;
}
}
La clase CSocketWnd es aproximadamente la siguiente:
BEGIN_MESSAGE_MAP(CSocketWnd, CWnd)
ON_MESSAGE(WM_SOCKET_NOTIFY, OnSocketNotify)
END_MESSAGE_MAP(
)
LRESULT CSocketWnd::OnSocketNotify(WPARAM wParam, LPARAM lParam)
{
CAsyncSocket::DoCallBack( wParam, lParam ); mensaje, devolución de llamada Función DoCallBack() de CAsyncSocket
devuelve 0L;
}
Sin embargo, lo más difícil de entender para los principiantes en la programación de Socket es también lo más Un punto importante a recordar en este artículo es que cuando el cliente usa CAsyncSocket::Connect(), a menudo devuelve un error WSAEWOULDBLOCK (lo mismo ocurre con otras llamadas a funciones. De hecho, esto no debe contarse como un error). es Socket Recuérdenos que dado que utiliza el método Socket sin bloqueo, la operación (de conexión) lleva tiempo y no se puede establecer instantáneamente. En este caso, podemos esperar hasta que se conecte exitosamente, por lo que muchos programadores duermen (0) después de llamar a Connect(), y luego usan constantemente WSAGetLastError() o CAsyncSocket::GetLastError() para verificar el error devuelto por el Socket hasta que se devuelva el éxito. Este es un enfoque equivocado, afirmar que no se puede lograr el propósito previsto. De hecho, podemos esperar a que se active el evento CAsyncSocket::OnConnect() después de la llamada a Connect(). CAsyncSocket::OnConnect() es para indicar que el Socket se ha conectado correctamente o que la conexión ha fallado por completo. En este punto, sabemos si la conexión del Socket se realizó correctamente o falló después de llamar a CAsyncSocket::OnConnect().
De manera similar, si Send() devuelve un error WSAEWOULDBLOCK, esperamos en OnSend(), si Recibir() devuelve un error WSAEWOULDBLOCK, esperamos en OnReceive(), y así sucesivamente.
Otro punto, que puede ser una dificultad, es que cuando el cliente llama a Connect() para conectarse al servidor, ¿cómo acepta el servidor() para establecer la conexión? La forma más sencilla es utilizar un nuevo objeto CAsyncSocket para establecer una conexión cuando el Socket de escucha recibe OnAccept(), por ejemplo:
void CMySocket::OnAccept( int ErrCode )
{
CMySocket* pSocket = new CMySocket;
Accept( *pSocket);
}
Entonces, el pSocket y el cliente anteriores Después el cliente ha establecido una conexión, la comunicación posterior es con el objeto pSocket y el cliente, y el Socket de escucha continúa escuchando. Una vez que otro cliente quiera conectarse al servidor, se volverá a llamar al OnAccept () anterior. Por supuesto, pSocket es el servidor que se comunica con el cliente. No activará el evento OnAccept() porque no está escuchando el Socket.
CSocket
CSocket es una clase de encapsulación de Socket de bloqueo síncrono 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, ingresa inmediatamente a un bucle de mensajes, que es para obtener el mensaje de interés de la cola de mensajes del hilo actual. el mensaje WM_PAINT, luego actualice la ventana. Si se obtiene el mensaje enviado por el Socket, se llamará a la función de devolución de llamada correspondiente (OnConnect (), etc.) según si el Socket tiene un código de error de operación.
El código simplificado aproximado es:
BOOL CSocket::Connect( ... )
{
if( !CAsyncSocket: :Connect( ... ) )
{
if( WSAGetLastError() == WSAEWOULDBLOCK ) //Debido a que la operación asincrónica lleva tiempo y no se puede completar de inmediato, Socket devuelve este error
{
// Ingrese al bucle de mensajes para ver el mensaje FD_CONNECT de la cola de mensajes del hilo Hasta que se reciba el mensaje FD_CONNECT, la conexión se considera exitosa.
while( PumpMessages( FD_CONNECT ) );
}
}
}
BOOL CSocket:: PumpMessages( UINT uEvent )
{
CWinThread* pThread = AfxGetThread();
while( bBlocking ) //bBlocking es solo una bandera para ver si el usuario Cancelar la llamada a Connect()
{
MSG msg;
if( PeekMessage( &msg, WM_SOCKET_NOTIFY ) )
{
if( msg.message == WM_SOCKET_NOTIFY && WSAGETSELECTEVENT(msg.lParam) == uStopFlag )
{
CAsyncSocket::DoCallBack( msg.wParam, msg .lParam );
devuelve VERDADERO;
}
}
más
{
OnMessagePending(); //Procesar otros mensajes en la cola de mensajes
pThread->OnIdle(-1);
}
}< / p>
}
BOOL CSocket::OnMessagePending()
{
Mensaje MSG;
if( PeekMessage( &msg , NULL, WM_PAINT, WM_PAINT, PM_REMOVE ) )
{ // Aquí solo nos preocupamos por el mensaje WM_PAINT para manejar el redibujado de la ventana principal durante el bloqueo
::DispatchMessage( &msg );
devuelve FALSO;
}
devuelve FALSO;
}
Otras funciones de CSocket, como Enviar ( ), Recibir(), Aceptar() están todos recibiendo WSAEWOULD
Cuando se produce un error BLOCK, se ingresa al bucle de mensajes PumpMessages(), de modo que un CAsyncSocket originalmente asincrónico se vuelve sincrónico cuando llega a la clase derivada CSocket.
Después de comprenderlo, podemos usar CSocket libremente. Por ejemplo, algunos programadores colocan operaciones de CSocket en un subproceso para implementar un Socket asíncrono de subprocesos múltiples (generalmente, sincronización + subprocesos múltiples es similar a asíncrono).