今天分享一个基于tcp通信的万人聊天室,非常的有意思,让我们一起来看看吧,首先我们做聊天室需要一个客户端一个服务器,
1.服务器用于为客户端提供服务,默默的守护在后台,给每一位客户提供服务,将群聊的每一位成员发送的消息转发给所有人,当然也可以看到一些信息。比如显示是哪一位客户登录了进来,以及当有成员离开群聊的时候会提示一个反馈信息(相当于客户给开发者的一些建议,把自己想对开发者的话发送给服务器,这条消息是不会发送给其他群友的!)
让我们来看看整体的的效果吧,左边是服务器,右边是三个客户

首先我们得先打开服务器件,然后运行客户端之后可以选择进入群聊还是退出,
如果是进入群聊的话,服务器会给客户端发送一个随机生成的五位数验证码,客户有两次机会,输错验两次证码就会跳转界面


输错次数达到上限之后跳转,重新选择进入群聊

进群之后系统提示给自己的群聊群一个昵称,然后进入群聊,服务器会打印出来是谁进入的群聊

进入后可以自由群聊,在每一位额群员的接受消息的后面会打印出来接收到消息的时间,如图一效果展示
群聊结束后发送quit的退出指令,服务器就会相应,给客户发送评价反馈



如果有评价就会打印出客户的评价消息,如果没有就是潇洒的离开,这条反馈消息是不会转发给所有的群友的,只是客户端和服务端的一次沟通,离开和加入群聊人数也会相应的增加减少,
好了说了这么多让我们来看看源码吧
首先来看看服务器的代码怎么写
第一个是服务端的.h文件,第二个是服务端的.c文件
#ifndef __CHATROOM__HH__
#define __CHATROOM__HH__
//头文件
#include<sys/wait.h>
#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>
#include<time.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
#include<signal.h>
#include<sys/ipc.h>
#include <sys/msg.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/select.h>
//定义客户端结构体,存放客户端套接字
typedef struct client
{
int cidstr[10];//存放客户端套接字
int cidstrnum;//人数
}client;
//客户界面
void StuDesk();
//客户群聊
void qunliao();
//客户进群验证
int yanzheng();
//聊天界面
void uiinit();
#endif
#include"touwenjian.h"
client clients={0};
void* pthread_fun(void* arg)
{
//强转赋值给cid
int* p=(int*)arg;
int cid=*p;
//发送内容数组
char sendbuf[128]={0};
//接收内容数组
char recvbuf[128]={0};
//读取客户端发来的名字
read(cid,recvbuf,sizeof(recvbuf));
//定义名字数组承接客户端发来的名字
char name[128] = {0};
strcpy(name,recvbuf);
//打印哪位用户上线
printf("%s:上线了\n",name);
char buf[128]="欢迎";
//把用户名追加到欢迎后
strcat(buf,name);
strcat(buf,"加入群聊\n");
for (int i = 0; i < 10; i++)
{
if (clients.cidstr[i]==0)
{ //如果没有人就退出
continue;
}
write(clients.cidstr[i],buf,sizeof(buf));
}
char kaitouname[128] = {0};
//将用户名和:放在最前面
strcpy(kaitouname,name);
strcat(kaitouname,":");
bzero(recvbuf,sizeof(recvbuf));//清空接收内容
while(strncmp("quit",recvbuf,4))//退出
{
bzero(recvbuf,sizeof(recvbuf));//清空接收内容
if(read(cid,recvbuf,sizeof(recvbuf)))//返回值非0成功读取客户发的内容
{
bzero(sendbuf,sizeof(sendbuf));//清空发送内容
strcpy(sendbuf,kaitouname);//将名字和:追加
//生成时间信息
time_t tm1 = 0;
time(&tm1);
struct tm* localtm;
localtm = localtime(&tm1);
char timestr[128] = {0};
//将时间信息(int 型)转化成字符串型存入timestr
//主要功能是把格式化的数据写入某个字符串中
sprintf(timestr,"\t\033[33m\033[32m接收时间[周%d时%d分%d秒%d]\033[0m\n",localtm->tm_wday,localtm->tm_hour,localtm->tm_min,localtm->tm_sec);
//将接收数组的\n剔除
char* huanhangp = strstr(recvbuf,"\n");
*huanhangp=0;
strcat(sendbuf,recvbuf);//将客户发的内容追加到后面
strcat(sendbuf,timestr);//将时间信息追加到 姓名:内容 后
//群发消息
for(int i = 0;i<10;i++)
{ //遍历发送
if(clients.cidstr[i] == cid || clients.cidstr[i] == 0)
{
continue;
}
write(clients.cidstr[i],sendbuf,sizeof(sendbuf));
}
}
}
for(int i = 0;i<10;i++)
{
if(clients.cidstr[i]==cid)//避免发给自己
{
clients.cidstr[i] = 0;
break;
}
}
clients.cidstrnum--;
char likai[128];
if(read(cid,likai,sizeof(likai)))
{
printf("\033[33m\033[32m%s:下线了,并说道%s\033[0m\n\n",name,likai);
}
else
{
printf("\033[33m\033[33m%s下线了,啥也没说只是潇洒的离开\033[0m\n",name);
}
close(cid);
printf("\n\033[0;43m现在有%d个人在线\033[0m\n\n",clients.cidstrnum);
}
int main(int argc,char* argv[])
{
//1.建立套接字
int sid=socket(AF_INET,SOCK_STREAM,0);
if (sid<0)
{
perror("套接子建立失败\n");
return -1;
}
//定义服务端地址结构体
struct sockaddr_in faddr={0};
faddr.sin_family=AF_INET;
faddr.sin_addr.s_addr=inet_addr("192.168.1.0");
faddr.sin_port=htons(11000);
//2.绑定服务端
if(bind(sid,(struct sockaddr*)&faddr,sizeof(faddr)))
{
printf("绑定失败\n");
}
else
{
printf("绑定成功\n");
}
//3.监听
if(listen(sid,10))
{
perror("listen is failed\n");
return -1;
}
else
{
printf("监听成功\n");
}
//定义客户端地址结构体
struct sockaddr_in caddr = {0};
int size = sizeof(caddr);
//定义线程id
pthread_t tid;
//定义客户端的ip
char ip[16] = "";
//4.链接客户端
while (1)
{
int rid=accept(sid,(struct sockaddr*)&caddr,&size);
if (rid>0)
{
for(int i = 0;i < 10;i++)
{
if(clients.cidstr[i] == 0)
{
clients.cidstr[i] =rid;
break;
}
}
printf("%s:%d:连接成功\n",inet_ntop(AF_INET,&caddr.sin_addr.s_addr,ip,16),ntohs(caddr.sin_port));
pthread_create(&tid,NULL,pthread_fun,(void*)&rid);
clients.cidstrnum++;
printf("\n\033[0;42m现在有%d个人在线\033[0m\n",clients.cidstrnum);
}
}
//关闭套接字
close(sid);
return 0;
}
然后我么看到的是客户端的.c文件
#include"touwenjian.h"
//群聊函数
void qunliao()
{
//1.建立套接字
int cid=socket(AF_INET,SOCK_STREAM,0);
if (cid<0)
{
perror("create socket failed\n");
return ;
}
//2.客户端链接服务端 客户端这边ip和端口写入结构体
struct sockaddr_in caddr={0};
caddr.sin_family=AF_INET;
caddr.sin_addr.s_addr=inet_addr("192.168.1.0");
caddr.sin_port=htons(11000);
char name[128]={0};
//2.连接客户端
if (connect(cid,(struct sockaddr*)&caddr,sizeof(caddr)))
{ //失败返回非0抱错
perror("连接服务器失败\n");
return ;
}
else
{
printf("\n\033[33m\033[31m连接服务器成功!\033[0m\n\n");
printf("\033[33m\033[36m请给你的聊天号取一个炫酷的昵称:\033[0m");
scanf("%s",name);
write(cid,name,strlen(name)+1);
}
//建立rest文件集合
fd_set rset={0};
char sendbuf[1024]="";//发送(键盘)
char recvbuf[1024]="";//接收
//监控键盘和套接字
while (strncmp("quit",sendbuf,4))
{
//把标准输入设备加入集合
FD_SET(STDIN_FILENO,&rset);
//把套接字加入集合
FD_SET(cid,&rset);
select(cid+1,&rset,NULL,NULL,NULL);
if (FD_ISSET(STDIN_FILENO,&rset))
{
bzero(sendbuf,sizeof(sendbuf));
read(STDIN_FILENO,sendbuf,sizeof(sendbuf));
write(cid,sendbuf,strlen(sendbuf));
}
if (FD_ISSET(cid,&rset))
{
bzero(recvbuf,sizeof(recvbuf));
read(cid,recvbuf,sizeof(recvbuf));
printf("%s",recvbuf);
}
}
char likai[128];
bzero(likai,sizeof(likai));
bzero(sendbuf,sizeof(sendbuf));
system("clear");
printf("\n\033[33m\033[31m是否直接退出群聊选择\033[0m yes \033[33m\033[31m直接退出\n选择\033[0m no \033[33m\033[31m则发表意见\n\033[0;42myes/no\033[0m\n");
scanf("%s",sendbuf);
if(strncmp("yes",sendbuf,3))
{
printf("请发表你对该群的看法\n");
scanf("%s",likai);
write(cid,likai,sizeof(likai));
}
else
{
close(cid);
}
//关闭套接字
close(cid);
return ;
}
//进群验证函数
int yanzheng()
{
system("clear");
AAA:
srand(time(NULL));
int yanzhen=rand()%100000;//随即生成验证码(最大999999)
int shuyan;//输入验证码匹配
int flg=0;
int ass=0;
printf("\n");
printf("\n\033[33m\033[32m等待系统发送进群验证码>>>>>\033[0m\n");
sleep(3);
printf("\033[33m\033[33m您的验证码是:%d\033[0m\n",yanzhen);
ppp:
sleep(1);
printf("请输入系统发送的验证码:");scanf("%d",&shuyan);
printf("\n\t\t\t\033[0;42m正在验证.............\033[0m\n");
sleep(2);
if(shuyan==yanzhen)
{
sleep(1);
printf("\n\033[33m\033[32m验证码正确,等待连接接进群.........\n\n");
sleep(3);
return 0;
}
else
{
flg++;
if(flg==2)
{
printf("\n\t\033[33m\033[33m进群验证码连续输入错误两次,无法进入,请做出以下选择\033[0m\n");
sleep(2);
system("clear");
printf("\n\t\033[33m\033[33m进群验证码连续输入错误两次,无法进入,请做出以下选择\033[0m\n");
sleep(1);
printf("\t\t=================================\n");
printf("\t\t* [1]直接退出到主界面 *\n");
printf("\t\t* [2]重新选择接收验证码 *\n");
printf("\t\t=================================\n");
printf("\t\t请输入您的选择>>");
scanf("%d",&ass);
if(ass==1)
{
printf("\033[33m\033[32m您选择直接退出到主界面,稍等\033[0m\n");
sleep(2);
return 1;
}
if(ass==2)
{
printf("\033[33m\033[32m您选择重新接收验证码,请稍等\033[0m\n");
sleep(2);
goto AAA;
}
}
sleep(1);
printf("\n\033[33m\033[31m\t红色警告!进群验证码输入错误一次,输入错误两次则禁止进入该群!\n\t\t\t\t请重新输入验证码!\n\n");
sleep(1);
goto ppp;
}
}
然后这里是ui.c文件
#include"touwenjian.h"
void StuDesk()
{
printf("\033[33m\033[33m*************************************************************\n");
printf("*************************************************************\n");
printf("* ==欢迎各位首富来此交流== *\n");
printf("* =========选择界面============ *\n");
printf("* *\n");
printf("*\033[33m\033[37m [1]进群交流 *\n");
printf("* *\n");
printf("* *\n");
printf("* *\n");
printf("* *\n");
printf("* [0]退出 *\n");
printf("* *\n");
printf("\033[33m\033[33m*************************************************************\n");
printf("*************************************************************\n");
printf("\033[33m\033[37m请输入你的选择:\n");
printf(">>>>");
}
接下来是最后的是main函数
#include"touwenjian.h"
int main(int argc,char* argv[])
{
int add=0;//客户选择变量
do
{
system("clear");
StuDesk();//客户界面
scanf("%d",&add);
switch(add)
{
case 1: system("clear");
int add=yanzheng();//验证进群函数
if(add==1)
{
break;
}
system("clear");
qunliao();//群聊函数
break;
case 2: system("clear");
break;
case 0: printf("感谢使用====群聊系统==== 已退出\n");
break;
default:printf("没有此选项,请重新输入您的选项\n");
break;
}
} while(add);
}
到这里的话代码展示和效果展示就到一段落了,主要是实现了一个群的功能,在群聊的基础上添加了一些其他的小功能,让整个程序的效果更优化了,比如显示时间,离场反馈,入场简介,入场验证码的功能,好了,快动手试试吧,快拉上你的小伙伴在自己写的群聊里畅所欲言吧😁