Работа с Socket с помощью MFC

Простейший пример приёма-передачи данных с использованием классов MFC.
Для этого минимально необходимо следующее:
Ну, во-первых, проинициализировать работу с сокетами. Если каркас проекта изначально создаётся Wizard-ом, то там есть специальная опция (Windows Sockets), позволяющая автоматически проводить инициализацию. Иначе можно просто вызвать AfxSocketInit(); Далее мы заводим класс CSock, производный от CAsyncSocket.

class CSock : public CAsyncSocket
{
public:
CSock(); virtual ~CSock(); virtual void OnReceive(int nErrorCode); };

Теперь о принципе работы: CAsyncSocket не зря назван асинхронным. Он не ждёт завершения выполнения начатой операции. Вместо этого при завершении операции он вызывает соответствующую виртуальную функцию. Например, при вызове функции Connect, она возвращается сразу, и поток её вызвавший продолжает работу, а уже потом, постепенно, начинают приходить извещения о завершении операции. Установилось соединение - вызывается функция OnConnect(), драйвера договорились о деталях сессии - вызывается OnSend() (то есть после вызова OnSend можно слать данные, хотя фактически их можно слать сразу после после OnConnect).
В нашем классе нужно перегрузить функцию OnReceive. Она вызовется, когда кто-нибудь подключённый попытается прислать данные.

void CSock::OnReceive(int nErrorCode)
{
	char Data[200];
int iLen=Receive(Data,200); Data[iLen]=0;
//iLen - длина принятых данных AfxMessageBox(Data);
}

В общих словах, принцип работы тут такой: при приёме данных драйвер сетевой карты копирует их в специальный буфер, и посылает приложению сообщение, что данные могут быть прочитаны. То есть при работе с большими сообщениями нельзя принять за один раз данных больше, чем помещается в буфер. Можно, конечно изменить размер буфера (используя SetSockOpt), но это не самый надёжный способ обеспечить, что всё сообщение будет принято за один раз.
Теперь сервер. Опять перегружаем CAsyncSocket (ну или можно наш CSock),

class CServer : public CAsyncSocket
{
	public:
	CServer(int iPort=3333);
	virtual ~CServer();
	virtual void OnAccept(int nErrorCode);
	protected:
	//заводим переменную типа CSock, к которому мы и 
	//будем подключать тех, кто пытается присоединиться
	CSock m_Socket;
};

Запускаем сервер: слушать он должен какой-то определённый порт (у нас это будет 3333), на нём мы и создаём сокет (Create), а потом заставляем слушать (Listen):

 CServer::CServer(int iPort) 
{
	Create(iPort); 
	Listen(); 
} 

При попытке подключения вызывается функция OnAccept. В ней мы должны подключить того, кто к нам просится к заранее приготовленному сокету. В данном примере имеется только одно свободное гнездо, к которому можно кого-нибудь подключить. Очевидно, что если мы хотим параллельно обрабатывать несколько подключений, нужно иметь и несколько свободных сокетов.

 


void CServer::OnAccept(int nErrorCode) {
Accept(m_Socket); }


Итак, мы приняли входящее соединение и теперь можем слать данные через это соединение (m_Socket.Send(“Message”,7), где 7 - количество посылаемых байт). Если же данные пошлёт тот, кто подключился, наш m_Socket вызовет функцию OnReceive, описанную выше.
И напоследок маленький нюанс: При вызове функции OnAccept нужно обязательно выполнить Accept, иначе при последующих попытках присоединиться OnAccept не вызывается. То есть, если у нас по каким-то причинам нет возможности установить новое соединение, (например, нет свободных гнёзд), а в это время ещё кто-то пытается подключиться, желательно иметь специальный “резервный” сокет, к которому можно подключить и сразу отключить ненужное (лишнее) соединение.



Опубликовал admin
26 Июн, Суббота 2004г.



Программирование для чайников.