基于TCP的一对多通信

    技术2022-05-20  32

    注意问题:

    (1):输入聊天信息,用fgets()而不是scanf(),scanf()遇到空格结束录入;

    (2):用send()发送信息时应用strlen(),这样可避免接收并打印时乱码出现;

    (3):当客户端退出时,在server()端应使用FD_CLR()清除对应的sockfd;

           缺少清除的后果可能是在client端退出时,server端无限循环(出错),并显示select()的返回值为1或其它大于0的数;

    (4):client端的地址结构中填充的是server端的IP,即seraddr.sin_addr.s_addr=192.... ,这可以保证server端收到client端的信息,转发后可保证client端也能收到。若写成seraddr.sin_addr.s_addr=INADDR_ANY,则server端能收到,但client端接收不到server端的转发信息。

    (5):套接字描述符集的存储方式为大端序,开一个客服端,描述符集对应为11000,注意是从右向左,最右面三位分别对应标准输入,标准输出,标准错误。右面第一个1为server端创建的第一个套接字描述符3对应的位,右面第二个1为开第一个client端时server端创建的新的套接字的描述符,以此类推。

    那么对应位到底是0还是1呢,分两种情况:[1]:若有新客户端打开,则右面第四位为1,反之为0;[2]:若对应的client端有数据写入套接字,则该client的套接字(server端创建的)对应位为1,反之为0。

    (6)在server端和client端,while(1){}中,select()中的描述符集均为原始描述符集的复制体。因为select()之后,不活跃的文件描述符将被置空,若使用原始描述符集,则会破坏原始描述符集。造成各种问题......

    以下为server.c文件:

     

    #include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#define BUFFER_SIZE 1024#define ECHO_PORT 8080int main(){    fd_set master;    fd_set read_fds;    char buffer[BUFFER_SIZE];    int sockfd;    int fdmax;    int addrlen;    int newfd;    int nbytes;    int ret_select;    int i;    int j;    struct sockaddr_in seraddr;    struct sockaddr_in cliaddr;    FD_ZERO(&master);//置空套接字描述符集    FD_ZERO(&read_fds);    sockfd=socket(AF_INET,SOCK_STREAM,0);    

        //解决端口冲突问题    int opt;    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));    bzero(&seraddr,sizeof(seraddr));    seraddr.sin_family=AF_INET;    seraddr.sin_port=htons(ECHO_PORT);    seraddr.sin_addr.s_addr=INADDR_ANY;    //绑定套接字和地址结构

        bind(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));    

        //监听    listen(sockfd,10);        //将套接字描述符加到描述符集里

        FD_SET(sockfd,&master);    fdmax=sockfd;    while(1)    {        read_fds = master;//复制描述符集,为了保存原有描述符集        ret_select = select(fdmax+1,&read_fds,NULL,NULL,NULL);//不活跃的描述符对应位置0                for(i=0;i<=fdmax;i++)        {            if(FD_ISSET(i,&read_fds))//经过select()后的read_fds中对应位为1则FD_ISSET()返回1            {                if(i==sockfd)//判断是否为server中的第一个套接字                {                    addrlen=sizeof(cliaddr);                                        newfd=accept(sockfd,(struct sockaddr *)&cliaddr,&addrlen);//创建新的套接字                    FD_SET(newfd,&master);//将新的套接字写入描述符集中                    fdmax=newfd;                    printf("new connection from %s on socket %d/n",inet_ntoa(cliaddr.sin_addr),newfd);                }                else                {                    memset(buffer,0,BUFFER_SIZE);//置空buffer                    nbytes = recv(i,buffer,sizeof(buffer),0);//接收信息存于buffer中                    printf("from client: %s/n",buffer);                                        if(nbytes==0)   //没收到信息,即client端退出                    {                        FD_CLR(i,&master); //将该client对应的套接字从描述符集中清除                    }                    for(j=0;j<=fdmax;j++) //循环群发收到的信息                    {                        if(FD_ISSET(j,&master))                        {                            if(j!=sockfd && j!=i)//群发收到的信息但排除本身和来源端                            send(j,buffer,strlen(buffer),0);                            }                       }                   }            }        }    }}

     

    以下为client.c:

     

    #include <stdio.h>#include <stdlib.h>#include <sys/types.h>#include <sys/socket.h>#include <unistd.h>#include <netinet/in.h>#include <error.h>#include <errno.h>#include <string.h>#include <sys/wait.h>#include <sys/times.h>#include <time.h>#include <arpa/inet.h>#define RET_OK 0#define RET_ERROR -1#define BUFFER_SIZE 1024#define ECHO_PORT 8080int main(int argc,char **argv){    int sockfd;    int fdmax;    int ret_select;    int i;    int len;        char buffer[BUFFER_SIZE];    fd_set read_fds;

        fd_set copy_fds;    struct sockaddr_in seraddr;    sockfd = socket(AF_INET,SOCK_STREAM,0);        int opt;    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));        memset(&seraddr,0,sizeof(seraddr));    seraddr.sin_family = AF_INET;    seraddr.sin_port = htons(ECHO_PORT);    seraddr.sin_addr.s_addr = inet_addr("192.168.1.142");    connect(sockfd,(struct sockaddr *)&seraddr,sizeof(seraddr));            FD_ZERO(&read_fds);    FD_SET(0,&read_fds);    FD_SET(sockfd,&read_fds);    fdmax = sockfd;    while(1)    {        copy_fds = read_fds;

            ret_select = select(fdmax+1,©_fds,NULL,NULL,NULL);//返回活跃的字符数        if(ret_select == 0)             {            continue;        }        else        {            if(FD_ISSET(sockfd,©_fds)) //套接字中有信息,接收信息            {                memset(buffer,0,BUFFER_SIZE);                len = recv(sockfd,buffer,sizeof(buffer),0);                if(len == 0)//收到信息为空,即server端退出                {                    FD_CLR(sockfd,©_fds);                    fdmax = 0;                }                else                {                    printf("Receive message:  %s/n",buffer);                }            }            if(FD_ISSET(0,©_fds)) //从标准输入输入信息,发送到server端            {                fgets(buffer,sizeof(buffer),stdin);                len=send(sockfd,buffer,strlen(buffer),0);            }        }    }    close(sockfd);    return 0;} 

     

     


    最新回复(0)