Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* Пример использования функции listen()
- * (c) Лёха @ 2020
- */
- #include <stdio.h>
- #include <Windows.h>
- #include <WinSock2.h>
- /* Начнём с того, что это адрес и порт, на которых мы будем слушать
- * входящие соединения.
- */
- const char listenOnAddr[] = "127.0.0.1";
- const unsigned short listenOnPort = 80;
- const unsigned int listenTimeout = 3; // В секундах
- /* Описание клиента на той стороне сокета. Добавить поля по вкусу
- */
- typedef struct _client {
- int fd;
- } client_t;
- static client_t *_clients;
- static int numberOfClients, clientsArraySize;
- /* Получение структуры для нового клиента.
- */
- client_t *allocClient()
- {
- client_t *temp;
- /* Стратегия выделения памяти такая. При нехватке места в массиве клиентов увеличиваем
- * его в два раза. Константа 16 взята из головы, не парься
- */
- if(numberOfClients == clientsArraySize) {
- temp = (client_t *)realloc(_clients, sizeof(client_t)*(clientsArraySize? clientsArraySize: 16));
- if(NULL == temp)
- return NULL;
- }
- temp = _clients + numberOfClients;
- ++numberOfClients;
- return temp;
- }
- /* Поиск описателя клиента по его дескриптору сокета
- */
- client_t *findClientByFd(int fdToFind)
- {
- for(int i=0; i<numberOfClients; ++i) {
- if(_clients[i].fd == fdToFind)
- return &_clients[i];
- }
- return NULL;
- }
- int main(int argc, char *argv[])
- {
- struct hostent *host;
- SOCKET listenSock;
- struct timeval tmo;
- _clients = NULL;
- numberOfClients = 0;
- clientsArraySize = 0;
- /* Резольвим имя нашего хоста, на котором будем слушать коннекты от клиентов
- */
- host = gethostbyname(listenOnAddr);
- if(NULL == host) {
- printf("Неизвестное имя хоста %s\n", listenOnAddr);
- return EXIT_FAILURE;
- }
- /* Создаём сокет, на котором будем слушать
- */
- listenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // Или SOCK_DGRAM и IPPROTO_UDP, по вкусу
- if(INVALID_SOCKET == listenSock) {
- printf("Не удалось создать сокет\n");
- return EXIT_FAILURE;
- }
- /* Привязываемся
- */
- if(SOCKET_ERROR == bind(listenSock, host->h_addr_list[0], sizeof(host->h_addr_list[0]))) {
- printf("Не удалось привязаться к данному адресу: %s\n", listenOnAddr);
- return EXIT_FAILURE;
- }
- /* Слушаем входящие соединения
- */
- SOCKET s = listen(listenSock, 0);
- /* Вот тут начинается немного магии
- */
- fd_set rdfds;
- for(;;) {
- /* заполняем структуру fd_set для чтения номерами дескрипторов, которые надо
- * слушать. Можно, конечно, заполнить структуры, которые отвечают
- * за запись и ошибки сокетов, но на практике этого никогда не
- * приходилось использовать. Обычно сокеты доступны на запись всегда,
- * а ошибки ловятся другими способами.
- */
- FD_SET(listenSock, &rdfds); // Дескриптор который принимает соединения
- for(int i=0; i<numberOfClients; ++i) {
- FD_SET(_clients[i].fd, &rfds); // И все другие клиенты
- }
- tmo.tv_sec = listenTimeout;
- tmo.tv_usec = 0;
- /* Вызов select() закончится либо по событию, либо по таймауту.
- * +1, потому что к клиентам прибавлен 1 сокет, на котором
- * слушаем новые соединения.
- */
- int nfds = select(numberOfClients+1, &rdfds, NULL, NULL, &tmo);
- if(SOCKET_ERROR == nfds) {
- if(WSAEINTR == WSAGetLastError())
- continue;
- printf("Ошибка сетевой подсистемы %u\n", WSAGetLastError());
- break; // Лениво писать обрабтчик ошибок, прости :)
- }
- /* После вызова select(), fd_set rfds содержит номера дескрипторов,
- * у которых либо а) в случае listenSock произошло новое соединение,
- * либо у других есть данные, которые можно прочитать
- */
- if(FD_ISSET(s, &rdfds)) {
- struct sockaddr_in addr;
- int addrLen = sizeof(addr);
- SOCKET newClient = accept(s, (struct sockaddr *)&addr, addrLen);
- client_t *newClient = allocClient();
- if(NULL == newClient) {
- printf("Мало памяти\n");
- exit(EXIT_FAILURE);
- }
- /* Тут можно заполнить структуру, описывающую клиента. Инициализация
- * и всё такое
- */
- // -----
- /* Если вызов read() вернёт 0, то значит клиент закрыл соединение
- * После этого надо вызвать shutdown(SD_BOTH) и closesocket()
- */
- }
- for(int i=0; i<numberOfClients; ++i) {
- if(FD_ISSET(_clients[i].fd, &rdfds)) {
- /* Тут читаем из соответствующего сокета данные и обрабатываем их,
- * как надо
- */
- }
- }
- /* Если nfds равно нулю, значит случился таймаут. Тут можно
- * слать хартбит. Ну или что там
- */
- }
- return EXIT_SUCCESS;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement