Мы постарались сделать код переносимым (по крайней мере между Windows и Linux. Для того, чтобы собрать данный пример под Windows мы должны указать компоновщику на библиотеку wsock32.lib). Как видно из предыдущего примера, "открытие порта на прослушку" -- операция достаточно простая: необходимо создать сокет (socket(2)), заполнить и связать с сокетом структуру sockaddr_in (bind(2)), после чего вызвать listen(2). В данном примере сервер начинает "слушать" по порту 8100. По приходу запроса отрабатывает функция accept(2), которая создает новый сокет, оставляя "старый" готовым к приему нового соединения. Новый сокет готов к приему-передаче данных, мы посылаем приветствие и закрываем оба сокета (тонко, правда?

.
Обратим внимание на то, что accept является блокирующим вызовом, т. е. поток исполнения не проходит ниже этой строчки, пока не принято входящее соединение, и наша программа не может в это время делать ничего, кроме как "болтаться в accept'е". Кроме того, данный пример написан так, что принимает только одно соединение. Мы могли бы не закрывать первый сокет, а снова вызвать с ним accept для приема второго соединения, однако проблема блокировки вызовом accept все равно не была бы решена (несколько забегая вперед, заметим, что и функция приема данных из сокета recv(2) также является блокирующей). Часто данную проблему снимают организуя многопоточное (multithreaded) приложение, в котором каждое соединение обрабатывается в собственном потоке или, под UNIX, используют вызов разделения процесса fork(2) (кстати, ежели кто не понял, зачем двойки в скобках, -- это означает вторую секцию руководства). Добиться переносимости такого кода -- задача совсем нетривиальная, мы же пока не хотим привязываться к платформе, насколько это возможно, и потому воспользуемся вызовом select(2), который присутствует и в UNIX и в Windows. Функция select ожидает изменения статуса набора дескрипторов (в Windows поддерживаются только сокеты, а в UNIX -- файловые дескрипторы, коими сокеты и являются). Кроме того, нам потребуется перевести наши сокеты в неблокирующее состояние (non-blocking mode).
Все вышесказанное отражено в следующем примере, состоящем из трех файлов (по прежнему, в Windows следует подключать библиотеку wsock32.lib):
smsce.h
--------------------------------------------------------------------------------
#ifndef _SMSCE_H_
#ifdef _WIN32
#include <winsock2.h>
#define socklen_t int
#else
#include "unisock.h"
#include <fcntl.h>
#include <sys/time.h>
#endif
#define _SMSCE_H_
#endif /* _SMSCE_H_ */
--------------------------------------------------------------------------------
smsce.cpp
--------------------------------------------------------------------------------
#include <iostream>
#include <list>
#include <stdio.h>
#include "smsce.h"
#define SERVER_PORT 8200
#define RECVBUFSIZ 4096
using namespace std;
bool process_data(SOCKET sock)
{
static char buf[RECVBUFSIZ];
int received;
if ((received = recv(sock, buf, RECVBUFSIZ, 0)) != SOCKET_ERROR) {
buf[received] = '\0';
cout << (char *)buf << flush;
return true;
}
return false;
}
static void shutdown_socket(SOCKET *s)
{
if (*s != INVALID_SOCKET) {
shutdown (*s, SD_BOTH);
closesocket(*s);
*s = INVALID_SOCKET;
}
}
int main(int argc, char **argv)
{
#ifdef _WIN32
WSADATA WSAData;
/* Startup socket library */
if (WSAStartup(MAKEWORD(1,1), &WSAData) != 0)
perror("Can't initialize socket library");#endif
// list for server clients
typedef list<SOCKET> CL;
CL clients;
CL::iterator ii;
struct timeval tv;
fd_set readfds;
fd_set exfds;
SOCKET ssocket;
SOCKET accepted;
SOCKET maxfd;
bool true_value = true;
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(SERVER_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
// Creating the socket and setting it's optioins
ssocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ssocket == INVALID_SOCKET) {
perror("Can't create socket");
return 1;
}
setsockopt(ssocket, SOL_SOCKET, SO_REUSEADDR, (char *) &true_value, sizeof (true_value));
#ifdef _WIN32
ioctlsocket(ssocket, FIONBIO, (unsigned long *)&true_value); // Set to non-block mode
#else
fcntl(ssocket, F_SETFL, O_NONBLOCK); // Set to non-block mode
#endif // _WIN32
// Binding
if (bind(ssocket, (struct sockaddr *) &addr, sizeof (addr)) == SOCKET_ERROR) {
perror("Can't start listening");
return 1;
}
// sockaddr for client socket
struct sockaddr_in ca;
int cal = sizeof(ca);
// Start listening
if (listen (ssocket, SOMAXCONN) == SOCKET_ERROR) {
perror("Can't start listening");
return 1;
}
while (true) {
// Trying to accept connection (non-blocking mode)
// Please note that if no incoming connection presents at non-blocking
// socket accept returns with some error like EAGAIN or EWOULDBLOCK
if ((accepted = accept(ssocket, (struct sockaddr *)
&ca, (socklen_t *)&cal)) != SOCKET_ERROR)
clients.push_back(accepted);
// Preparing descriptor sets
FD_ZERO(&readfds);
FD_ZERO(&exfds);
FD_SET(ssocket, &exfds);
tv.tv_sec = 1;
tv.tv_usec = 0;
maxfd = ssocket;
for (ii = clients.begin(); ii != clients.end(); ++ii) {
FD_SET((SOCKET )*ii, &readfds);
FD_SET((SOCKET )*ii, &exfds);
maxfd = max(maxfd, (SOCKET )*ii);
}
// select failing breaks the work
if (select(maxfd + 1, &readfds, NULL, &exfds, &tv) == -1) break;
// On exception in server socket also breaks immediately
if(FD_ISSET(ssocket, &exfds)) break;
// Test events on client sockets
for (ii = clients.begin(); ii != clients.end(); ++ii) {
if (FD_ISSET(*ii, &exfds)) {
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
if ((ii = clients.erase(ii)) == clients.end()) break;
}
if (FD_ISSET(*ii, &readfds) && !process_data(*ii)) {
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
if ((ii = clients.erase(ii)) == clients.end()) break;
}
// Send data
if (*ii != INVALID_SOCKET)
if (send(*ii, "Connection is established ",
strlen("Connection is established "), 0) == SOCKET_ERROR)
if ((ii = clients.erase(ii)) == clients.end()) break;
}
}
for (ii = clients.begin(); ii != clients.end(); ++ii)
if (*ii != INVALID_SOCKET) shutdown_socket(&(*ii));
shutdown_socket(&ssocket);
return 0;
}