无法在客户端之间发送消息(C)

时间:2017-12-05 09:06:57

标签: c sockets

我希望使用客户端服务器(具有多个客户端的服务器)完成minitalk。使用name参数启动客户端,并完成与服务器的连接。

服务器会记住每个客户端的名称。然后,在每个客户端,我可以发送消息到另一个 - 例如我有2个客户端启动 - c1和c2,我可以从c1发送消息 - c2 hello,它应该在c2中显示为收到的消息。我也可以从c1发送到c1,这没关系。我期望的是在客户代码之下(因为服务器运行良好)

    #include <stdio.h> 
#include <string.h>   //strlen 
#include <stdlib.h> 
#include <errno.h> 
#include <unistd.h>   //close 
#include <arpa/inet.h>    //close 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros 
#define PORT 8080

int main(int argc, char *argv[])
{
    struct sockaddr_in address;
    int sock = 0, thisclient = 0, valread;
    struct sockaddr_in serv_addr;
    int activity;

    fd_set readfds; 

    char *hello = malloc(100);
    char tab[1024] = {0};
    char* name = argv[1];

    if((thisclient = socket(AF_INET , SOCK_STREAM , 0)) == 0)  
    {  
        perror("socket failed");  
        exit(EXIT_FAILURE);
    } 

    memset(&serv_addr, '0', sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary form
    if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0) 
    {
        printf("\nInvalid address/ Address not supported \n");
        return -1;  
    }

    if (connect(thisclient, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
        printf("\nConnection Failed \n");
        return -1;
    } else {

    send(thisclient, name, strlen(name), 0);
    }



    valread = read(thisclient , tab, 1024);

    if(strcmp(tab,name) != 0) {
        printf("Already used user!");
        return -1;
    } else {
        bzero(tab, sizeof(tab));
        send(thisclient, "Connection", 11, 0);
        valread = read(thisclient , tab, 1024); 
        printf("%s---\nSending message to not in a list causes server exiting\n---\n",tab);
        bzero(tab, sizeof(tab));
    }



    while(1) { 

     FD_ZERO(&readfds);
     FD_SET(0,&readfds); //stdin
     FD_SET(thisclient,&readfds);

        activity = select( thisclient+1 , &readfds , NULL , NULL , NULL);  

        if ((activity < 0) && (errno!=EINTR))  
        {  
            printf("select error");  
        }  

        for(int i=0; i<thisclient+1; ++i) {

            if (FD_ISSET( i , &readfds))  
            { 

                if(i== thisclient) {
                    char* buffer = malloc(1025);

                    if ((valread = read( thisclient , buffer, 1024)) != 0) {  
                        if(strcmp(buffer,"Server error, is closing") == 0) {
                            exit(0);
                        }

                        printf("Received (yours name) (message): %s\n",buffer );
                        bzero(buffer, sizeof(buffer));

                    } 
                    free(buffer);
                }
                else if(i == 0) {
                    char* buffer = malloc(1025);
                    if ((valread = read( 0 , buffer, 1024)) != 0) {  
                        if(strcmp(buffer,"Server error, is closing") == 0) {
                            exit(0);
                        }

                        send(thisclient, buffer, 1024, 0);
                        printf("Send (yours name) (message): %s\n",buffer );
                        bzero(buffer, sizeof(buffer));

                    } 
                    free(buffer);
                }
            }
        }

    }
    return 0;
}

我应该可以发送&#34; ping-pong&#34;等消息,所以c1发送到c2,c2发送到c1,c1发送两个消息到c2,c2发送一个到c1等...

此刻,连接工作正常,然后我发送第一条消息(发送方和接收方都不重要,例如c1发送到c2)。我可以从c1发送许多消息到c2,但是当我想响应时(从c2到c1的消息)没有任何反应。

我也注意到,这条消息甚至不是服务器,所以它肯定是客户的错。我仍然可以从c1发送消息到c2,但不是从内到外。什么&#34;冲洗&#34;这个状态?我从c1发送消息给c1。在此消息之后,c2中的前一个也显示在c1中(最近从c1到c1)。但我必须&#34;冲洗&#34;每当我想收到来自c2的消息时。

我应该在select()来电添加内容吗? (例如写入的参数)。或者循环没有做好?

-EDIT服务器代码

#include <stdio.h> 
#include <string.h>   //strlen 
#include <stdlib.h> 
#include <errno.h> 
#include <unistd.h>   //close 
#include <arpa/inet.h>    //close 
#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros 

#define TRUE   1 
#define FALSE  0 
#define PORT 8080

int main(int argc , char *argv[])  
{  
    int opt = TRUE;  
    int master_socket , addrlen , new_socket , client_socket[30] , 
          max_clients = 30 , activity, i , valread , sd;  
    int max_sd;  
    struct sockaddr_in address;  

    char client_name[30][100];


    //set of socket descriptors 
    fd_set readfds;  

    //initialise all client_socket[] to 0 so not checked 
    for (i = 0; i < max_clients; i++)  
    {  
        client_socket[i] = 0;  
    }  

    //create a master socket 
    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)  
    {  
        //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 < 0) && (errno!=EINTR))  
        {  
            printf("select error");  
        }  

        //If something happened on the master socket , 
        //then its an incoming connection 
        if (FD_ISSET(master_socket, &readfds))  
        {  
            if ((new_socket = accept(master_socket, 
                    (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)  
            {  
                perror("accept");  
                exit(EXIT_FAILURE);  
            }  

            //inform user of socket number - used in send and receive commands 
            printf("New connection, ip- %s, port- %d\n" , inet_ntoa(address.sin_addr) , ntohs
                  (address.sin_port));  

            char* tempclient = malloc(100);
            read(new_socket, tempclient, 100);


            //add new socket to array of sockets 
            for (i = 0; i < max_clients; i++)  
            {  
                //if position is empty 
                if( client_socket[i] == 0 )  
                {  
                    client_socket[i] = new_socket;
                    strcpy(client_name[i], tempclient);
                    printf("Adding to list of sockets as %s - pos %d\n",client_name[i],i);  
                    send(new_socket, client_name[i], strlen(client_name[i]), 0);
                    read(new_socket, tempclient, 100);

                    char list[1000];
                    strcpy(list, "List of users\n");
                    for(int j=0; client_socket[j] != 0; ++j) {
                        strcat(list, client_name[j]);
                        strcat(list,"\n");
                    }
                    send(new_socket, list, strlen(list), 0);
                    break;  
                } else if(strcmp(client_name[i],tempclient) == 0) {
                    send(new_socket, "ERROR", 5, 0);
                    break;
                } else if(strcmp(tempclient,"exit ") == 0) {
                    send(new_socket, "ERROR", 5, 0);
                    break;
                }
            }  
        }  

        //else its some IO operation on some other socket
        for (i = 0; i < max_clients; i++)  
        {  
            char* buffer = malloc(1025);
            char* name = malloc(100);
            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 );  
                    client_socket[i] = 0;  
                }       
                //Echo back the message that came in 
                else {  
                    strcpy(name, buffer);
                    strtok(name, " ");
                    puts("bufor = ");
                    puts( buffer);
                    for (i = 0; i < max_clients; i++) //loop seraching for name
                    {
                        if( client_socket[i] != 0 ) { //client in array
                            if(strcmp(client_name[i], name) == 0) {// message name same as client name

                                 send(client_socket[i] , buffer , 1024 , 0 );
                                 bzero(buffer, sizeof(buffer));
                                bzero(name, sizeof(name)); 
                                 break;
                            }
                        }
                        else { //no more clients
                            for (i = 0; i < max_clients; i++) //loop after clients 
                            {
                                if( client_socket[i] != 0 ) { //send error
                                    send(client_socket[i], "Server error, is closing", 30, 0);
                                } else break;
                            }

                            puts("Message error, whole communication is closing\n");
                            exit(0);
                        }
                    }
                    bzero(buffer, sizeof(buffer));
                    bzero(name, sizeof(name));
                }  
            }  
             free(buffer);
             free(name);
        }

    } 

    return 0;  
}  

1 个答案:

答案 0 :(得分:1)

主要错误在于服务器中的以下循环:

        //else its some IO operation on some other socket
        for (i = 0; i < max_clients; i++)  
        {  
            …
                //Check if it was for closing , and also read the 
                //incoming message 
                if ((valread = read( sd , buffer, 1024)) == 0)  
                …
                //Echo back the message that came in 
                else {  
                    …
                    for (i = 0; i < max_clients; i++) //loop seraching for name
                    …
                                 break;
                    …
                }  
            …
        }

内部循环中使用的循环变量i与外部循环中使用的循环变量相同,导致尝试两次读取相同的客户端消息,从而阻塞服务器,直到该客户端重新发送内容。

第二个错误是tempclient尽管用作字符串,但并不总是以空值结尾。快速修复:将char* tempclient = malloc(100)替换为char *tempclient = calloc(100, 1)