Клиент серверные системы linux

Клиент-сервер под linux на c++ общение клиентов «все со всеми» с использованием потоков

Начну с того, что была предложена работа на должность программиста с\с++. Задание это название темы.

Полез в интернет, кругом все напичкано чатами и общением по типу клиент-сервер, но увы кода с подобным заданием я так и не нашел. Был примитив типа ЭХО клиент-сервера, который я и решил взять за основу:
Это у нас клиент:

 struct sockaddr_in addr; // структура с адресом struct hostent* hostinfo; port = atoi(PORT); sock = socket(AF_INET, SOCK_STREAM, 0); // создание TCP-сокета if(sock < 0) < perror("socket"); exit(1); >// Указываем параметры сервера addr.sin_family = AF_INET; // домены Internet addr.sin_port = htons(port); // или любой другой порт. addr.sin_addr.s_addr = inet_addr("127.0.0.1"); if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) // установка соединения с сервером
if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0) < perror("socket failed"); exit(EXIT_FAILURE); >//set master socket to allow multiple connections , this is just a good habit, it will work without this if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ) < perror("setsockopt"); exit(EXIT_FAILURE); >//type of socket created address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons( PORT ); //bind the socket to localhost port 8888 if (bind(master_socket, (struct sockaddr *)&address, sizeof(address)) <0) < perror("bind failed"); exit(EXIT_FAILURE); >printf("Listener on port %d \n", PORT); //try to specify maximum of 3 pending connections for the master socket if (listen(master_socket, 3) < 0) < perror("listen"); exit(EXIT_FAILURE); >//accept the incoming connection addrlen = sizeof(address); puts("Waiting for connections . "); while(TRUE) < if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) < perror("accept"); exit(EXIT_FAILURE); >>

После всего этого в клиенте нужно отправить сообщение серверу используя функции send или write а на стороне сервера принять сообщение и переотправить его обратно клиенту используя функции read и send.

Вообще есть разные функции отправки и приема, к примеру send и recv вместе с сообщением шлют еще и флаг подтверждения, а функции read и write не требуют подтверждения, то есть сообщение может потерять байты при отправке и это не будет зафиксировано.

Так как сокеты это дуплекс и создавая связь между клиентом и сервером мы не можем писать туда сообщения из других подключенных сокетов, необходимо создать массив со всеми активными сокетами подключенными к серверу. И еще одно замечание очень важное:

Для общения между несколькими сокетами необходимо использовать функцию select, которая выбирает сокет из списка и отсылает ему сообщение, и так далее, пока не закончатся все записанные сокеты

//clear the socket set FD_ZERO(&readfds); //add master socket to set FD_SET(master_socket, &readfds); max_sd = master_socket; //add child sockets to set for ( i = 0 ; i < max_clients ; i++) < //socket descriptor sd = client_socket[i]; //if valid socket descriptor then add to read list if(sd >0) FD_SET( sd , &readfds); //highest file descriptor number, need it for the select function if(sd > max_sd) max_sd = sd; > //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL); if ((activity

После этого в массив сокетов будет записано правильное значение подключаемого сокета а далее остается лишь перебирать их при рассылке сообщений:

 sd = client_socket[i]; if (FD_ISSET( sd , &readfds)) < //Check if it was for closing , and also read the incoming message if ((valread = read( sd , buffer, 1024)) == 0) < //Somebody disconnected , get his details and print getpeername(sd , (struct sockaddr*)&address , (socklen_t*)&addrlen); printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //Close the socket and mark as 0 in list for reuse close( sd ); user_count--; client_socket[i] = 0; >//Echo back the message that came in else < //set the string terminating NULL byte on the end of the data read buffer[valread] = '\0'; for (i = 0; i < max_clients; i++) < sd = client_socket[i]; send(sd , buffer , strlen(buffer) , 0 ); >buffer[1024] = ; > >

Запишем все это в функцию и создадим отдельный поток:

void *server(void *); pthread_create(&threadA[0], NULL, server, NULL); pthread_join(threadA[0], NULL);

Что касаемо клиента, то необходимо создать два разных потока для чтения и записи в сокет:

void *write(void *); void *read(void *); pthread_create(&threadA[0], NULL, write, NULL); pthread_create(&threadA[1], NULL, read, NULL); pthread_join(threadA[1], NULL); pthread_join(threadA[0], NULL); void *write (void *dummyPt) < for(;;) < char s[BUF_SIZE]; cout close(sock); > void *read (void *dummyPt) < char test[BUF_SIZE]; bzero(test, BUF_SIZE + 1); bool loop = false; while(!loop) < bzero(test, BUF_SIZE + 1); int rc = read(sock, test, BUF_SIZE); if ( rc >0) < string tester (test); cout > cout

