Linux C 基于tcp多线程在线聊天室

多线程在线聊天室

概述

  客户端实现了判单用户登录结果、防止单回车字符发送、保存和显示历史聊天记录(仅自己)、退出聊天室功能。
  服务端实现了验证用户是否已经存在(支持最大64用户连接)支持广播用户进入退出聊天室以及用户聊天内容。

客户端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <time.h>

typedef struct sockaddr  SA;
typedef struct sockaddr_in  SIN;
#define MAXBACKLOG   100
int Socket(int domain,int type,int protocol);
int Connect(int sockfd,struct sockaddr * serv_addr,int addrlen);
void *son_fun(void * arg);
void save(const char * dbuff,const char * nbuff);
void list_history_msg(const char * nbuff);
int main(int argc,char *argv[])
{	
	char namebuff[512]={0};
	pthread_t id;
	//建立监听套接字
	int socketfd = Socket(AF_INET,SOCK_STREAM,0);
	//connect
	SIN   serverinfo;
	serverinfo.sin_family = AF_INET;
	serverinfo.sin_port   = htons(atoi(argv[2]));
	serverinfo.sin_addr.s_addr =  inet_addr(argv[1]);
	int addrlen = sizeof(SIN);
	Connect(socketfd,(SA*)&serverinfo,addrlen);
	//send name
	printf("请输入昵称:\n");
	gets(namebuff);
	write(socketfd,namebuff,sizeof(namebuff));
	char b[20];
	read(socketfd,b,sizeof(b));
	if(strstr(b,"已存在"))
	{
		printf("已存在\n");
		close(socketfd);
		return 0;		
	}
	
	//print serve info
	printf("登入成功,服务器:%s 端口:%d\n",inet_ntoa(serverinfo.sin_addr),ntohs(serverinfo.sin_port));
	//make son thread
	pthread_create(&id,NULL,son_fun,(void *)&socketfd);
	//w 
	while(1)
	{
		//time
		time_t t = time(NULL);
		struct tm *tinfo = localtime(&t);
		//msg
		char readbuff[512] = {0};
		gets(readbuff);
		//prevent "\n" send to serve and save to data
		if(strlen(readbuff)==0) continue;
		//determine wheather it is "ls"
		if(strcmp(readbuff,"ls") == 0) 
		{
			list_history_msg(namebuff);
			continue;
		}
 		//determine wheather it is "quit"
		if(strcmp(readbuff,"quit") == 0)
		{
			char sendbuff[618] = {0};
			sprintf(sendbuff,"%s : %s%s",namebuff,asctime(tinfo),"退出聊天");
			write(socketfd,sendbuff,sizeof(sendbuff));
			close(socketfd);
			exit(0);
		}
		//send
		char sendbuff[618] = {0};
		sprintf(sendbuff,"%s : %s%s",namebuff,asctime(tinfo),readbuff);
		write(socketfd,sendbuff,sizeof(sendbuff));
		//save data
		save(sendbuff,namebuff);
	}
	//关闭
	close(socketfd);
	return 0;
}
void list_history_msg(const char * nbuff)
{
	char path[128];
	char *line = NULL;
    size_t len = 0;
	
	sprintf(path,"./userdata/%s.txt",nbuff);
	FILE * fp = fopen(path,"a+");
	printf("********聊天记录*********\n");
	while(getline(&line , &len , fp) != -1)
		printf("%s",line);
	printf("************************\n");
	free(line);
	fclose(fp);
}
void save(const char * dbuff,const char * nbuff)
{
	char path[128];
	sprintf(path,"./userdata/%s.txt",nbuff);
	FILE * fp = fopen(path,"a+");
	fprintf(fp,"%s\n",dbuff);
	fclose(fp);
}

int Socket(int domain,int type,int protocol)
{
	int socketFd = socket(domain,type,protocol);
	if(socketFd == -1)
	{
		perror("socket");
		exit(1);
	}
	return socketFd;
}
int Connect (int sockfd,struct sockaddr * serv_addr,int addrlen)
{
	int val = connect(sockfd,serv_addr,addrlen);
	if(val == -1)
	{
		perror("connect");
		exit(1);
	}
	return 0;
}
void *son_fun(void * arg)
{
	int readpipefd = *((int *)arg);
	char readbuff[512]={0};
	while(1)
	{
		memset(readbuff,0,sizeof(readbuff));
		if(read(readpipefd,readbuff,sizeof(readbuff))>0)
		{
			printf("%s\n\n",readbuff);
		}
		else
		{
			close(readpipefd);
			pthread_exit(NULL);
		}
	}
}

服务端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>

