В чем разница между параллельной клиент-серверной программой TCP и итеративной? - PullRequest
0 голосов
/ 23 декабря 2018

Я хочу научиться писать параллельный клиентский сервер TCP на C, но я не могу понять разницу между итеративной программой сервер-клиент и параллельной.В интернете я не смог найти много информации.Я просматриваю реализацию клиентского сервера TCP на C с сайта www.geeksforgeeks.org, но думаю, что это итерационный пример.Как я могу сделать это одновременно?

TCP-сервер:

#include <netdb.h> 
#include <netinet/in.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/socket.h> 
#include <sys/types.h> 
#define MAX 80 
#define PORT 8080 
#define SA struct sockaddr 

// Function designed for chat between client and server. 
void func(int sockfd) 
{ 
char buff[MAX]; 
int n; 
// infinite loop for chat 
for (;;) { 
    bzero(buff, MAX); 

    // read the message from client and copy it in buffer 
    read(sockfd, buff, sizeof(buff)); 
    // print buffer which contains the client contents 
    printf("From client: %s\t To client : ", buff); 
    bzero(buff, MAX); 
    n = 0; 
    // copy server message in the buffer 
    while ((buff[n++] = getchar()) != '\n') 
        ; 

    // and send that buffer to client 
    write(sockfd, buff, sizeof(buff)); 

    // if msg contains "Exit" then server exit and chat ended. 
    if (strncmp("exit", buff, 4) == 0) { 
        printf("Server Exit...\n"); 
        break; 
       } 
    } 
 } 

// Driver function 
int main() 
{ 
int sockfd, connfd, len; 
struct sockaddr_in servaddr, cli; 

// socket create and verification 
sockfd = socket(AF_INET, SOCK_STREAM, 0); 
if (sockfd == -1) { 
    printf("socket creation failed...\n"); 
    exit(0); 
} 
else
    printf("Socket successfully created..\n"); 
bzero(&servaddr, sizeof(servaddr)); 

// assign IP, PORT 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); 
servaddr.sin_port = htons(PORT); 

// Binding newly created socket to given IP and verification 
if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) { 
    printf("socket bind failed...\n"); 
    exit(0); 
} 
else
    printf("Socket successfully binded..\n"); 

// Now server is ready to listen and verification 
if ((listen(sockfd, 5)) != 0) { 
    printf("Listen failed...\n"); 
    exit(0); 
} 
else
    printf("Server listening..\n"); 
len = sizeof(cli); 

// Accept the data packet from client and verification 
connfd = accept(sockfd, (SA*)&cli, &len); 
if (connfd < 0) { 
    printf("server acccept failed...\n"); 
    exit(0); 
} 
else
    printf("server acccept the client...\n"); 

// Function for chatting between client and server 
func(connfd); 

// After chatting close the socket 
close(sockfd); 
} 

TCP-клиент:

#include <netdb.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/socket.h> 
#define MAX 80 
#define PORT 8080 
#define SA struct sockaddr 
void func(int sockfd) 
{ 
char buff[MAX]; 
int n; 
for (;;) { 
    bzero(buff, sizeof(buff)); 
    printf("Enter the string : "); 
    n = 0; 
    while ((buff[n++] = getchar()) != '\n') 
        ; 
    write(sockfd, buff, sizeof(buff)); 
    bzero(buff, sizeof(buff)); 
    read(sockfd, buff, sizeof(buff)); 
    printf("From Server : %s", buff); 
    if ((strncmp(buff, "exit", 4)) == 0) { 
        printf("Client Exit...\n"); 
        break; 
      } 
    } 
  } 

int main() 
{ 
int sockfd, connfd; 
struct sockaddr_in servaddr, cli; 

// socket create and varification 
sockfd = socket(AF_INET, SOCK_STREAM, 0); 
if (sockfd == -1) { 
    printf("socket creation failed...\n"); 
    exit(0); 
} 
else
    printf("Socket successfully created..\n"); 
bzero(&servaddr, sizeof(servaddr)); 

// assign IP, PORT 
servaddr.sin_family = AF_INET; 
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
servaddr.sin_port = htons(PORT); 

// connect the client socket to server socket 
if (connect(sockfd, (SA*)&servaddr, sizeof(servaddr)) != 0) { 
    printf("connection with the server failed...\n"); 
    exit(0); 
} 
else
    printf("connected to the server..\n"); 

// function for chat 
func(sockfd); 

// close the socket 
close(sockfd); 
} 

1 Ответ

0 голосов
/ 23 декабря 2018

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

  • Как предлагается в комментариях, поместите вызов accept в цикл.Он будет блокироваться, пока кто-то не подключится.Когда кто-то подключается, вы создаете поток и предоставляете ему всю информацию, необходимую для взаимодействия с клиентом, для простого примера должно хватить файлового дескриптора (sockfd).После этого поток может выполнить ваш func, поскольку этот поток будет знать только об одном клиенте, но основной цикл снова заблокирует accept, ожидая другого соединения. Я нашел этот пример в Google .
  • Используя системный вызов POSIX выберите (или его альтернативы poll и epoll), вы можете отслеживать список сокетовдля деятельности.Если какое-либо из отслеживаемых сокетов имеет активность, то read или accept к нему не блокируется, и затем вы обрабатываете эти сокеты.Если дескриптор файла сервера имеет активность, это означает, что подключен новый клиент, вы должны хранить где-нибудь дескриптор файла клиента и начать передавать его для выбора.Если дескриптор файла клиента имеет активность, вы обрабатываете его сообщения, как и раньше.Этот способ не требует потока, все обрабатывается в основной программе и может иметь преимущества в зависимости от вашего варианта использования. Я нашел следующие два примера в Google: здесь и здесь
  • Этот способ не рекомендуется: отключите блокировку сокета сервера с помощью fcntl.Таким образом, accept не будет блокироваться, вместо этого он немедленно вернет сигнал об ошибке EAGAIN или EWOULDBLOCK.Затем вы можете повторно вызывать accept в вашем главном цикле, в большинстве случаев он выдаст ошибку, но те, которые не будут сигнализировать о подключении нового клиента.Когда клиент подключается, вы делаете его файловый дескриптор неблокирующим и сохраняете его где-нибудь.В вашей основной программе для каждого клиентского сокета вы будете использовать read для них, но если новая информация недоступна, она также выдаст ошибку.Если что-то доступно, вы относитесь к этому, как раньше.Недостатком этого является то, что всегда требуется 100% загрузка процессора.Вы можете вставить задержку в цикл, но это увеличит дополнительную ненужную задержку.Вы не должны этого делать.
...