Question

I wanna make a chat room for 4 guys in UDP. Here's the code:

<code>
#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<unistd.h>
#define PORT 9999
#define SIZE 1024
int c;
int i=0;
int k=0;
char name[4][20];
char ip[4][16];
FILE * txt;
struct sockaddr_in seraddr,cliaddr[3],getcliaddr[3];

void gettxt()
{
    txt=fopen("ip.txt","r");
    for(k=0;k<4;k++)
    {
        c=fgetc(txt);
        while(c!=' ')
        {
            name[k][i]=(char)c;
            i++;
            c=fgetc(txt);
        }
        name[k][i]='\0';
        i=0;
        c=fgetc(txt);
        while(c<'1'||c>'3')
            c=fgetc(txt);
        while(c!='\n')
        {
            ip[k][i]=c;
            c=fgetc(txt);
            i++;
        }
        ip[k][i]='\0';
        i=0;
    }
    for(k=0;k<4;k++)
        printf("<%s>%s\n",name[k],ip[k]);
}

int compare(struct sockaddr_in whichcli)
{
    int w=1;
    for(w=1;w<4;w++)
    {
        if(whichcli.sin_addr.s_addr==cliaddr[w].sin_addr.s_addr)
            break;
    }
    return w;
}

int main()
{
    int com;//compare return value (just the "k")
    int qq;
    int ret;
    int ser;
    int maxsock;
    char bufrecv[SIZE];
    char bufsend[SIZE];
    socklen_t clilen=sizeof(cliaddr[1]);//the same wa ~~I guess

    gettxt();

    fd_set readfds;

    seraddr.sin_family=AF_INET;
    seraddr.sin_addr.s_addr=inet_addr(ip[0]);
    seraddr.sin_port=htons(PORT);

    for(qq=1;qq<4;qq++)
    {
        cliaddr[qq].sin_family=AF_INET;
        cliaddr[qq].sin_addr.s_addr=inet_addr(ip[qq]);
        cliaddr[qq].sin_port=htons(PORT);
    }
    ser=socket(AF_INET,SOCK_DGRAM,0);
    bind(ser,(struct sockaddr*)&seraddr,sizeof(seraddr));

    if(STDIN_FILENO>ser)
        maxsock=STDIN_FILENO;
    else 
        maxsock=ser;

    while(1)
    {
        FD_ZERO(&readfds);  
        FD_SET(STDIN_FILENO,&readfds);
        FD_SET(ser,&readfds);
        ret=select(maxsock+1,&readfds,NULL,NULL,0); 
        if(ret>0)
        {
            if(FD_ISSET(STDIN_FILENO,&readfds))
            {
                fgets(bufsend,SIZE,stdin);
                for(qq=1;qq<4;qq++)
                    sendto(ser,bufsend,SIZE,0,(struct sockaddr*)&cliaddr[qq],clilen);
            }
            if(FD_ISSET(ser,&readfds))
            {
                for(qq=1;qq<4;qq++)
                {
                    recvfrom(ser,bufrecv,SIZE,0,(struct sockaddr*)&getcliaddr[qq],&clilen);
                    com=compare(getcliaddr[qq]);//
                    printf("<%s>%s\n",name[com],bufrecv);
                }
            }
        }
    }
    return 0;
}
</code>

The file "ip.txt" is just the name-IP file, shows as follows:

<txt>
I 192.168.1.2
Sun 192.168.1.4
Jerry 192.168.1.5
Peter 192.168.1.6
</txt>

The first contained the information of my own, the following 3 were other guy's. But when I ran the program with only one guy, first of all, we can chat with each other with nonblocking. After several words, it didn't work well. I ran gcc and I guessed that when the "sendto" buffer was full, it blocked waiting the other "recvfrom" the buf. The program I wrote is for 4 guys, but I just ran it with only one guy, the other two can't recvfrom it(still the buffer in "recvfrom" I guess,am I right?). So the "sendto" buffer was full and blocked. That is what I think,am I right? If it's true, and how to solve it? I mean how to clean the "sendto" buffer periodically? Or there is some other way? Thanks very much ~~;-)

Was it helpful?

Solution

I think the problem is in your logic, when a client sends you message you loop through all the clients and call recvfrom, you should only call recvfrom once for each time select returns.

if(FD_ISSET(ser,&readfds))
{    
    struct sockaddr_in src_addr;
    socklen_t addrlen = sizeof(src_addr);
    recvfrom(ser, bufrecv, SIZE, 0, (struct sockaddr*)&src_addr, &addrlen);
    com=compare(src_addr);//
    printf("<%s>%s\n",name[com],bufrecv);    
}

Edit: It seems that you use getcliaddr in recvfrom as the src_addr which means that each time you receive a message from a client you overwrite the address of another client, this is not a problem for one client but for more than one you could overwrite the first one with the sender's address and you if you call recvfrom again it would block because you think it's waiting for the first client when it's actually waiting for the second one.

OTHER TIPS

You're misunderstanding the meaning of the src_addr parameter to recvfrom(2):

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                 struct sockaddr *src_addr, socklen_t *addrlen);

src_addr is an OUTPUT from this function, not an input. It should point at an empty struct sockaddr which it will fill in with the source address of the packet that was received. This means that you can't pick a particular client to receive a packet from (and you don't really want to, as you don't know who is going to be typing next). Instead, you just receive a packet, and then figure out who it was from.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top