Теперь все работает. Спасибо за снимание. Надеюсь что это пригодится тем, кто так же как и я пытался написать клиент-сервер, но не смог найти нужную информацию в сети.

Читайте также:  Linux создать исполняемый файл скрипт

Источник

Клиент-Сервер Socket linux (Система обмена сообщениями)

Здравствуйте! Помогите,пожалуйста,доработать Клиент-Сервер.Никак не получается. Завтра последний день сдачи Условия выполнения: «Задание: разработать приложение-клиент и приложение сервер, обеспечивающие функции мгновенного обмена сообщений между пользователями.

Основные возможности. Серверное приложение должно реализовывать следующие функции: 1) Прослушивание определенного порта 2) Обработка запросов на подключение по этому порту от клиентов 3) Поддержка одновременной работы нескольких клиентов через механизм нитей 4) Передача текстового сообщения одному клиенту 5) Передача текстового сообщения всем клиентам 6) Прием и ретрансляция входящих сообщений от клиентов 7) Обработка запроса на отключение клиента 8) Принудительное отключение указанного клиента

Клиентское приложение должно реализовывать следующие функции: 1) Установление соединения с сервером 2) Передача сообщения всем клиентам 3) Передача сообщения указанному клиенту 4) Прием сообщения от сервера с последующей индикацией 5) Разрыв соединения 6) Обработка ситуации отключения клиента сервером

Настройки приложений. Разработанное клиентское приложение должно предоставлять пользователю настройку IP-адреса или доменного имени сервера сообщений и номера порта сервера. »

Пока в работе у меня выполнено следующие условия:Сервер:1,2,3 ;Клиент:1,. В данном случае у меня клиент отправляет сообщение серверу,а сервер отправляет этоже сообщение обратно клиенту.

#include #include #include #include #include #include int main() < int sock, listener; // дескрипторы сокетов struct sockaddr_in addr; // структура с адресом char buf[2048]; // буфур для приема int bytes_read; // кол-во принятых байт listener = socket(AF_INET, SOCK_STREAM, 0); // создаем сокет для входных подключений if(listener < 0) < perror("socket"); exit(1); >// Указываем параметры сервера addr.sin_family = AF_INET; addr.sin_port = htons(3425); //addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_addr.s_addr=inet_addr("127.0.0.1"); if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) // связываемся с сетевым устройство. Сейчас это устройство lo - "петля", которое используется для отладки сетевых приложений < perror("bind"); exit(2); >listen(listener, 1); // очередь входных подключений while(1) < sock = accept(listener, NULL, NULL); // принимаем входные подключение и создаем отделный сокет для каждого нового подключившегося клиента if(sock < 0) < perror("Прием входящих подключений"); exit(3); >switch(fork()) < case -1: perror("fork"); break; case 0: close(listener); while(1) < printf("Ожидаем сообщение. \n"); bytes_read = recv(sock, buf, 2048, 0); // принимаем сообщение от клиента if(bytes_read close(sock); // закрываем сокет _exit(0); default: close(sock); > > return 0; >
#include #include #include #include #include int main (int argc, char **argv) < char message[2048];//=(char*)malloc(sizeof(char));// сообщение на передачу char buf[sizeof(message)]; int port,ch; //---------------------------------------------------------------------------- if(argc!=3)< printf("Не верное кол-во аргументов!\nДолжно быть 2 аргумента (Порт,ip-адрес -сервера)!\n"); exit(0); >int sock; // дескриптор сокета struct sockaddr_in addr; // структура с адресом struct hostent* hostinfo; port = atoi(argv[1]); hostinfo = argv[2]; sock = socket(AF_INET, SOCK_STREAM, 0); // создание TCP-сокета if(sock < 0) < perror("socket"); exit(1); >// Указываем параметры сервера addr.sin_family = AF_INET; // домены Internet addr.sin_port = htons(port); // или любой другой порт. addr.sin_addr.s_addr=inet_addr(hostinfo); /* * Начинающие программисты часто спрашивают, как можно отлаживать сетевую программу, если под рукой нет сети. * Оказывается, можно обойтись и без неё. Достаточно запустить клиента и сервера на одной машине, а затем использовать для соединения адрес интерфейса внутренней петли (loopback interface). * В программе ему соответствует константа INADDR_LOOPBACK (не забудьте применять к ней функцию htonl!). * Пакеты, направляемые по этому адресу, в сеть не попадают. * Вместо этого они передаются стеку протоколов TCP/IP как только что принятые. * Таким образом моделируется наличие виртуальной сети, в которой вы можете отлаживать ваши сетевые приложения. */ // addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //addr.sin_addr.s_addr = inet_addr(host_ip); if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) // установка соединения с сервером < perror("Подключение"); exit(2); >while(1)//END_WHILE return 0; >

