Параллельный сервер на С
Наверняка ваша ОС поддерживает многопоточность, а потому вместо создания нового процесса - создавайте новый поток (pthread_create и далее по документации) на новое подключение. Это довольно расспростроненная практика, плюс более удобное взаимодействие. Потоки паралельно (или псевдопаралельно) выполняются внутри одного процесса, имеют одно адресное пространство, общий доступ к динаимическим объектам и т.п. Не забывайте только о синхронизации.
Про новый трэд я думал, но у меня такой ужастный легаси код и его архитектура, что мне кажется, чтобы сделать новый трэд, мне нужно переписать половину сервера. Ведь там нужно вынести в отдельную функцию часть кода, это, конечно, не проблемма, но по-моему слишком много аргументов придется либо передавать, либо глобальными делать. А для процесса функции не нужно, насколько я понял, нужно только часть кода завернуть в if? Вот у меня и стоит проблемма - какую часть кода завернуть...Бьюсь уже неделю, может наметанный глаз профессионала сможет подсказать?
listen(server_sockfd, 1);
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
FD_SET(0, &readfds); /* Add keyboard to file descriptor set */
char * testDir;
size = pathconf(".", _PC_PATH_MAX);
if ((testDir = (char *) malloc((size_t)size)) != NULL)//set server`s directory
testDir = (char *) getcwd(testDir, (size_t)size);
/* Now wait for clients and requests */
while (1) {
testfds = readfds;
select(FD_SETSIZE, &testfds, NULL, NULL, NULL);
/* If there is activity, find which descriptor it's on using FD_ISSET */
for (fd = 0; fd < FD_SETSIZE; fd++) {
if (FD_ISSET(fd, &testfds)) {
if (fd == server_sockfd) { /* Accept a new connection request */
client_sockfd = accept(server_sockfd, NULL, NULL);
if (num_clients < MAX_CLIENTS) {
FD_SET(client_sockfd, &readfds);
fd_array[num_clients] = client_sockfd;
clientInfo = malloc(sizeof(struct info));
clientInfo[num_clients]->clientFd = client_sockfd;
//strcpy(clientInfo[num_clients]->currentDir,testDir);
printf("Client %d joined\n In directory %s", num_clients++, clientInfo[num_clients]->currentDir);
fflush(stdout);
} else {
sprintf(msg, "XSorry, too many clients. Try again later.\n");
write(client_sockfd, msg, strlen(msg));
close(client_sockfd);
}
} else if (fd) { /*Process Client specific activity*/
В этой части идет чтение и обработка команд клиента, составление и отсылака запроса. Принятие и отсылака фаилов и т.д.
}
Спасибо за ответ!
Передать в новый thread данные можно не обязательно глобальные. Можно их например динамически выделять, структурировать, а передавать указатель на эту структуру.
Про селект чуть позже постараюсь рассказать. там просто все псевдопаралеьность вручную надо делать )
Есть именно такое решение. На 4м курсе делали многопоточный (не многопроцессный!) сервер-клиент: http://foxweb.net.ru/files/?id=225
Хотя для простых реализаций обычный select подходит намного лучше, но его надо правильно использовать - использовать неблокирующие соединения и тд и тп...
foxweb, спасибо, но там код нужен, я не нахожусь не в одной из этих стран:)
Yos, спасибо за совет! А где-то можно про это почитать?
Жаль только что в продаже вы ее не найдете...
Не был бы так категоричен. Тем более, что хотябы из-за fork(), автор програмирует не в сетях Windows. )
Вообщем предлагаю абстрагироваться от платформ и весь код давать на алгоритмическом языке Ершова, который плотненько так внедряли во всех школах на уроках информатики в конце 80-х годов :)))
А по жизни - сокеты Беркли везде работают одинаково, select идет именно от туда из UNIX'а... Лучше сразу признайтесь что Вас просто коробит от Microsoft...
А так это в принципе должно выглядеть. А то от "пенсионеров" пока дождешся... Хотя ща налетят и будут говорить что принципиально работать не будет :)))
// Основной поток
main()
{
// инициализация и создание серверного сокета для соединения
...
ServerSocket = ;
...
listen();
// обработка подключений
while( true )
{
...
// выбираем только подключения на серверном сокете
FD_ZERO(&Read);
// добавляем серверный сокет в набор
FD_SET(ServerSocket,&Read);
...
select();
// есть что обрабатывать
if()
{
// соединяемся
ClientSocket = accept();
// создаем поток для обработки сообщений от клиента
new Thread(&Reader,ClientSocket);
}
}
}
// клиентский поток
Reader(Socket client)
{
// работаем с данными клиента
...
closesocket(client);
}
А по жизни - сокеты Беркли везде работают одинаково
ага. только в винде не сокеты беркли. а своя реализация, ага?
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr,
&client_addr_len );
if( (pid = fork()) < 0 ) {
perror("fork");
exit(EXIT_FAILURE);
} else if(!pid) {
char * testDir;
size = pathconf(".", _PC_PATH_MAX);
if ((testDir = (char *) malloc((size_t)size)) != NULL)//set server`s directory
testDir = (char *) getcwd(testDir, (size_t)size);
mes = (char *) malloc(sizeof(char) * MSG_SIZE);
result = read(client_sockfd, mes, MSG_SIZE);
if (result == -1) {
perror("read");
exit(EXIT_FAILURE);
}
mes[result] = '\0';
if (mes[0] == 'X') {
exitClient(client_sockfd, &readfds, fd_array, &num_clients);
}
printf("Message from client %d: %s\n", client_sockfd, mes);
if (!strncmp(mes, "FBEGIN",6)) {
file = fopen ("SERVERblabla.txt", "w");
char fileBuf[1];
recv(client_sockfd, buf, 4, 0) ;
while(buf[0] != -1) {
putc(buf[0], file);
recv(client_sockfd, buf, 4, 0) ;
}
fclose(file);
} else {
error = parseCommand(mes, responce, testDir);
if (error != -1)
if (error == 10) {
printf("doing get. Arg: %s\n", argument);
file = fopen(argument, "r");
if (!file) {
sprintf(responce, "Error while getting file <%s>. Check file name.\n", argument);
write(client_sockfd, responce, strlen(responce));
} else {
long fileSize;
fseek(file, 0, SEEK_END);
fileSize = ftell(file);
rewind(file);
sprintf(responce, "FBEGIN:%s:%d\r\n", argument, fileSize);
send(client_sockfd, responce, sizeof(responce), 0);
while ((i = getc(file)) != EOF){
buf[0] = i;
send(client_sockfd, buf, sizeof(int) / sizeof(char), 0);
}
buf[0] = -1;
send(client_sockfd, buf, sizeof(int) / sizeof(char), 0);
fclose(file);
}
} else {
write(client_sockfd, responce, error);
}
}
сейчас сервер вообще обрабатывает одну команду :confused: Причем если один юзер сменил директорию (testDir) командой cd, то почему-то другие клиенты попадают в его директорию. Ведь для них должен быть новый процесс с новой директорией!!!!! :confused: В чем дело????
а я и не спорю, что реализация разная, а вот то что они касательно модели select полностью совместимы это факт. Так что Ваша борьба за чистоту "нации" мне не очень интересна...
Лучше человеку помогите, а то напрашивается вывод что Вам нечего сказать по существу из-за отсутствия оного. Хотя скорее дело в другом, мы же не чета вам, набивавшим перфокарты (или начнете лить воду о том что они тоже разные?) с Буденным...
Вобщем пустую полемику лично я прекращаю...
Отписал код в личку, и по ошибке Yos :)
простите, некогда сейчас вчитываться в ваш код. каркас должен выглядеть так (это при классической модели, без select, poll и т. п.):
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilent);
if (newsockfd < 0)
error("ERROR on accept");
pid = fork();
if (pid < 0)
error("ERROR on fork");
if (pid == 0) {
close(sockfd);
somefunction(newsockfd);
exit(0);
}
else
close(newsockfd);
}
т. е. - родитель, после fork - должен прибивать сокет и возвращаться к accept. потомок - вызывает некоторую функцию-обработчик, которая принимает параметром сокет и уже она начинает с ним работать и общаться с клиентами. у вас же - подозрительно много букв после ветвления в родителе. сократите код до минимума - чтобы работало правильно, а потом начинайте накручивать функционал. дома - посмотрю, если чадо кричать не будет над ухом :)
[QUOTE=Yos]а я и не спорю, что реализация разная, а вот то что они касательно модели select полностью совместимы это факт. Так что Ваша борьба за чистоту "нации" мне не очень интересна...[/QUOTE]
если бы вы внимательно читали книги, которые рекомендуете, вместо того, чтобы пытаться мне доказать, что я дурак - знали бы, что использование POSIX select в Windows - сродни забиванию гвоздей отверткой. в Windows используются Async расширения WSA.
[QUOTE=Yos]отя скорее дело в другом, мы же не чета вам, набивавшим перфокарты (или начнете лить воду о том что они тоже разные?) с Буденным...[/QUOTE]
хамство я расцениваю как знак того, что сказать по существу нечего именно вам.
sprintf(msg, "XClient is shutting down.\n");
write(sockfd, msg, strlen(msg));
close(sockfd); //close the current client
exit(0); //end program
}
а сервер, судя по всему продолжает считывать данные и приходит ошибка read: bad file descriptor..
...ну это я еще посмотрю. Просто на радостях решил написать...я с эти сервером уже месяц бьюсь!! :)
foxweb, и вам спасибо за доброту сердешую!:)
http://www.piter.com/book.phtml?978594723991
http://www.piter.com/book.phtml?978531800453
честное слово, не пожалеете :)
А почему родитель и ребенок убиваю сокеты? Причем разные, что-то я не пойму?
[QUOTE=ljevik]А почему родитель и ребенок убиваю сокеты? Причем разные, что-то я не пойму?[/QUOTE]
не совсем понял вопрос. поясните
Процесс потомок, как известно, порождаясь, приобретает всю структуру процесса родителя, кроме идентификатора процесса, получается как бы два одинаковых процесса (даже количество выделенной системной памяти под процессы одинаковое). Если существует идентификатор сокета (pid) в родителе, созданного до появления потомка, то его же и наследует потомок. Для этого и закрывают сокет в потомке, но думаю в родителе это лишнее.
Если используешь потоки (threads), то закрывать да открывать сокеты постоянно нет нужды. Данные процесса, породившего поток, прозрачны для потока во всем пространстве памяти процесса его родителя (как бы являются глобалными для потока). Так же потоки могут иметь свои личные данные (как бы локальные) маскируемые ключами.
Вобщем, сначало стоило бы почитать про потоки :)