实现简单的实时聊天系统

一、功能说明

        利用 TCP 套接字实现简单的 QQ 聊天系统。该聊天系统支持新用户注册、用户登录和退出、在线聊天服务、在线用户查询功能。

二、项目框图及说明

        如图所示,当用户通过客户端成功建立连接后,服务器使用 pthread_create 创建一个子线程用于与该客户端通信,pthread_create 的第三个参数 Handle 是该项目中集中处理用户操作并给出相关反馈的函数,该子线程执行这个函数。当另一个用户建立连接成功后,再创建一个与之对应的子线程,实现服务器并发, 达到简单在线聊天的目标。

三、项目关键数据结构  

        在这个项目中,需要将线程 id、通信标识符、客户端地址信息全部传入子线程,因此需要将这些数据打包成结构体 SockInfo,每个线程都对应相关信息, 因此还要定义结构体数组 SockInfo infos[];另外需要存放用户信息的结构体数组 User list[];

四、关键函数说明

void WFile(User t):将用户名写入文件的函数

int IsExist(User u):判断用户是否存在的函数

void *Handle(void *arg):集中处理用户操作并给出相关反馈的函数(在子线程中运行)

五、本项目源码

服务器端:

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <sys/socket.h>
#include <netinet/in.h> 
#include <netdb.h> 
#include <arpa/inet.h> 
#include <pthread.h> 
#include <unistd.h> 
#define MAX_SIZE 100 
#define SRVPORT 3000 
#define BACKLOG 10 
 
 
struct SockInfo //信息结构体,存放Handle函数所需要的信息 
{ 
 	int fd;   // 通信 
 	int online; 
 	pthread_t tid;              // 线程ID 
 	struct sockaddr_in addr;    // 地址信息 
 	char onlineuser[20]; 
}; 
 
struct SockInfo infos[128]; 
int max = sizeof(infos) / sizeof(infos[0]); 
 
 
 
 
typedef struct 
{ 
 	char username[10]; 
 	char password[10]; 
} User; 
User list[MAX_SIZE]; User u; 
 
char* filename = "user.txt"; 
 
int x = 0, y = 0; User   lg1[20]; User   lg2[20]; char tempname[20]; int count=0; 
 
void WFile(User t)        //将用户名写入文件的函数; 
{ 
 	FILE* fw = fopen(filename, "a+"); 
 	fprintf(fw, "%s", t.username); 
 	fprintf(fw, "\t"); 
 	fprintf(fw, "%s", t.password); 
 	fprintf(fw, "\n"); 
} 
 
int IsExist(User u)   //判断用户是否存在 
{ 
 	int i; 
 	for (i = 0; i < MAX_SIZE; i++) 
 	{ 
 	 	if (0 == strcmp(list[i].username, u.username) && 0 == strcmp(list[i].password, u.password)) 
 	 	{ 
 	 	 	return 1; 
 	 	} 
 	} 	
 		
 	return -1; 
} 
 
