server0451
245 строк · 8.4 Кб
1/******************* ОПИСАНИЕ *******************/
2
3/**
4* Имя файла: sockets.c
5* ----------------------------------------------------------------------------|---------------------------------------|
6* Назначение: работа с сокетами TCP/IP.
7* ----------------------------------------------------------------------------|---------------------------------------|
8* Примечания:
9*/
10
11
12/************ ДИРЕКТИВЫ ПРЕПРОЦЕССОРА ***********/
13
14/*--- Включения ---*/
15
16// Из стандартной библиотеки языка Си.
17#include <stdio.h>18#include <inttypes.h>19#include <string.h>20
21// Из библиотек POSIX.
22#include <unistd.h>23#include <netinet/in.h>24#include <signal.h>25
26/* Эти заголовочные файлы упоминаются в примерах серверов, написанных
27* на языке Си, но код компилируется и работает и без них. Оставляю на память.
28*/
29//#include <netdb.h>
30//#include <sys/socket.h>
31//#include <sys/types.h>
32
33// Настройки.
34#include "config_general.h"35
36// Локальные модули.
37#include "sockets.h"38#include "utilities.h"39#include "timestamp.h"40
41/******************** ФУНКЦИИ *******************/
42
43uint32_t sockets_init(int32_t *sockfd, int32_t port, uint32_t numconn, uint32_t verbosity_level)44{
45/*--- Создание сокета ---*/46
47*sockfd = socket(AF_INET, SOCK_STREAM, 0);48if (*sockfd < 0) {49return SOCKETS_INIT_ERR_CREATE;50} else if (verbosity_level > 0) {51printf("\n...socket successfully created.\n");52}53
54
55/*--- Установка опций сокетов ---*/56
57/* Условной компиляцией можно управлять с помощью соответствующих директив,58* приведённых в файле config_general.h.
59*/
60
61#ifdef SOCKOPT_SO_REUSEPORT62/* Разрешение заново использовать сочетание порта и адреса63* немедленно после закрытия предыдущего соединения.
64* Если это делается, то обязательно до вызова bind().
65*/
66int32_t so_reuseport = 1;67int32_t so_reuseport_result = setsockopt(*sockfd,68SOL_SOCKET,69SO_REUSEPORT,70&so_reuseport,71(socklen_t)sizeof(so_reuseport));72
73if (so_reuseport_result != 0) {74return SOCKETS_INIT_ERR_SETSOCKOPT;75} else if (verbosity_level > 1) {76printf("...setting SO_REUSEPORT socket option. Success.\n");77}78#endif79
80#ifdef SOCKOPT_SO_REUSEADDR81/* Разрешение заново использовать адрес82* немедленно после закрытия предыдущего соединения.
83* Если это делается, то обязательно до вызова bind().
84*/
85int32_t so_reuseaddr = 1;86int32_t so_reuseaddr_result = setsockopt(*sockfd,87SOL_SOCKET,88SO_REUSEADDR,89&so_reuseaddr,90(socklen_t)sizeof(so_reuseaddr));91
92if (so_reuseaddr_result != 0) {93return SOCKETS_INIT_ERR_SETSOCKOPT;94} else if (verbosity_level > 1) {95printf("...setting SO_REUSEADDR socket option. Success.\n");96}97#endif98
99#ifdef SOCKOPT_SO_LINGER100/* Установка времени ожидания полной передачи данных.101* Если это делается, то обязательно до вызова bind().
102*/
103struct linger so_linger;104so_linger.l_onoff = 1;105so_linger.l_linger = L_LINGER;106int32_t so_linger_result = setsockopt(*sockfd,107SOL_SOCKET,108SO_LINGER,109&so_linger,110(socklen_t)sizeof(so_linger));111
112if (so_linger_result != 0) {113return SOCKETS_INIT_ERR_SETSOCKOPT;114} else if (verbosity_level > 1) {115printf("...setting SO_LINGER socket option, specified linger time is %d sec. Success.\n",116so_linger.l_linger);117}118#endif119
120
121/*--- Связка вновь созданного сокета и адреса ---*/122
123struct sockaddr_in serveraddr;124
125// Заполнение структуры нулями.126memset(&serveraddr, '\0', sizeof(serveraddr)); /* Иногда используют функцию bzero(),127* но она считается устаревшей.
128*/
129
130serveraddr.sin_family = AF_INET;131serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);132serveraddr.sin_port = htons(port);133
134if (bind(*sockfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) != 0) {135return SOCKETS_INIT_ERR_BIND;136} else if (verbosity_level > 0) {137printf("...socket successfully bound.\n");138}139
140
141/*--- Связка вновь созданного сокета и адреса ---*/142
143if (listen(*sockfd, numconn) != 0) {144return SOCKETS_INIT_ERR_LISTEN;145} else if (verbosity_level > 0) {146printf("...server is listening.\n");147}148
149return SOCKETS_INIT_OK;150}
151
152uint32_t sockets_proceed(int32_t sockfd, int32_t *connfd, uint32_t timeout_sec, uint32_t verbosity_level)153{
154/*--- Установка связи с клиентом ---*/155
156int32_t socklen = 0;157struct sockaddr_in clientaddr;158
159socklen = sizeof(clientaddr);160*connfd = accept(sockfd, (struct sockaddr*)&clientaddr, (socklen_t*)&socklen);161if (*connfd < 0) {162return SOCKETS_PROCEED_ERR_ACCEPT;163} else if (verbosity_level > 0) {164printf("\nServer accepted a client, waiting for incoming data. ");165timestamp_print();166printf(".\n");167fflush(stdout);168}169
170
171/*--- Проверка наличия данных в сокете ---*/172
173struct timeval tv;174tv.tv_sec = timeout_sec;175tv.tv_usec = 0;176
177fd_set readfds;178FD_ZERO(&readfds);179FD_SET(*connfd, &readfds);180
181int32_t result = select(*connfd + 1, &readfds, NULL, NULL, &tv);182int32_t retval = -1;183
184if (result < 0) {185retval = SOCKETS_PROCEED_ERR_SELECT;186} else if (result == 0) {187retval = SOCKETS_PROCEED_TIMEOUT;188if (verbosity_level > 0) {189printf("\nConnection closed due to timeout.\n");190}191} else if (result > 0) {192retval = SOCKETS_PROCEED_OK;193}194
195return retval;196}
197
198void sockets_read_message(int32_t connfd, char *buf, size_t buf_size, uint32_t verbosity_level)199{
200// Чтение сообщения от клиента и его запись в буфер.201read(connfd, buf, buf_size);202utilities_nullify_all_trailing_CR_and_LF_in_string(buf);203
204// Вывод содержимого буфера.205if (verbosity_level > 0) {206printf("\nMessage received from the client: %s\n", buf);207}208}
209
210void sockets_write_message(int32_t connfd, char *buf, uint32_t verbosity_level)211{
212utilities_nullify_all_trailing_CR_and_LF_in_string(buf);213
214strcat(buf, "\n");215write(connfd, buf, strlen(buf));216
217utilities_nullify_all_trailing_CR_and_LF_in_string(buf);218
219if (verbosity_level > 0) {220printf("\nMessage sent to the client: %s\n", buf);221}222}
223
224int32_t sockets_close(int32_t fd, uint32_t pause)225{
226//shutdown(fd, SHUT_RDWR); // Вроде не обязательно, хотя иногда рекомендуют.227usleep(pause);228int32_t retval = close(fd);229usleep(pause);230
231return retval;232}
233
234void sockets_report_broken_pipe(int32_t sig)235{
236if (sig == SIGPIPE) {237fprintf(stderr, "\n"238"Error: a client had disconnected before any response was sent by the server (broken pipe).\n");239}240}
241
242void sockets_sig_ign()243{
244signal(SIGPIPE, sockets_report_broken_pipe);245}
246