服务器端代码

#include "myhead.h"

struct thread_pool *pool=NULL;//线程池结构体指针
pclient_connfd cli=NULL;//已连接套接字数组结构体指针
pclient_info head=NULL;//客户端信息链表结构体指针
int totaluser;//总在线用户数

//任务函数
void *my_task(void *arg)
{
    pthread_detach(pthread_self());//设置线程结束后自动回收资源

    // printf("123\n");
    char buf[100];//接收缓冲区
    int ret=0;//是否接收到数据标志位
    char  *divide=":";//分隔符,分开收到数据的名字和内容

    // printf("000\n");
    pclient_info m = (pclient_info)arg;

    // printf("m->connfd:%d\n",m->connfd);
    // printf("m->client_name:%s\n",m->client_name);
    // printf("111\n");
    while(1)
    {
        //连接成功,开始接收数据
        bzero(buf,sizeof(buf));
        ret=recv(m->connfd,buf,sizeof(buf),0);//大于0说明接收到了数据
        // if(ret==-1)//5s内无数据
        // {
        //     printf("已超时\n");
        // }
        // printf("ret:%d\n",ret);
        if(ret>0)//5s内有数据
        {
            printf("%s\n",buf);//打印收到的内容
            
            //将收到的信息转发给所有客户端
            for(pclient_info h=head->next;h!=NULL;h=h->next)//将已经存入的用户名发送给用户端,来确保不会同名
            {
                send(h->connfd,buf,strlen(buf),0);
            }

            //char *p=strtok(buf,divide);//以冒号分割,得到前面的名字
            //p=strtok(NULL,buf);//获取冒号后的发送内容
            char *p= strtok(buf, divide);
   
            /* 继续获取其他的子字符串 */
            while( p != NULL ) 
            {
                p = strtok(NULL, divide);

                if(p!=NULL&&strncmp(p,"quit",strlen(p))==0)//客户端输入quit说明该客户端想要下线
                {
                    if(strlen(p)==4)
                    {
                        printf("%s已下线\n",m->client_name);
                        printf("(当前在线用户数:%d)\n", --totaluser);
                        close(m->connfd);
                        delete_clientnode(head,m->client_IP);//从链表中删除下线的客户端
                    }
                }
            }
            // printf( "%s\n", p );
            // printf("123\n");
            // if(strncmp(p,"quit",strlen(p))==0)//客户端输入quit说明该客户端想要下线
            // {
            //     if(strlen(p)==4)
            //     {
            //         printf("%s已下线\n",m->client_name);
            //         printf("(当前在线用户数:%d)\n", --totaluser);
            //         delete_clientnode(head,m->client_IP);//从链表中删除下线的客户端
            //         close(m->connfd);
            //     }
            // }
            // printf("222\n");
        }
    }
}

int main(int argc,char *argv[])
{
    //初始化线程池
    pool = malloc(sizeof(thread_pool));
	init_pool(pool,0);//初始化线程池

    //初始化已连接套接字数组
    cli=init_connfdaddr();
    
    //初始化客户端结构体链表
    head=init_clientlist();

    //创建套接字
    int socket_fd=socket(AF_INET,SOCK_STREAM,0);
    printf("socket_fd:%d\n",socket_fd);

    //绑定
    struct sockaddr_in server_addr;
    socklen_t len=sizeof(server_addr);//结构体长度
    bzero(&server_addr,len);

    server_addr.sin_family=AF_INET;//地址族
    server_addr.sin_port=htons(atoi(argv[1]));//端口号
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);//IP地址
    bind(socket_fd,(struct sockaddr *)&server_addr,len);

    //设置监听套接字
    listen(socket_fd,60);//可同时监听60个客户端

    //添加非阻塞属性(给socket_fd)
    int state;
    state=fcntl(socket_fd,F_GETFL);
    state|=O_NONBLOCK;//在原来的基础上再添加一个非阻塞属性
    fcntl(socket_fd,F_SETFL,state);

    
    struct sockaddr_in client_addr;//客户端结构体
    bzero(&client_addr,len);

    int tmp;//是否成功添加到数组中标志位
    char buf_name[50];//客户端用户名

    //接收数据
    while(1)
    {
        bzero(buf_name,sizeof(buf_name));
        //等待客户端连接
        int connfd=accept(socket_fd,(struct sockaddr *)&client_addr,&len);//一直阻塞直到有客户端连入
        // printf("connfd:%d\n",connfd);
        if(connfd>0)//表示有客户端连入
        {
            // //设置非阻塞属性
            // state=fcntl(connfd,F_GETFL);
            // state|=O_NONBLOCK;
            // fcntl(connfd,F_SETFL,state);

            //设置套接字属性为超时接收(给connfd)
            // struct timeval v;
            // v.tv_sec=10;//设置每次5秒接收时间
            // v.tv_usec=0;

            // setsockopt(connfd,SOL_SOCKET,SO_RCVTIMEO,&v,sizeof(v));//设置套接字超时接收属性

            if(totaluser>=60)//为-1表示数组满了,加入失败
            {
                printf("可连入服务器端的客户端数量已满,无法连接\n");
                close(connfd);
                continue;
            }

            recv(connfd,buf_name,sizeof(buf_name),0);//接收客户端的用户名

            // for(plient_info h=head->next;h!=NULL;h=h->next)//将已经存入的用户名发送给用户端,来确保不会同名
            // {
            //     send(connfd,h->client_name,strlen(h->client_name),0);
            //     recv(connfd,buf_name,sizeof(buf_name),0);//接收客户端的用户名
            // }

            printf("connfd:%d\n",connfd);
            printf("新连入的用户名为:%s\n",buf_name);
            printf("新连入的IP为:%s\n",inet_ntoa(client_addr.sin_addr));
            printf("(当前在线用户数:%d)\n", ++totaluser);

            if(find_nameclientnode(head,buf_name)==NULL&&find_IPclientnode(head,inet_ntoa(client_addr.sin_addr))==NULL)//等于NULL说明链表中没有存入该客户端(按名字)
            {
                new_clientnode(head,buf_name,inet_ntoa(client_addr.sin_addr),connfd);//将该客户端的名字与IP号插入到链表中
                print_clientlist(head);
            }
            

            pclient_info p=find_IPclientnode(head,inet_ntoa(client_addr.sin_addr));


            //新客户端连入,开辟一条新线程
            add_thread(pool,1);
            add_task(pool,my_task,(void *)p);
        }
    }
    
    //关闭套接字
    close(socket_fd);

    return 0;
}

