Связываем socket
После того, как socket создан, нам надо связать его с адресом. Связь socket’а с адресом происходит с помощью функции bind. Подробно о функции bind прочитайте в первой части статьи. Для функции bind, нам необходимо заполнить структуру sockaddr_in. Делается это с помощью функции SetServerSockAddr
void SetServerSockAddr(sockaddr_in *pSockAddr, int portNumber)
{
// Устанавливаем адресное семейство, номер порта и определяем IP
pSockAddr->sin_family = AF_INET;
pSockAddr->sin_port = htons(portNumber);
pSockAddr->sin_addr.S_un.S_addr = INADDR_ANY;
}
Эта функция вызывается в функции RunServer:
// Связывае socket
cout << "Binding socket... ";
SetServerSockAddr(&sockAddr, portNumber);
if (bind(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr))!=0)
throw ROTException("could not bind socket.");
cout << "bound.\n";
«Пусть socket слушает»
Если socket успешно связан, его надо установить в режим прослушивания. Как только этот режим будет установлен, у сервера смогут запрашивать соединения. Установка режима прослушивание осуществляется с помощью функции listen
// Устанавливаем socket в режим прослушивания
cout << "Putting socket in listening mode... ";
if (listen(hSocket, SOMAXCONN)!=0)
throw ROTException("could not put socket in listening mode.");
cout << "done.\n";
В функцию передаются два параметра: первый – socket, которые будет «слушать», второй – длина очереди ждущих соединений. Обычно для второго параметра отлично подходит значение SOMAXCONN (подробнее о нем можете прочесть в первой части статьи).
Принимаем соединение
Что бы принимать входящие соединения, надо использовать функцию accept. Эта функция заблокирует Вашу программу, до тех пор, пока соединение не установится и функция не вернет дескриптор socket’а, по которому установлено соединение. Важно понимать, что socket сервера – всего лишь средство для установления соединения с клиентами. После того, как Вы создадите соединение, будет создан новый socket. Именно поэтому socket’у Вы и будете обмениваться данными с клиентом. Причем это надо иметь в виду, т.к. многие пытаются передать данные по прослушивающему socket’у.
Кроме разрешения соединения с клиентом и возвращения дескриптора socket’а, по которому будет происходить соединение, функция accept так же заполняет структуру sockaddr_in информацией о клиенте. В нашем примере эта информация будет использоваться для вывода IP адреса и номера порта клиента.
sockaddr_in clientSockAddr;
int clientSockSize = sizeof(clientSockAddr);
// Принимаем соединение:
hClientSocket = accept(hSocket,
reinterpret_cast<sockaddr*>(&clientSockAddr),
&clientSockSize);
// Проверяем успешно ли принято соединение
if (hClientSocket==INVALID_SOCKET)
throw ROTException("accept function failed.");
cout << "accepted.\n";
// Обрабатываем соединение:
HandleConnection(hClientSocket, clientSockAddr);
Я думаю этот код почти понятен. Полностью понятным он станет после того, как я расскажу про функцию HandleConnection.
HandleConnection
Функция HandleConnection обрабатывает соединение. Передаваемые параметры понятны из их названия. Сначала функция показывает краткую информацию о клиенте.
void HandleConnection(SOCKET hClientSocket, const sockaddr_in &sockAddr)
{
// Выводим информацию о подключенном клиенте
cout << "Connected with " << GetHostDescription(sockAddr) << ".\n";
А функция GetHostDescription как раз и формирует эту информацию
string GetHostDescription(const sockaddr_in &sockAddr)
{
ostringstream stream;
stream << inet_ntoa(sockAddr.sin_addr) << ":" << ntohs(sockAddr.sin_port);
return stream.str();
}
Теперь опишем функцию, которая осуществляет rot13. Эта функция циклически вызывается при завершении функции recv, до тех пор, пока recv не вернет 0. Каждый раз, когда приходят данные, функция преобразует их и возвращает клиенту.
void rot13(char *pBuffer, int size)
{
for(int i=0;i<size;i++)
{
char c = pBuffer<span style="font-style:italic">;
if ((c >= 'a' && c < 'n') || (c >= 'A' && c < 'N') )
c += 13;
else if ((c>='n' && c <= 'z') || (c>='N' && c <= 'Z'))
c -= 13;
else
continue;
pBuffer[i] = c;
}
}
Ну и основной цикл этой функции прост. В нем мы сначала принимает данные от клиента, посредством recv, кодируем их и отправляем пользователю с помощью send
// Считывем данные
while(true)
{
int retval;
retval = recv(hClientSocket, tempBuffer, sizeof(tempBuffer), 0);
if (retval==0)
{
break; // Соединение было закрыто
}
else if (retval==SOCKET_ERROR)
{
throw ROTException("socket error while receiving.");
}
else
{
//rot13 кодирует данные и отправляет их обратоно
rot13(tempBuffer, retval);
if (send(hClientSocket, tempBuffer, retval, 0)==SOCKET_ERROR)
throw ROTException("socket error while sending.");
}
}
cout << "Connection closed.\n";