void *Handle(void *arg) { 
 
 	 
 
 	char buf[1024]; 
 	struct SockInfo* info = (struct SockInfo*)arg; 
 	int r; 
 	r=read(info->fd, buf, 1023); 
 	buf[r]=0; 
 	if (strcmp(buf, "1") == 0) { 
 	 	printf("客户请求登录\n"); 
 	 	 
 	 	write(info->fd, "请输入用户名:", strlen("请输入用户名:")); 
 	 	r = read(info->fd, buf, 1023); 
 	 	buf[r] = 0; 
 	 	strcpy(lg1[x].username, buf); 
 	 	strcpy(tempname, buf); 
 	 	printf("用户名:%s\n", lg1[x].username); 
 	 	write(info->fd, "请输入密码:", strlen("请输入密码:")); 
 	 	r = read(info->fd, buf, 1023); 
 	 	buf[r] = 0; 
 	 	strcpy(lg1[x].password, buf); 
 	 	printf("密码:%s\n", lg1[x].password); 
 	 	if (1 == IsExist(lg1[x])) 
 	 	{ 
 	 	 	printf("登录成功\n"); 
 	 	 	write(info->fd, "登录成功\n", strlen("登录成功\n")); 
 	 	 	info->online = 1; strcpy(info->onlineuser, tempname); 
 	 	 	x++; 
 
 	 	} 
 	 	else 
 	 	{ 
 	 	 	printf("账号或密码错误\n"); 
 	 	 	write(info->fd, "账号或密码错误\n", strlen("账号或密码错误\n")); 
 	 	} 
 
 	} 
 
 	else if (strcmp(buf, "2") == 0) 
 
 	{ 
 	 	int a; 
 	 	printf("客户请求注册\n"); 
 	 	write(info->fd, "请输入用户名:", strlen("请输入用户名:")); 
 	 	r = read(info->fd, buf, 1023); 
 
 	 	buf[r] = 0; 
 	 	strcpy(lg2[y].username, buf); 
 	 	printf("%s", buf); 
 	 	int temp = 0; 
 		
 	 	for (a = 0; a < MAX_SIZE; a++) 
 	 	{ 
 	 	 	if (0 == strcmp(list[a].username, lg2[y].username)) 
 	 	 	{ 
 	 	 	 	write(info->fd, "该账号已注册", strlen("该账号已注册")); 
 	 	 	 	temp = 1; 
 	 	 	 	break; 
 	 	 	} 
 		
 	 	} 
 	 	if (temp == 0) { 
 	 	 	write(info->fd, "账号可注册", strlen("账号可注册")); 
 	 	 	write(info->fd, "请输入密码:", strlen("请输入密码:")); 
 	 	 	r = read(info->fd, buf, 1023); 
 	 	 	buf[r] = 0; 
 	 	 	strcpy(lg2[y].password, buf); 
 	 	 	WFile(lg2[y]); 
 	 	 	strcpy(list[count].username, lg2[y].username); 
 	 	 	strcpy(list[count].password, lg2[y].password); 
 	 	 	count++; 
 	 	 	write(info->fd, "已注册成功", strlen("已注册成功")); 
 	 	 	y++; 
 	 	} 
 	} 	
 	char roster[1024]; 
 	char chat[1024]; 
 	char buff[1024]; 
 	int l=0; 
 	char roster1[1024]; 
 	while (1) { 
 	 	strcpy(roster1,"当前在线用户有:"); 
 	 	for (int i = 0; i < max; i++) 
 	 	{ 
 	 	 	if (infos[i].online == 1) 
 	 	 	{ 
 	 	 	 	strcat(roster, infos[i].onlineuser); 
 	 	 	 	strcat(roster, " "); 
 	 	 	} 
 	 	} 
 
 	 	strcat(roster1,roster); 
 	 	write(info->fd, roster1, strlen(roster1)); 
 	 	bzero(roster,strlen(roster)); 
 	 	bzero(roster1,strlen(roster1)); 
 		
 	 	r = read(info->fd, buf, 1023); 
 	 	buf[r] = 0; 
 	 	 
 	 	if (strcmp(buf, "#") == 0) { 
 	 	 	bzero(info, sizeof(info)); 
 	 	 	info->fd = -1; 
 	 	 	info->tid = -1; 
 	 	 	info->online = 0; 
 	 	 	bzero(info->onlineuser, strlen(info->onlineuser)); 
 	 	 	close(info->fd); 
 	 	 	pthread_exit(NULL); 
 	 	} 
 	 	else { 
 	 	 	l = read(info->fd, buff, 1023); 
 	 	 	buff[l] = 0; 
 	 	 	strcpy(chat, info->onlineuser); 
 	 	 	strcat(chat, ":"); 
 	 	 	strcat(chat, buff); 
 	 	 	for (int i = 0; i < max; i++) 
 	 	 	{ 
 	 	 	 	if (strcmp(buf, infos[i].onlineuser) == 0) 
 	 	 	 	{ 
 	 	 	 	 	write(infos[i].fd, chat, strlen(chat)); 
 	 	 	 	} 
 	 	 	 	 
 	 	 	} 
 	 	} 
 		
 	} 	
} 		
 		
int main() 
{ 
 	FILE* fp = fopen(filename, "r"); 
 	int i = 0; 
 	if (NULL == fp) 
 	{ 
 	 	printf("FILE NOT FOUND"); 
 	 	return -1; 
 
 
 	} 
 	char uname[10]; 
 	char upassword[10]; 
 	for (i = 0; i < MAX_SIZE; i++) 
 	{ 
 	 	fscanf(fp, "%s%s", uname, upassword); 
 	 	strcpy(list[i].username, uname); 
 	 	strcpy(list[i].password, upassword); 
 	} 
 	for (i = 0; i < MAX_SIZE; i++) 
 	{ 
 	 	if (strcmp(list[i].username, "") == 0) 
 	 	 	count = i; 
 	} 
 	int sockfd; 
 	struct sockaddr_in srvaddr; 
 	if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 
 	 	perror("socket creat error"); 
 	 	exit(1); 
 
 	} 
 	srvaddr.sin_family = AF_INET; 
 	srvaddr.sin_port = htons(SRVPORT); 
 	if (inet_aton("192.168.87.128", &srvaddr.sin_addr) == 0) { 
 	 	printf("addr convert error\n"); 
 	 	close(sockfd); 
 	 	exit(1); 
 	} 
 
 	bzero(&(srvaddr.sin_zero), 8); 
 
 	if (bind(sockfd, (struct sockaddr*)&srvaddr, sizeof(struct sockaddr)) == -1) { 
 	 	printf("bind error\n"); 
 	 	close(sockfd); 
 	 	exit(1); 
 	} 
 
 	if (listen(sockfd, BACKLOG) == -1) { 
 	 	printf("listen error\n"); 
 	 	close(sockfd); 
 	 	exit(1); 
 	} 
 
 
 	int len = sizeof(struct sockaddr); 
 
 	 
 	for (int i = 0; i < max; ++i) 
 	{ 
 	 	bzero(&infos[i], sizeof(infos[i])); 
 	 	infos[i].fd = -1; 
 	 	infos[i].tid = -1; 
 	 	infos[i].online=0; 
 	 	bzero(infos[i].onlineuser,20); 
 	} 
 	while (1) { 
 	 	struct SockInfo* pinfo; 
         for(int i=0; i<max; ++i) 
         { 
               	if(infos[i].fd == -1) 
               	{ 
                   	pinfo = &infos[i]; 
                   	break; 
               	} 
               	if(i == max-1) 
               	{ 
                   	sleep(1); 
                   	i--; 
               	} 
         } 
 
         int connfd = accept(sockfd, (struct sockaddr*)&pinfo->addr, &len); 
         printf("parent thread, connfd: %d\n", connfd); 
         if(connfd == -1) 
         { 
               	perror("accept"); 
               	exit(0); 
         } 
         pinfo->fd = connfd; 
         pthread_create(&pinfo->tid, NULL, Handle, pinfo); 
         pthread_detach(pinfo->tid); 
 	} 
 
 	close(sockfd); 
} 

