基于TCP的一对多通信

注意问题:

(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 8080

int 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 8080

int 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,&copy_fds,NULL,NULL,NULL);//返回活跃的字符数
        if(ret_select == 0)    
        {
            continue;
        }
        else
        {
            if(FD_ISSET(sockfd,&copy_fds)) //套接字中有信息,接收信息
            {
                memset(buffer,0,BUFFER_SIZE);
                len = recv(sockfd,buffer,sizeof(buffer),0);

                if(len == 0)//收到信息为空,即server端退出
                {
                    FD_CLR(sockfd,&copy_fds);
                    fdmax = 0;
                }
                else
                {
                    printf("Receive message:  %s/n",buffer);
                }
            }

            if(FD_ISSET(0,&copy_fds)) //从标准输入输入信息,发送到server端
            {
                fgets(buffer,sizeof(buffer),stdin);
                len=send(sockfd,buffer,strlen(buffer),0);
            }
        }
    }
    close(sockfd);
    return 0;
}
 

 

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值