Деревянные треды лора: помогите решить ДЗ

3) Поддержка одновременной работы нескольких клиентов через механизм нитей

Это делается через epoll, тащемта. Вот как-то так, наверно

Читайте также:  Настроить синхронизацию времени linux

Вот ещё пример, как работать со многими клиентами:

man select && man poll && man kqueue (через последнюю функцию сделан event-base в iolib)

fork() и указанные в ТЗ нити - мягко говоря разные вещи

и перед тем как начать списывать код из источников стоит набросать на бумажке протокол: какие и как данные ходят туда-сюда, форматы сообщений, состояния соединений.

fork() и указанные в ТЗ нити - мягко говоря разные вещи

А разве и то и другое не через clone() выполняется?

Еще вопросик:

Как сделать так,чтобы клиент мог скачать с сервера песню?Например формата .mp3

А у тебя по этому поводу свои мысли есть какие-нибудь? Мне просто интересно, есть ли у тебя мозг впринципе.

есть куча протоколов прикладного уровня. самое простое читать в стрим файл и send()/recv()

Возможно как то так: На сервере как то так: FILE * file; file = fopen(«test.mp3»,«wt»); после делаю send. Далее на клиента пишу recv . а вот дальше не знаю,куда записать эту песню и т.д.

Re: Еще вопросик:

Лучше воспользоваться более высокоуровневым протоколом. Хоть http.

Далее на клиента пишу recv . а вот дальше не знаю,куда записать эту песню и т.д.

Также открываешь файл на запись и пишешь.

К сожалению по заданию нельзя

не могли бы написать пример в коде?

Нет, не хочу тратить время. Читай man'ы.

Ты делаешь recv из сокета в буфер, а потом write (man 2 write) из буфера в файл

//====================================================== #define BUF_SIZE 4096 FILE* f = fopen("filename_in.mp3","rb"); fseek (f , 0 , SEEK_END); fSize = ftell (f); rewind (f); long sended = 0; long readed = 0; char buffer[BUF_SIZE]; < readed = fread (buffer,1,BUF_SIZE,f); send(sock, buffer, readed, 0); sended += readed; >while(sended while(rcv_len != 0); close(sock); fclose(f); 

писал прям тут, думаю суть понятна

есть опечатки сразу сам вижу.. но чет редактирование поста не доступно

Пару опечаток исправил,но не компилируется. gcc пишет:expected declaration of statement at and of intut.

Код: FILE* f = fopen(«music.mp3»,«rb»);

fseek (f , 0 , SEEK_END); int fsize = ftell (f); rewind (f);

long sended = 0; long readed = 0;

Пару опечаток исправил,но не компилируется. gcc пишет:expected declaration of statement at and of intut.

