[Comm] Q: Programming sockets in C language troubles

Andrey Brindeew =?iso-8859-1?q?abr_=CE=C1_altlinux=2Eru?=
Пн Май 19 15:38:00 MSD 2003


Hi!

На работе встала задача перегонять данные с одной машины (расчетная,
сервер) на другую (отображение) по сети. В качестве языка используется
Си, ОС Linux, поэтому было решено использовать сокеты для передачи, дабы
ничего не изобретать ("всё уже украдено до нас" (C) :-) )

Берем тривиальный пример из документации по glibc (pinfo select, самая
последняя ссылка на странице: Server Example). Чуть-чуть дорабатываем
(избавляемся от функции make_socket путем включения ее в программу) и
пытаемся тестить:

$ gcc -o srv{,.c}
$ ./srv

На другой консоли:
$ telnet 127.0.0.1 1200
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
protocol_command 12345

Переключаемся на серверную консоль: _тишина полная_.
Нажимаем Enter три (или больше) раз - видим следующее:

$ ./srv

got message from 0 client: `
╛@пВЪ©PВЪ©└'

got message from 1 client: `
╛@пВЪ©PВЪ©└'

got message from 2 client: `
╛@пВЪ©PВЪ©└'
connect from host 127.0.0.1, port 33683.
got message from 4 client: `protocol_com─ЖЪ©L╛@L╛@ХВЪ©┼'
read: Bad file descriptor

Теперь вопросы:
 1. Почему у нас на серверной консоли фигурируют 0, 1 и 2 сокеты (я
    так понимаю, что это stdin, stdout и stderr сервера собственной
	персоной)? Я их в FD_SET не заказывал на прослушивание с помощью
	select. :-(
 2. Что за мусор идет после подстроки "protocol_com"? Я так понимаю, что
    буфер чтения у сокета заполнился (ибо 12 символов всего), но откуда
	мусор?
 3. Почему сообщение о подсоединении клиента не появилось сразу же после
    того, как я подцепился к серверу с помощью telnet?
 4. Я наверное еще много чего не понимаю, какая хорошая литература есть
    в сети и код какой _хорошо написанной_ программы можно посмотреть
	для примера? В исходниках sshd и popa3d запутался... :-(

Для желающих помочь начинающему Си-программисту советом код "сервера"
прицеплен к письму.

P.S. Первоначальная идея состояла в том, чтобы написать расчетный
     сервер, который работает по следующему протоколу: получает номер
	 алгоритма и (опционально) параметры для расчета по этому алгоритму,
	 а потом начинает тупо писать в открытый сокет рассчитываемые
	 данные, пока этот сокет удаленная сторона (визуализирующий клиент)
	 не закроет. Клиент реально будет один, но закладываться на
	 одноклиентную архитектуру при написании не хотелось бы.

-- 
WBR, Andrey Brindeew.
"No one person can understand Perl culture completely"
(C) Larry Wall.
----------- следующая часть -----------
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 1200
#define MAXMSGLENGTH 12

int
main (void)
{
		int sock; /* server socket, which accepts new connections */
		fd_set active_fd_set, read_fd_set; /* sets of serving socket descriptors */
		int i;
		struct sockaddr_in clientname, server;
		size_t size;

		/* Create the socket and set it up to accept connections */
		sock = socket(PF_INET, SOCK_STREAM, 0);
		if (sock < 0) {
				perror("socket");
				exit(EXIT_FAILURE);
		}

		/* Give the socket a name */
		server.sin_family = AF_INET;
		server.sin_port = htons(PORT);
		server.sin_addr.s_addr = htonl(INADDR_ANY);

		if (bind(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
				perror("bind");
				exit(EXIT_FAILURE);
		}

		if (listen(sock, 10) < 0) {
				perror("listen");
				exit(EXIT_FAILURE);
		}

		/* Initialize the set of active sockets */
		FD_ZERO (&active_fd_set);
		FD_SET (sock, &active_fd_set);

		/* going into server main loop */
		while (1) {
				/* Block until unput arrives on one or more active sockets */
				read_fd_set = active_fd_set;
				if (select (FD_SETSIZE, &read_fd_set, NULL, NULL, NULL) < 0) {
						perror ("select");
						exit (EXIT_FAILURE);
				}

				/* Service all sockets with input pending */
				for (i = 0; i < FD_SETSIZE; ++i) {
						if (i == sock) {
								/* Connection on server socket - new connection request */
								int new;
								size = sizeof (clientname);
								new = accept(sock, (struct sockaddr *) &clientname, &size);
								if (new < 0) {
										perror ("accept");
										exit (EXIT_FAILURE);
								}

								fprintf (stderr, "connect from host %s, port %hd.\n",
									inet_ntoa (clientname.sin_addr),
									ntohs(clientname.sin_port));
								FD_SET (new, &active_fd_set);
						}
						else {
								if (read_from_client (i) < 0) {
										close (i);
										FD_CLR (i, &active_fd_set);
								}
						}
				}
		}
}

int
read_from_client (int fd)
{
		char buffer[MAXMSGLENGTH];
		int nbytes;

		nbytes = read (fd, buffer, MAXMSGLENGTH);
		if (nbytes < 0) {
				perror("read");
				exit(EXIT_FAILURE);
		}
		else if (nbytes == 0) {
				return -1;
		}
		else {
				fprintf (stderr, "got message from %d client: `%s'\n", fd, buffer);
				return 0;
		}
}
----------- следующая часть -----------
Было удалено вложение не в текстовом формате...
Имя     : =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Тип     : application/pgp-signature
Размер  : 245 байтов
Описание: =?iso-8859-1?q?=CF=D4=D3=D5=D4=D3=D4=D7=D5=C5=D4?=
Url     : <http://lists.altlinux.org/pipermail/community/attachments/20030519/38cb3288/attachment-0004.bin>


Подробная информация о списке рассылки community