typedef struct sockaddr  SA;
typedef struct sockaddr_in  SIN;
#define MAXBACKLOG   100

int Socket(int domain,int type,int protocol);
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen);
int Listen(int s,int backlog);
int Accept(int s,struct sockaddr * addr,int * addrlen);
void *son_fun(void * arg);
int is_exist(char * username);
void broadcast(char *r,char *n);

char Userlist[64][20] = {0};
int Userfdlist[64] = {0};

int main(int argc,char *argv[])
{	
	//建立监听套接字
	int socketfd = Socket(AF_INET,SOCK_STREAM,0);
	//需要进行重用地址及其端口号
	int  opt = 1;
	setsockopt(socketfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	//绑定信息编写服务器信息
	SIN   serverinfo;
	serverinfo.sin_family = AF_INET;		//协议IPV4
	serverinfo.sin_port   = htons(atoi(argv[2]));	//网络字节序(大端字节序)与主机字节序(小端字节序)  
	serverinfo.sin_addr.s_addr =  inet_addr(argv[1]);
	int addrlen = sizeof(SIN);
	Bind(socketfd,(SA*)&serverinfo,addrlen);
	//监听
	Listen(socketfd,MAXBACKLOG);
	//读写
	while(1)
	{
		//等待连接
		SIN clientinfo;
		int  clientaddrlen = sizeof(SA);
		int newfd = Accept(socketfd,(SA*)&clientinfo,&clientaddrlen);
		printf("客户端地址:%s 端口号:%d\n",inet_ntoa(clientinfo.sin_addr),ntohs(clientinfo.sin_port));
		//创建子线程
		pthread_t id;
		pthread_create(&id,NULL,son_fun,(void *)&newfd);
	}
	//关闭
	close(socketfd);
	return 0;
}
int is_exist(char * username)
{
	for(int i = 0 ; i < 10; i++)
	{
		if(strcmp(username,Userlist[i]) == 0)
			return 1;
	}
	return 0;
}
void *son_fun(void * arg)
{
	int readfd = *((int *)arg);
	char readbuff[512] = {0};
	char namebuff[ 20] = {0};
	read(readfd,namebuff,sizeof(namebuff));
	//determine wherther it is exist
	if(is_exist(namebuff))
	{write(readfd,"已存在",sizeof("已存在"));close(readfd);pthread_exit(NULL);}
	else
	{
		write(readfd,"登录成功",sizeof("登录成功"));
		char r[50];
		sprintf(r,"%s %s",namebuff,"进入聊天室");
		printf("%s\n",r);
		broadcast(r,namebuff);
	}
	//save username and userfd
	for(int i=0;i<10;i++)
	{
		if(strlen(Userlist[i])==0)
		{
			strcpy(Userlist[i],namebuff);
			Userfdlist[i] = readfd;
			break;
		}
	}
	while(1)
	{
		memset(readbuff,0,sizeof(readbuff));
		if(read(readfd,readbuff,sizeof(readbuff))>0)
		{
			if(strlen(readbuff)>0)
			{
				//printf("%s\n\n",readbuff);
				//broadcast
				broadcast(readbuff,namebuff);
				if(strstr(readbuff,"退出聊天"))
					for(int i=0;i<10;i++)
						if(strcmp(Userlist[i],namebuff)==0)
						{
							printf("%s\n",readbuff);
							strcpy(Userlist[i],"\0");
							close(readfd);
							pthread_exit(NULL);
						}
			}
		}
		else
		{
			close(readfd);
			pthread_exit(NULL);
		}
	}
}
void broadcast(char *r,char *n)
{
	for(int i=0 ; i<10 ;i++)
		//if it is a user and not himself
		if(strcmp(Userlist[i],n)!=0 && strlen(Userlist[i])!=0)
			write(Userfdlist[i],r,strlen(r));
}
int Socket(int domain,int type,int protocol)
{
	int socketFd = socket(domain,type,protocol);
	if(socketFd ==-1)
	{
		perror("socket");
		exit(1);
	}
	return socketFd;
}
int Bind(int sockfd,struct sockaddr * my_addr,int addrlen)
{
	int val = bind(sockfd,my_addr,addrlen);
	if(val)
	{
		perror("bind");
		exit(1);
	}
	return 0;
}
int Listen(int s,int backlog)
{
	int val = listen(s,backlog);
	if(val == -1)
	{
		perror("listen");
		exit(1);
	}
	return val;
}
int Accept(int s,struct sockaddr * addr,int * addrlen)
{
	int NEWfd = accept(s,addr,addrlen);
	if(NEWfd == -1)
	{
		perror("listen");
		exit(1);
	}
	return NEWfd;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值