## 客户端代码

```c
#include "myhead.h"

int main(int argc,char *argv[])
{

    //创建套接字
    int socket_fd=socket(AF_INET,SOCK_STREAM,0);
    printf("sockfd = %d\n",socket_fd);


    //连接服务器端
    struct sockaddr_in server_addr;
    socklen_t len=sizeof(server_addr);
    bzero(&server_addr,len);
    server_addr.sin_family=AF_INET;//地址族
    server_addr.sin_port=htons(atoi(argv[2]));//服务器端端口号
    inet_pton(AF_INET,argv[1],&server_addr.sin_addr);//服务器端ip地址

    connect(socket_fd,(struct sockaddr *)&server_addr,len);//套接字连接客户端

    //输入客户端用户名
    char bufname[50];//客户端名字
    bzero(bufname,sizeof(bufname));
    printf("请输入用户名:");
    scanf("%s",bufname);
    send(socket_fd,bufname,strlen(bufname),0);

    // char bufback[50];

    // while(strncmp(bufname,bufback,strlen(bufname)!=0))
    // {
    //     bzero(bufback,sizeof(bufback));
    //     recv(socket_fd,bufback,sizeof(bufback),0);//接收客户端的用户名
    //     if(strncmp(bufname,bufback,strlen(bufname)==0))
    //     {
    //         printf("用户名已存在,请重新输入\n");
    //         printf("请输入用户名:");
    //         scanf("%s",bufname);
    //         send(socket_fd,bufname,strlen(bufname),0);
    //     }
    // }

    char buf[50];//发送缓冲区
    char buf1[100];//拼接字符串后存入的数组
    char bufall[100];//群聊接收缓冲区
    int ret1=0;//客户端是否接收成功标志位

    while(1)
    {
        bzero(buf,sizeof(buf));
        bzero(buf1,sizeof(buf1));
        bzero(bufall,sizeof(bufall));
        printf("请输入要送发的内容:");
        scanf("%s",buf);
        sprintf(buf1,"%s:%s",bufname,buf);
        // sprintf(buf1,"张三:%s",buf);
        send(socket_fd,buf1,strlen(buf1),0);
        ret1=recv(socket_fd,bufall,strlen(bufall),0);
        printf("ret1:%d\n",ret1);
        if(ret1>0)
        {
            printf("%s\n",bufall);
        }
        printf("strlenbufall:%ld\n",strlen(bufall));
        if(strncmp(buf,"quit",strlen(buf))==0)
        {
            if(strlen(buf)==4)
            {
                break;
            }
        }
    }
    
    //关闭套接字
    close(socket_fd);

    return 0;
}

问题分析

客户端代码中,接收服务器端发送的消息的逻辑有问题。在循环中,你使用了recv函数接收服务器端发送的消息,但是没有正确处理接收到的消息。

修改后的客户端代码

#include "myhead.h"

int main(int argc,char *argv[])
{
    //创建套接字
    int socket_fd=socket(AF_INET,SOCK_STREAM,0);
    printf("sockfd = %d\n",socket_fd);

    //连接服务器端
    struct sockaddr_in server_addr;
    socklen_t len=sizeof(server_addr);
    bzero(&server_addr,len);
    server_addr.sin_family=AF_INET;//地址族
    server_addr.sin_port=htons(atoi(argv[2]));//服务器端端口号
    inet_pton(AF_INET,argv[1],&server_addr.sin_addr);//服务器端ip地址

    connect(socket_fd,(struct sockaddr *)&server_addr,len);//套接字连接客户端

    //输入客户端用户名
    char bufname[50];//客户端名字
    bzero(bufname,sizeof(bufname));
    printf("请输入用户名:");
    scanf("%s",bufname);
    send(socket_fd,bufname,strlen(bufname),0);

    char buf[50];//发送缓冲区
    int ret=0;//接收数据的长度

    while(1)
    {
        bzero(buf,sizeof(buf));
        printf("请输入要发送的内容:");
        scanf("%s",buf);

        if(strcmp(buf, "quit") == 0) {
            send(socket_fd, buf, strlen(buf), 0);
            break;
        }

        send(socket_fd,buf,strlen(buf),0);

        ret=recv(socket_fd,buf,sizeof(buf),0);
        if(ret>0)
        {
            printf("收到服务器端的消息:%s\n",buf);
        }
    }

    //关闭套接字
    close(socket_fd);

    return 0;
}

这样,客户端就可以正确接收到服务器端发送的消息了。注意,在客户端输入"quit"时,会发送给服务器端并退出循环。

多线程聊天服务器端代码 - C语言实现

原文地址: https://www.cveoy.top/t/topic/jqv3 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录