Я написал программу на C, которая должна действовать как примитивный сервер, и мне нужна помощь с проблемой распределения сокетов / ресурсов.
Функция main()
открывает один прослушивающий сокет, вызывает accept()
для каждого входящего соединения, затем раскручивает отдельный поток, чтобы обработать фактическую обработку клиентского запроса:
typedef struct {
int sock;
struct sockaddr address;
int addr_len;
} connection_t;
int main(int argc, char ** argv)
{
int sock = -1;
struct sockaddr_in address;
int port = 12345;
connection_t* connection;
pthread_t thread;
int cnt = 0;
signal(SIGINT, handle_sigint);
// Create the listening socket
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock <= 0){
fprintf(stderr, "%s: error: cannot create socket\n", argv[0]);
return -3;
}
// Bind socket to port
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(port);
if (bind(sock, (struct sockaddr *)&address, sizeof(struct sockaddr_in)) < 0){
fprintf(stderr, "%s: error: cannot bind socket to port %d\n", argv[0], port);
return -4;
}
// Listen on the port
if (listen(sock, 5) < 0){
fprintf(stderr, "%s: error: cannot listen on port\n", argv[0]);
return -5;
}
printf( "...starting loop...\n" );
while (1){
// Accept incoming connections
connection = (connection_t *)malloc(sizeof(connection_t));
connection->addr_len = 20;
connection->sock = accept(sock, &connection->address, &connection->addr_len);
if (connection->sock <= 0){
printf("Connection FAILED :: Value of errno: %d\n ", errno);
free(connection);
}
else{
// Start a new thread but do not wait for it
pthread_create(&thread, 0, process, (void *)connection);
pthread_detach(thread);
}
}
return 0;
}
Выше должно быть C Network Programming 101. И это прекрасно работает… пока я не начну отправлятьбольшое количество клиентских запросов. Когда я это вижу, я вижу это:
me@mylinux:/home/me/myServer
...starting loop...
Connection FAILED :: Value of errno: 24
Connection FAILED :: Value of errno: 24
Connection FAILED :: Value of errno: 24
Connection FAILED :: Value of errno: 24
Хмм. Поработав как первопроходец в группе более ранних подключений, accept()
внезапно задыхается.
Быстрый поиск в Google показывает, что Errno 24 равно EMFILE: «Слишком много открытых файлов». И поиск других сообщений в StackOverflow ( здесь ) предполагает, что каждый раз, когда мой код вызывает accept()
, это создает файловый дескриптор дляновое открытое соединение. Это означает, что каждый новый поток претендует на новый дескриптор файла.
Я предполагаю, что проблема в том, что потоки не очищаются после себя. Они читают из сокета, выполняют свою работу, затем завершают работу, не сообщая системе, что их FD больше не нужны. Таким образом, система поддерживает все эти FD, и я получаю лавину ошибок Errno 24 после первых 4096 подключений.
(я выполнил «ulimit -n 4096
» на моем компьютере с Linux).
Итак, вот функция process()
, которая направляет каждый поток;Я опустил часть разбора строки, потому что здесь она выходит за рамки. Обратите внимание, что после того, как поток выполняет свою работу, он пытается убрать после себя:
void * process(void * ptr)
{
char * buffer;
int len;
connection_t * conn; // See above for this data type
long addr = 0;
conn = (connection_t *)ptr;
// read length of message
read(conn->sock, &len, sizeof(int));
if (len > 0)
{
addr = (long)((struct sockaddr_in *)&conn->address)->sin_addr.s_addr;
buffer = (char *)malloc((len+1)*sizeof(char));
buffer[len] = 0;
// Read the message from the socket
read(conn->sock, buffer, len);
// Parse client message
parseCliMsg( buffer, len );
free(buffer);
}
// Close socket and clean up
close(conn->sock);
free(conn);
pthread_exit(0);
}
Кто-нибудь видит, где я иду не так?