FILE* f = fopen("music.mp3","rb"); fseek (f , 0 , SEEK_END); int fsize = ftell (f); rewind (f); long sended = 0; long readed = 0; char buffer[BUF_SIZE]; do< readed = fread (buffer,1,BUF_SIZE,f); send(sock, buffer, readed, 0); sended += readed; >while(sended  

jo_b1ack,Спасибо тебе большое. Теперь дошло как это работает.

Осталось разбираться с системой обмена сообщений.

Мог ли бы еще помочь в одной проблемке?

У меня все передаётся,но после сервер и клиент просто висят.

Если я убираю while то передается только 4кб и идет дальше по коду т.е. не висит .

попробуй заменить в приеме строчку

возможно после закрытия сокета recv возвращает не 0 - закрытие соединения, а SOCKET_ERROR

Пробовал,все также. В коде пробовал задать время на while ,но не очень успешно,закоментил .

#include #include #include #include #include #include #define BUF_SIZE 4096 int main (int argc, char **argv) < char message[2048];//=(char*)malloc(sizeof(char));// сообщение на передачу char buf[sizeof(message)]; int port,ch; //---------------------------------------------------------------------------- if(argc!=3)< printf("Не верное кол-во аргументов!\nДолжно быть 2 аргумента (Порт,ip-адрес -сервера)!\n"); exit(0); >int sock; // дескриптор сокета struct sockaddr_in addr; // структура с адресом struct hostent* hostinfo; port = atoi(argv[1]); hostinfo = argv[2]; sock = socket(AF_INET, SOCK_STREAM, 0); // создание TCP-сокета if(sock < 0) < perror("socket"); exit(1); >// Указываем параметры сервера addr.sin_family = AF_INET; // домены Internet addr.sin_port = htons(port); // или любой другой порт. addr.sin_addr.s_addr=inet_addr(hostinfo); /* * Начинающие программисты часто спрашивают, как можно отлаживать сетевую программу, если под рукой нет сети. * Оказывается, можно обойтись и без неё. Достаточно запустить клиента и сервера на одной машине, а затем использовать для соединения адрес интерфейса внутренней петли (loopback interface). * В программе ему соответствует константа INADDR_LOOPBACK (не забудьте применять к ней функцию htonl!). * Пакеты, направляемые по этому адресу, в сеть не попадают. * Вместо этого они передаются стеку протоколов TCP/IP как только что принятые. * Таким образом моделируется наличие виртуальной сети, в которой вы можете отлаживать ваши сетевые приложения. */ // addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //addr.sin_addr.s_addr = inet_addr(host_ip); if(connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) // установка соединения с сервером < perror("Подключение"); exit(2); >while(1) < int counter=0; char buftext[1024]; while(counter!=1)< int bytes_read=recv(sock,buftext,1024,0); printf("Принятое сообщение от сервера: %s/n",buftext); printf("Введите номер композиции(Для выхода:exit): "); if (!strcmp(gets(message), "exit"))send(sock, message, sizeof(message), 0); // отправка сообщения на сервер counter=1; > if(counter==1)< FILE* f = fopen("music.mp3","wb"); char buffer[BUF_SIZE]; long rcv_len = 0; time_t seconds; time_t seconds1; seconds=time(NULL); do< rcv_len = recv(sock, buffer, BUF_SIZE, 0); fwrite (buffer,1,rcv_len,f); //seconds1=time(NULL); //seconds1=seconds1-seconds; //if(seconds1 >10) >while(rcv_len > 0); //close(sock); fclose(f); > >close(sock); /* while(1)//END_WHILE */ return 0; >
#include #include #include #include #include #include #define BUF_SIZE 4096 int main() < int sock, listener; // дескрипторы сокетов struct sockaddr_in addr; // структура с адресом char buf[2048]; // буфур для приема int bytes_read; // кол-во принятых байт listener = socket(AF_INET, SOCK_STREAM, 0); // создаем сокет для входных подключений if(listener < 0) < perror("socket"); exit(1); >// Указываем параметры сервера addr.sin_family = AF_INET; addr.sin_port = htons(3425); //addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_addr.s_addr=inet_addr("127.0.0.1"); if(bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) // связываемся с сетевым устройство. Сейчас это устройство lo - "петля", которое используется для отладки сетевых приложений < perror("bind"); exit(2); >listen(listener, 1); // очередь входных подключений while(1) < sock = accept(listener, NULL, NULL); // принимаем входные подключение и создаем отделный сокет для каждого нового подключившегося клиента if(sock < 0) < perror("Прием входящих подключений"); exit(3); >while(1) < int counter=0; while(counter!=1)< char text[]="Привет,Я музыкальный сервис!Вот мои композиции:"; char buftext[sizeof(text)]; send(sock,text,sizeof(text),0); bytes_read = recv(sock, buf, 2048, 0); // принимаем сообщение от клиента if(bytes_read FILE* f; int x=atoi(buf); if(counter==1) < switch(x)< case 1: f = fopen("1.mp3","rb"); break; case 2: f = fopen("2.mp3","rb"); break; case 3: f = fopen("3.mp3","rb"); break; case 4: f = fopen("4.mp3","rb"); break; case 5: f = fopen("5.mp3","rb"); break; case 6: f = fopen("6.mp3","rb"); break; >fseek (f , 0 , SEEK_END); int fsize = ftell (f); rewind (f); long sended = 0; long readed = 0; char buffer[BUF_SIZE]; time_t seconds; time_t seconds1; seconds=time(NULL); do< readed = fread (buffer,1,BUF_SIZE,f); send(sock, buffer, readed, 0); sended += readed; //seconds1=time(NULL); //seconds1=seconds1-seconds; //if(seconds1 >10) >while(sended > /*int filefd=0,servfd=0; size_t nbytes=0; size_t filesize=0; char recvbuf[BUFSIZE]; if((filefd=open("file.mp3",rb))==-1) filesize = (size_t)lseek(filefd,0,SEEK_END); lseek(filefd,0,0); if(sendfile(sock,filefd,NULL,filesize) != filesize) < perror("fail to send file");close(filefd);close(sock); >while(recv(servfd,recvbuf,BUFSIZE,0)>0)*/ /* switch(fork()) < case -1: perror("fork"); break; case 0: close(listener); while(1) < printf("Ожидаем сообщение. \n"); bytes_read = recv(sock, buf, 2048, 0); // принимаем сообщение от клиента if(bytes_read close(sock); // закрываем сокет _exit(0); default: close(sock); > */ > close(sock); return 0; >

Источник

Оцените статью
Adblock
detector