客户端:

#include <stdio.h> 
 
#include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h> #include <unistd.h> #define PORT 3000 
 
 
int main() 
{ 
 	int sockfd; 
 	char buf[1024]; 
 	struct sockaddr_in srvaddr; 
 	sockfd=socket(AF_INET,SOCK_STREAM,0); 
 	if(sockfd==-1){ 
 	 	printf("can't create socket\n"); 
 	 	exit(1); 
 	} 
 	bzero(&srvaddr,sizeof(srvaddr)); 
 	srvaddr.sin_family=AF_INET; 
 	srvaddr.sin_port=htons(PORT); 
 	if(inet_aton("192.168.87.128",&srvaddr.sin_addr)==0){ 
 	 	printf("addr convert error\n"); 
 	 	close(sockfd); 
 	 	exit(1); 
 	} 
 	if(connect(sockfd,(struct sockaddr *)&srvaddr,sizeof(struct sockaddr))==-1){ 
 	 	printf("connect error\n"); 
 	 	close(sockfd); 
 	 	exit(1); 
 	} 
 	printf("登录:1     注册:2\n"); 
     bzero(buf,sizeof(buf)); 
 	scanf("%s", buf); 
 	write(sockfd, buf, strlen(buf)); 
 	if(strcmp(buf,"1")==0) 
 	{ 
 	 	char recvBuff[1024]; 
 	     int r; 
 	 	r=read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
 
 	 	scanf("%s", buf); 
 	 	write(sockfd, buf, strlen(buf)); 
 	 	r=read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
 	 	scanf("%s", buf); 
 	 	write(sockfd, buf, strlen(buf)); 
 	 	r=read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
 	 	    
 	} 	
 	if(strcmp(buf,"2")==0) 
 	{ 
 	 	char recvBuff[1024]; 
 	     int r; 
 	 	r = read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
 	 	scanf("%s", buf); 
 	 	write(sockfd, buf, strlen(buf)); 
 	 	r = read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
         if(strcmp(recvBuff,"该账号已注册")==0){ 
          	return 0; 

 	 	}  
 	 	r = read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
 	 	scanf("%s", buf); 
 	 	write(sockfd, buf, strlen(buf)); 
 	 	r = read(sockfd, recvBuff, 1023); 
 	 	recvBuff[r] = 0; 
 	 	printf("%s\n",recvBuff); 
 	 	return 0;  
 	} 	
 	printf("请输入要发送的对象和内容,按“#”退出登录\n"); 
 	 
 	char buff[1024]; 
 	char recvBuff[1024]; 
 	char rcv[1024]; 
 	int l = 0,h=0; 
 	if (!fork()) { 
 
 	 	while (1) { 
 	 	 	l = read(sockfd, recvBuff, 1023); 
 	 	 	recvBuff[l] = 0; 
 	 	 	printf("%s\n", recvBuff); 
 	 	 	h = read(sockfd, rcv, 1023); 
 	 	 	rcv[h] = 0; 
 	 	 	printf("%s\n", rcv); 
 	 	} 
 	} 	
 	 	
 	while (1) { 
 	 	 
 	 	bzero(buf,1024); 
 	 	bzero(buff, 1024); 
 	 	scanf("%s", buf); 
 	 	 
 	 	 
 	 	write(sockfd, buf, strlen(buf)); 
 	 	if (strcmp(buf, "#") == 0) { 
 	 	 	close(sockfd); 
 	 	 	exit(1); 
 	 	} 
 	 	scanf("%s", buff); 
 	 	write(sockfd, buff, strlen(buff)); 
 	 	 
 	 	 
 	} 
 	close(sockfd); 
 	return 0; 
} 

六、项目具体操作说明  

实现环境:VMware Workstation 16 Pro Ubuntu 18.04 LTS;

服务器文件:srv.c 客户端文件:clt.c 用户信息文件 user.txt;

首先进入客户端和服务器所在目录,使用命令 gcc -o srv srv.c -lpthread 和gcc -o clt clt.c 编译服务器和客户端文件,生成两个可执行文件 srv 和 clt; 输入./srv 和./clt 执行这两个文件,然后就可以开始聊天。

用户操作流程如下图:

实际操作过程截图:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值