简体   繁体   English

如何在客户端连接时两两连接客户端

[英]How to connect clients two by two as they connect

I'm facing a problem with sockets programming in C. I've coded a server where multiple clients can connect and send messages in a chatroom but i don't know how to connect two clients two by two. 我在用C进行套接字编程时遇到了问题。我已经编码了一个服务器,其中多个客户端可以在聊天室中连接并发送消息,但是我不知道如何将两个客户端两个一个地连接。

example: Client A connect to the Server but must wait in a queue waiting for Client B to connect. 示例:客户端A连接到服务器,但必须在队列中等待客户端B连接。 Client C connect but must wait in a queue waiting for Client D to connect. 客户端C连接,但必须在队列中等待客户端D连接。

Every pair of clients must be in their specific chatroom, do I need to share the fd between the two sockets of the 2 clients or link between the two sockets. 每对客户端必须位于其特定的聊天室中,我是否需要在2个客户端的两个套接字之间共享fd或两个套接字之间的链接。

I thought about using another way by using the function accept() twice before I fork() but I have to use child processes for that. 我曾想过在fork()之前两次使用功能accept()来使用另一种方式,但是我必须为此使用子进程。

Here is my server code: 这是我的服务器代码:

#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;
}

And here is my client code: 这是我的客户代码:

#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);
    }
}

Yes, in my opinion you can use two accept and then call a fork() or, even better, a new thread . 是的,我认为您可以使用两个accept然后再调用fork()或者甚至更好的方法是调用新thread You can create a new process/thread for each pair of connections. 您可以为每对连接创建一个新的进程/线程。

What do you mean sharing the fd? 分享fd是什么意思? You mean sending a fd of one client to another? 您的意思是将一个客户的FD发送给另一个? In this case NO You can keep, in the thread function, a mapping between the two clients to know which clients are paired. 在这种情况下 ,你可以保持在线程函数,两个客户端之间的映射,以了解哪些客户端配对。 When receiving a message from one client, thanks to the mapping, you know which client you are supposed to send the message to. 从一个客户端接收消息时,由于有了映射,您知道应该将消息发送到哪个客户端。

I think the rest depends on further details you should specify in your question. 我认为其余内容取决于您应该在问题中指定的其他详细信息。 Let me know 让我知道

If each client can both send and receive you might want to use multithreading on client-side too: one to send and one to receive. 如果每个客户端都可以发送和接收,则您可能也想在客户端使用多线程:一个发送,一个接收。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM