欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux Socket编程---TCP实现多客户端的网络聊天室

程序员文章站 2022-03-15 20:34:47
...

本程序实现的功能:

基于linux的网络聊天室

服务器端功能:

1)能够实现同时监听10个客户端

2)新的客户进入聊天室,发送新客户进入的系统消息给所有在线客户

3)在线客户实现基本的群聊功能

4)保存聊天记录,并支持查询聊天记录

5)可以通过终端查看客户之间的聊天状况

客户端功能:

1)能过正常连接服务器

2)通过多线程实现同时进行收发消息

3)保存客户本地聊天记录

4)可以查询本地聊天记录

 

tcpserver.c

#include<stdio.h> 
#include<string.h> 
#include<stdlib.h> 
#include<unistd.h> 
#include<arpa/inet.h> 
#include<sys/socket.h> 
#include<netinet/in.h> 
#include<unistd.h> 
#include<pthread.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>

//给线程传输的信息
struct threadinfo
{	
	//socket
	int my_sock;
	//随机分配的端口号
	int my_port; 
};

//用户数量
int usernum = 0;
//socket数组
int sock_array[10];
//打开文件
int fd;

//发送线程
void *recvsocket(void *arg)
{
	//接受结构体
	struct threadinfo *thread = arg;

	int st = thread->my_sock;
	int port = thread->my_port;
	char receivebuffer[100]; 
	int i;
	int n;
	int length = sizeof(sock_array)/sizeof(sock_array[0]);
	char sendbuffer[100];
	char writebuffer[100];
	
	while(1){
		//接受客户端传回的数据 
		memset(receivebuffer, 0, sizeof(receivebuffer)); 
		n = recv(st, receivebuffer, sizeof(receivebuffer), 0);
		//判断通信是否结束 
		if(n <= 0)
			break;
		printf("来自%d的数据:%s\n", port,receivebuffer);

		//将接受到的聊天记录保存到文件中
		memset(writebuffer, 0, sizeof(writebuffer));
		sprintf(writebuffer,"%d",port);
		strcat(writebuffer,":");
		strcat(writebuffer,receivebuffer);
		strcat(writebuffer,"\n");
		write(fd,writebuffer,sizeof(writebuffer));

		//将接受到的消息发送给出自己外的其他人
		memset(sendbuffer, 0, sizeof(sendbuffer));
		sprintf(sendbuffer,"%d",port);
		strcat(sendbuffer,"说:");
		strcat(sendbuffer,receivebuffer);
		for(i = 0; i<length; i++)
		{
			if(sock_array[i] != st)
			{
				send(sock_array[i], sendbuffer, strlen(sendbuffer), 0);
			}
			
		} 
	}
	return NULL;
}

int main() { 

	//创建套接字 
	int serv_sock = socket(AF_INET, SOCK_STREAM, 0); 
	//将套接字和IP、端口绑定 
	struct sockaddr_in serv_addr; 
	memset(&serv_addr, 0, sizeof(serv_addr));//每个字节都用0填充 
	serv_addr.sin_family = AF_INET; 
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
	serv_addr.sin_port = htons(6666); 

	//服务器绑定信息
	if(bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) { 
		perror("bind failed!"); 
		exit(1); 
	} 

	//进入监听状态,等待用户发起请求 
	printf("监听6666端口...\n"); 

	//创建新的sockaddr_in结构体用于accept
	struct sockaddr_in client_addr; 
	//声明线程数组
	pthread_t thrd[10];
	int client_sock;
	int len = sizeof(client_addr);
	//声明一个结构体,放入下面的数组中
	struct threadinfo my_thread;
	//声明一个结构体数组
	struct threadinfo thread_array[10];
	
	int i;
	char portbuffer[100]; 

	//开始监听,监听10个用户
	listen(serv_sock, 10);
	//pthread_create(&thrd[11], NULL, sendsocket, &sock_array);
	//打开文件
	fd = open("./note.txt", O_CREAT|O_WRONLY|O_APPEND);

	while(1)
	{
		//printf("123");
		
		//accept()接收返回值为客户端的新socket,原来的socket用户继续监听端口 
		client_sock = accept(serv_sock, (struct sockaddr*)&client_addr, &len); 
		if(client_sock < 0) { 
			perror("connect failed!"); 
			exit(1); 
		} 
		printf("欢迎%d用户进入聊天室!\n", client_addr.sin_port);

		//向所有用户发送进入信息
		memset(portbuffer, 0, sizeof(portbuffer));
		sprintf(portbuffer,"%d",client_addr.sin_port);
		strcat(portbuffer,"用户进入聊天室!");
		for(i = 0; i<usernum; i++)
		{
			send(sock_array[i], portbuffer, strlen(portbuffer), 0);
		}
		//username[usernum] = client_addr.sin_port;
		
		//将socket和port放入结构体中   再将这个结构体放入的结构体数组中
		my_thread.my_sock = client_sock;
		my_thread.my_port = client_addr.sin_port;
		thread_array[usernum] = my_thread;
		
		//socket数组
		sock_array[usernum] = client_sock;

		//创建发送线程
		pthread_create(&thrd[usernum], NULL, recvsocket, &thread_array[usernum]);

		//用户数量自增
		usernum++;
			
	}

	//关闭文件
	close(fd);
	//关闭套接字 
	close(client_sock);
	close(serv_sock); 

	return 0; 

} 

tcpclient.c

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


//打开文件
int fd;
//客户端socket
int client_sock;
//文件路径
char path[100];
//段口号
char port[100];

//发送线程
void *sendsocket(void *arg)
{
	//接受socket
	int st = *(int *)arg;
	char sendbuffer[100]; 
	char writebuffer[100];
	while(1){
		//向服务器发送数据 
		memset(sendbuffer, 0, sizeof(sendbuffer)); 
		//printf("请输入消息:");
		scanf("%s",sendbuffer);
		memset(writebuffer, 0, sizeof(writebuffer)); 
		strcat(writebuffer,sendbuffer);
		strcat(writebuffer,"\n");
		//写入文件
		write(fd,writebuffer,sizeof(writebuffer));
		//发送消息
		send(st, sendbuffer, strlen(sendbuffer), 0); 
	}
	return NULL;
}

//接受线程
void *recvsocket(void *arg)
{
	int st = *(int *)arg;
	char receivebuffer[100]; 
	char writebuffer[100];
	int n; 
	while(1){
		//读取服务器传回的数据 
		memset(receivebuffer, 0, sizeof(receivebuffer));
		n = recv(st, receivebuffer, sizeof(receivebuffer), 0);  
		//用于判断通信是否结束 
		if(n<=0)
			break;		
		memset(writebuffer, 0, sizeof(writebuffer)); 
		strcat(writebuffer,receivebuffer);
		strcat(writebuffer,"\n");
		//写入文件
		write(fd,writebuffer,sizeof(writebuffer));
		//输出
		printf("%s\n", receivebuffer);
		
		
	}
	return NULL;
}

int main() { 

	//创建套接字 
	int sock = socket(AF_INET, SOCK_STREAM, 0); 
	//向服务器(特定的IP和端口)发起请求 
	struct sockaddr_in serv_addr; 
	memset(&serv_addr, 0, sizeof(serv_addr));//每个字节都用0填充 
	serv_addr.sin_family = AF_INET; 
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 
	serv_addr.sin_port = htons(6666); 

	//连接服务器,成功返回0 
	client_sock = connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
	if(client_sock == 0) { 
		printf("服务器连接成功!\n"); 
	} 
	
	//新建一个结构体
	struct sockaddr_in client_addr; 
	memset(&client_addr, 0, sizeof(client_addr));

	int len = sizeof(client_addr);
	//获得本程序的sockaddr_in结构体
	int ti = getsockname(sock, (struct sockaddr*)&client_addr, &len);

	//对字符串拼接得到路径
	sprintf(port,"%d",client_addr.sin_port);
	strcat(path,"./usernote/");
	strcat(path,port);
	//打开文件
	fd = open(path, O_CREAT|O_EXCL|O_WRONLY|O_APPEND|O_NONBLOCK);
	if(fd == -1)
	{
		printf("失败!");
	}

	//创建发送接受两个线程  一个接受线程一个发送线程
	pthread_t thrd1, thrd2;
	pthread_create(&thrd1, NULL, sendsocket, &sock);
	pthread_create(&thrd2, NULL, recvsocket, &sock);
	pthread_join(thrd1, NULL);
	pthread_join(thrd2, NULL);

	//关闭文件
	close(fd);
	//关闭套接字 
	close(sock); 

	return 0; 

}