多线程聊天服务器端代码 - C语言实现
服务器端代码
#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"时,会发送给服务器端并退出循环。
原文地址: https://www.cveoy.top/t/topic/jqv3 著作权归作者所有。请勿转载和采集!