Как связать клиентов по два при подключении - PullRequest
0 голосов
/ 03 января 2019

У меня проблема с программированием сокетов на C. Я кодировал сервер, на котором несколько клиентов могут подключаться и отправлять сообщения в чате, но я не знаю, как соединить двух клиентов два на два.

Пример: Клиент A подключается к Серверу, но должен ждать в очереди, ожидая подключения Клиента B. Клиент C подключается, но должен ждать в очереди, ожидая подключения клиента D.

Каждая пара клиентов должна находиться в своем особом чате, нужно ли мне разделить fd между двумя сокетами 2 клиентов или ссылку между двумя сокетами.

Я думал об использовании другого способа, используя функцию accept() дважды, прежде чем fork(), но для этого мне нужно использовать дочерние процессы.

Вот мой код сервера:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#define PORT "8888"   


void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
    fd_set master;    
    fd_set read_fds;  
    int fdmax;        

    int listener;     
    int newfd;        
    struct sockaddr_storage remoteaddr; 
    socklen_t addrlen;

    char buf[256];    
    int nbytes;

    char remoteIP[INET6_ADDRSTRLEN];

    int yes=1;        
    int i, j, rv;

    struct addrinfo hints, *ai, *p;

    FD_ZERO(&master);    
    FD_ZERO(&read_fds);  


    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;
    if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
        fprintf(stderr, "Server msg: %s\n", gai_strerror(rv));
        exit(1);
    }

    for(p = ai; p != NULL; p = p->ai_next)
    {
        listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (listener < 0) { 
            continue;
        }

        setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));

        if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
            close(listener);
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "Server msg: bind failed\n");
        exit(2);
    }

    freeaddrinfo(ai); 

    puts("Bind success");

    if (listen(listener, 10) == -1)
    {
        perror("listen");
        exit(3);
    }
    puts("Server listening ...");


    FD_SET(listener, &master);


    fdmax = listener;


    for(;;)
    {
        read_fds = master;
        if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)
        {
            perror("select");
            exit(4);
        }


        for(i = 0; i <= fdmax; i++) 
        {
            if (FD_ISSET(i, &read_fds))
            {   
                if (i == listener)
                {

                    addrlen = sizeof remoteaddr;
                    newfd = accept(listener,
                        (struct sockaddr *)&remoteaddr,
                        &addrlen);

                    if (newfd == -1)
                    {
                        perror("accept");
                    }
                    else
                    {
                        FD_SET(newfd, &master); 
                        if (newfd > fdmax)
                        {   
                            fdmax = newfd;
                        }
                        printf("Server msg: new connection from %s on "
                               "socket %d\n", inet_ntop(remoteaddr.ss_family,
                               get_in_addr((struct sockaddr*)&remoteaddr),
                               remoteIP, INET6_ADDRSTRLEN), newfd);
                    }
                } 
                else
                {

                    if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0)
                    {  
                        if (nbytes == 0)
                        {   
                            printf("Server msg: socket %d lost\n", i);
                        }
                        else
                        {
                            perror("recv");
                        }
                        close(i); 
                        FD_CLR(i, &master); 
                    }
                    else
                    {

                        for(j = 0; j <= fdmax; j++)
                        {

                            if (FD_ISSET(j, &master)) 
                            {

                                if (j != listener && j != i)
                                {
                                    if (send(j, buf, nbytes, 0) == -1)
                                    {
                                        perror("send");
                                    }
                                }
                            }
                        }
                    }
                }
            } 
        } 
    } 

    return 0;
}

А вот мой код клиента:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <arpa/inet.h>

#define PORT "8888" 

#define MAXDATASIZE 100 
#define MAXNAMESIZE 25 

void *receive_handler(void *);

void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
    char message[MAXDATASIZE];
    char nickName[MAXNAMESIZE]; 
    int sockfd;                 
    char sBuf[MAXDATASIZE]; 
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"Usage: ./client address\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }


    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("Client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);   
            perror("Client: connect");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "Client: connection failed\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
            s, sizeof s);
    printf("Client: connection to %s\n", s);

    freeaddrinfo(servinfo); 

    puts("Give your username:");
    memset(&nickName, sizeof(nickName), 0);
    memset(&message, sizeof(message), 0); 
    fgets(nickName, MAXNAMESIZE, stdin);  
    pthread_t recv_thread;

    if( pthread_create(&recv_thread, NULL, receive_handler, (void*)(intptr_t) sockfd) < 0)
    {   
        perror("Failed on thread creation");
        return 1;
    }    
    puts("Connection established");

    puts("Welcome!\n");
    puts("[Type '/quit' to quit the chatroom]");

    for(;;)
    {
        char temp[6];
        memset(&temp, sizeof(temp), 0);

        memset(&sBuf, sizeof(sBuf), 0); 
        fgets(sBuf, 100, stdin); 

        if(sBuf[0] == '/' &&
           sBuf[1] == 'q' &&
           sBuf[2] == 'u' &&
           sBuf[3] == 'i' &&
           sBuf[4] == 't')
            return 1;


        int count = 0;
        while(count < strlen(nickName))
        {
            message[count] = nickName[count];
            count++;
        }
        count--;
        message[count] = ':';
        count++;

        for(int i = 0; i < strlen(sBuf); i++)
        {
            message[count] = sBuf[i];
            count++;
        }
        message[count] = '\0';
        if(send(sockfd, message, strlen(message), 0) < 0)
        {
            puts("Sent failed");
            return 1;
        }
        memset(&sBuf, sizeof(sBuf), 0);

    }

    pthread_join(recv_thread , NULL);
    close(sockfd);

    return 0;
}

void *receive_handler(void *sock_fd)
{
    int sFd = (intptr_t) sock_fd;
    char buffer[MAXDATASIZE];
    int nBytes;

    for(;;)
    {
        if ((nBytes = recv(sFd, buffer, MAXDATASIZE-1, 0)) == -1)
        {
            perror("recv");
            exit(1);
        }
        else
            buffer[nBytes] = '\0';
        printf("%s", buffer);
    }
}

1 Ответ

0 голосов
/ 03 января 2019

Да, по моему мнению, вы можете использовать два accept и затем назвать fork() или, что еще лучше, новый thread. Вы можете создать новый процесс / поток для каждой пары соединений.

Что ты имеешь в виду под разделением fd? Вы имеете в виду отправку fd одного клиента другому? В этом случае NO В функции потока вы можете сохранить соответствие между двумя клиентами, чтобы узнать, какие клиенты связаны. При получении сообщения от одного клиента благодаря сопоставлению вы знаете, какому клиенту вы должны отправить сообщение.

Я думаю, что все остальное зависит от дальнейших деталей, которые вы должны указать в своем вопросе. Дайте мне знать

Если каждый клиент может отправлять и получать, вы можете использовать многопоточность также на стороне клиента: один для отправки и один для получения.

...