Linux 嵌入式 C 实现简单的网络聊天室客户端

Linux 嵌入式 C 实现简单的网络聊天室

版权声明: https://blog.csdn.net/qq_39603089/article/details/85080521

简单功能如下

  1. 服务器端接收用户信息,处理后转发给其他用户,如有用户登录或退出,服务器通知所有人;
  2. 群聊:用户发送的信息所有人都可以接收,接收的信息前面显示发送者的昵称和发送时间;
  3. 私聊:可以选定用户发送信息,其他用户看不到,该用户也使用相同的方式回复私聊信息;
  4. 保存和查看聊天记录,仅可查看自己保存的聊天记录,保存和读取聊天记录时需要使用文件锁; 保存和查看聊天记录,聊天记录保存在“./msgsave_昵称”文件中(“昵称”为保存者自己的昵称);
  5. 服务器的出错信息打印输出到屏幕上,同时发送给系统(/var/log/messages)。

使用C/S 模式,参考下图:
Linux 嵌入式 C 实现简单的网络聊天室客户端_第1张图片

源代码下载链接 与下方所示代码有所改进,添加与完善部分功能
https://blog.csdn.net/qq_39603089/article/details/85328262

先看客户端代码:

/*---------------------------------客户client.c------------------------------------
	time: 2018/12/17
	author:zyb-dy
*/
#include 	// 头文件
#include 
#include 
#include  //定义数据结构sockaddr_in
#include  //定义socket函数以及数据结构
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include  
#include 
GtkWidget *window;   //登录窗口
GtkWidget *home;	//主窗口
int clientfd,b_file;
struct sockaddr_in clientaddr; 
char user_name[50];
char fname[]="/var/tmp/";
//处理登录
void deal_pressed(GtkWidget *button, gpointer entry){
	int sendbytes;
	char *buff;
	struct hostent *host;
	char wel[]="Welcome";
	host = gethostbyname("127.0.0.1");   //本地地址
	buff = (char *)malloc(9);
	
	const gchar  *text = gtk_entry_get_text(GTK_ENTRY(entry));
	if(strlen(text)==0){
		printf("不能为空\n");         // 提示 不能为空
	}
	else{	
		if ((clientfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
		{
			perror("fail to create socket");
			exit(1);
		}
		bzero(&clientaddr, sizeof(clientaddr));
		clientaddr.sin_family = AF_INET;
		clientaddr.sin_port = htons((uint16_t)atoi("8787"));
		clientaddr.sin_addr = *((struct in_addr *)host->h_addr);
	
		if (connect(clientfd, (struct sockaddr *)&clientaddr, sizeof(struct sockaddr)) == -1)
		{
			perror("fail to connect");
			exit(1);
		}
		if ((sendbytes = send(clientfd, text, strlen(text), 0)) == -1)
		    {
			perror("fail to send");
			exit(1);
		    }

		if (recv(clientfd, buff, 7, 0) == -1)
		{
			perror("fail to recv");
			exit(1);
		}
		if(strcmp(buff,wel)==0){
			strcpy(user_name,text);
			gtk_widget_destroy(window);		
		}else{
			//  弹窗 提醒 提示 昵称重复
			GtkWidget *dialog;
			dialog = gtk_message_dialog_new((gpointer)window,
			GTK_DIALOG_DESTROY_WITH_PARENT,
			GTK_MESSAGE_ERROR,
			GTK_BUTTONS_OK,
			"昵称重复,拒绝登录");
			gtk_window_set_title(GTK_WINDOW(dialog), "拒绝");
			gtk_dialog_run(GTK_DIALOG(dialog));
			gtk_widget_destroy(dialog);	
			close(clientfd);
		}
	}
}
//登录界面
void login(int argc,char *argv[]){
	// 初始化
	gtk_init(&argc, &argv);		
	// 创建顶层窗口
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	// 设置窗口的标题
	gtk_window_set_title(GTK_WINDOW(window), "登录");
	// 设置窗口在显示器中的位置为居中
	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
	// 设置窗口的最小大小
	gtk_widget_set_size_request(window, 300, 200);
	// 固定窗口的大小
	gtk_window_set_resizable(GTK_WINDOW(window), FALSE); 
	// "destroy" 和 gtk_main_quit 连接
	g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	//创建一个固定容器
 	GtkWidget *fixed = gtk_fixed_new(); 	
	// 将布局容器放窗口中
	gtk_container_add(GTK_CONTAINER (window), fixed);
	// 创建标签
	GtkWidget *label_one = gtk_label_new("请输入昵称");
	// 将按钮放在布局容器里
	gtk_fixed_put(GTK_FIXED(fixed), label_one,120,30);
	// 行编辑的创建
	GtkWidget *entry = gtk_entry_new();
	//设置最大长度
	gtk_entry_set_max_length(GTK_ENTRY(entry),50);
	// 设置行编辑允许编辑
	gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);

	gtk_fixed_put(GTK_FIXED(fixed), entry,70,60); 
	// 创建按钮
	GtkWidget *button = gtk_button_new_with_label("  登录  "); 
    
	gtk_fixed_put(GTK_FIXED(fixed), button,130,110);
	//绑定点击事件
	g_signal_connect(button, "pressed", G_CALLBACK(deal_pressed), entry);  
	// 显示窗口全部控件
	gtk_widget_show_all(window);
	//启动主循环
 	gtk_main();	
}
//发送目标用户窗口
GtkWidget *entryname;
//发送信息
void sendtouser(GtkButton  *button, gpointer entry){
	char *buf;
	buf = (char *)malloc(1024);
	memset(buf, 0, 1024);	
	int sendbytes;
	const gchar  *text = gtk_entry_get_text(GTK_ENTRY(entry));
	const char *but = gtk_button_get_label(button);
	if(strlen(text)==0){
		printf("不能为空\n");                               //发送内容不能为空
	}else{
		if(strcmp(but,"--发送--")==0){
			const gchar  *name = gtk_entry_get_text(GTK_ENTRY(entryname));
			if(strlen(name)==0){
					printf("name为空。\n");
					return;                                  //目标用户不能为空
			}
			sprintf(buf,"%s%s%s%s\n","User:",name,":",text);   //发送个单一用户‘User:’为标记
			if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1)
			{
				perror("fail to send");          
			}
			return ;		
		}else{
			sprintf(buf,"%s%s\n","All::",text);				//群发‘All:’为标记
			if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1)
			{
				perror("fail to send");
			}
			return ;
		}
	}
}
void savetxt(GtkButton  *button, gpointer entry){
	struct flock lock;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_type = F_WRLCK;    //初始化l_type
	lock.l_pid = -1;    //初始化l_pid
	int src_file, dest_file;
	unsigned char buff[1024];
	int real_read_len;
	char txt_name[60];
	sprintf(txt_name,"%s%s","./msgsave_",user_name);
	src_file = open(fname, O_RDONLY);
	dest_file = open(txt_name,O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (src_file< 0 || dest_file< 0)
	{
		return;
	}	
	fcntl(dest_file, F_SETLKW, &lock);
	while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)
	{
		write(dest_file, buff, real_read_len);
	}
	fcntl(dest_file, F_UNLCK, &lock);
	close(dest_file);
	close(src_file);

}
// 文本框缓冲区
GtkTextBuffer *bufferuser;
GtkTextBuffer *buffernotice;
void *strdeal(void *arg){  //处理接受到的字符并调用相应的函数转发;
	char sign[10];
	char buf[1024];
	char s[1024];
	struct flock lock;
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;
	lock.l_type = F_WRLCK;    //初始化l_type
	lock.l_pid = -2;    //初始化l_pid
	while(1){
		memset(s, 0, strlen(s));
		memset(sign, 0, strlen(sign));
		memset(buf, 0, strlen(buf));
		if(recv(clientfd, s, 1024, 0) <= 0)
		    {
			perror("fail to recv");
			close(clientfd);
			exit(1);
		    }
		int i=0;
		int n=0;
		int j=0;
		for(i;i<strlen(s);i++){
			if(n==1){
				buf[j]=s[i];
				j++;
			}else{
				if(s[i]==':'){
					n++;
					sign[j]='\0';
					j=0;
					continue;				
				}
				sign[j]=s[i];
				j++;
			}	
		}
		if(strcmp(sign,"User")==0){    //判断是用户消息还是系统消息
			b_file = open(fname,O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
			fcntl(b_file, F_SETLKW, &lock);	   //文件锁 临时储存消息记录		
			write(b_file, buf, strlen(buf));	//可以选择使用共享内存
			fcntl(b_file, F_UNLCK, &lock);
			close(b_file);
			GtkTextIter start,end;
			gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(bufferuser),&start,&end);
			gtk_text_buffer_insert(GTK_TEXT_BUFFER(bufferuser),&start,buf,strlen(buf));
		}else{	
			GtkTextIter start,end;
			gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffernotice),&start,&end);
			if(strcmp(buf,"over")==0){   // 收到over说明服务停止,所以程序退出  
				strcpy(buf,"服务停止,程序即将退出\n");   
      gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffernotice),&start,buf,strlen(buf));
				sleep(2);
                close(clientfd);
                unlink(fname); 
				exit(0);
			}
	gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffernotice),&start,buf,strlen(buf));
		}	
	}	
}
//主界面
void homepage(int argc,char *argv[]){
	char *buf;
	buf = (char *)malloc(1024);
	memset(buf, 0, 1024);
	pid_t pid;
	// 初始化
    gtk_init(&argc, &argv);		
	// 创建顶层窗口
	home = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	// 设置窗口的标题
	gtk_window_set_title(GTK_WINDOW(home), "欢迎");
	// 设置窗口在显示器中的位置为居中
	gtk_window_set_position(GTK_WINDOW(home), GTK_WIN_POS_CENTER);
	// 设置窗口的最小大小
	gtk_widget_set_size_request(home, 500, 400);
	// 固定窗口的大小
	gtk_window_set_resizable(GTK_WINDOW(home), FALSE); 
	// "destroy" 和 gtk_main_quit 连接
	g_signal_connect(home, "destroy", G_CALLBACK(gtk_main_quit), NULL);
	//创建一个固定容器
 	GtkWidget *fixed = gtk_fixed_new(); 	
	gtk_container_add(GTK_CONTAINER(home), fixed);

	GtkWidget *label_two;
	GtkWidget *label_one;
	// 创建标签
	label_one = gtk_label_new("聊天内容:");
	// 将按钮放在布局容器里
	gtk_fixed_put(GTK_FIXED(fixed), label_one,20,10);	

	// 创建标签
	label_two = gtk_label_new("系统通知:");
	gtk_fixed_put(GTK_FIXED(fixed), label_two,320,10);

	label_two = gtk_label_new("发送用户:");
	gtk_fixed_put(GTK_FIXED(fixed), label_two,24,295);
	
	label_two = gtk_label_new("发送内容:");
	gtk_fixed_put(GTK_FIXED(fixed), label_two,24,335);


	// 行编辑的创建
	entryname = gtk_entry_new();
	// 最大长度
	gtk_entry_set_max_length(GTK_ENTRY(entryname),500);
	// 设置行编辑不允许编辑,只做显示用
	gtk_editable_set_editable(GTK_EDITABLE(entryname), TRUE);
	gtk_fixed_put(GTK_FIXED(fixed), entryname,90,290); 

	GtkWidget *entry = gtk_entry_new();		
	gtk_entry_set_max_length(GTK_ENTRY(entry),500);
	gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);
	gtk_fixed_put(GTK_FIXED(fixed), entry,90,330);	
	// 创建按钮
	GtkWidget *bsend = gtk_button_new_with_label("--发送--");
	gtk_fixed_put(GTK_FIXED(fixed), bsend,325,300);

	GtkWidget *send_all = gtk_button_new_with_label("--群发--");
	gtk_fixed_put(GTK_FIXED(fixed), send_all,390,300);
	
	GtkWidget *save = gtk_button_new_with_label("--保存记录--");
	gtk_fixed_put(GTK_FIXED(fixed), save,345,340);	
	// 绑定回调函数
	g_signal_connect(bsend, "pressed", G_CALLBACK(sendtouser), entry);
	g_signal_connect(send_all, "pressed", G_CALLBACK(sendtouser), entry);
	g_signal_connect(save, "pressed", G_CALLBACK(savetxt), entry);

	// 文本框
	GtkWidget *view = gtk_text_view_new(); 
	gtk_widget_set_size_request (view, 280, 250);
	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(view), FALSE);
	gtk_fixed_put(GTK_FIXED(fixed), view, 20, 30);
	// 获取文本缓冲区
	bufferuser=gtk_text_view_get_buffer(GTK_TEXT_VIEW(view));     	
	

	GtkWidget *name_view = gtk_text_view_new(); 
	gtk_widget_set_size_request (name_view, 150, 230);
	gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(name_view), FALSE);
  	gtk_fixed_put(GTK_FIXED(fixed), name_view, 320, 30);
	buffernotice=gtk_text_view_get_buffer(GTK_TEXT_VIEW(name_view));
	// 显示窗口全部控件
	gtk_widget_show_all(home);	
	
	int sendbytes, res;
	pthread_t thread;
	// 开启线程监听收到的数据
	res = pthread_create(&thread, NULL, strdeal, NULL);
	if (res != 0)
	{          
		exit(res);
	}
	usleep(10);
	gtk_main();
}

//主函数
int main(int argc,char *argv[])
{	
	uuid_t uuid;
	char str[36];
	uuid_generate(uuid);
	uuid_unparse(uuid, str);
	strcat(fname,str);
	
	login(argc,argv);
	homepage(argc,argv);
	
	unlink(fname);   //删除临时储存消息记录文件
	close(clientfd);
    return 0;
}

编译:

gcc chat_client.c -o clent -luuid -lpthread `pkg-config --cflags --libs gtk+-2.0`
  1. libuuid是一个用于生成UUID的C库,具体用法参考http://linux.die.net/man/3/libuuid,在Ubuntu中,可以用下面的命令安装libuuid:

    sudo apt-get install uuid-dev

  2. -lpthread 线程

  3. pkg-config 是一个为已经安装的包提供了include,以及实际库安装的位置编译选项的输出和管理的工具;
    –cflags 选项作用为自动获得预处理参数,如宏定义,头文件的位置;
    –libs 选项作用为自动获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数;gtk±2.0 选项作用为指定GTK版本。

    原文:https://blog.csdn.net/tennysonsky/article/details/42708085

部分运行效果图如下:

Linux 嵌入式 C 实现简单的网络聊天室客户端_第2张图片
Linux 嵌入式 C 实现简单的网络聊天室客户端_第3张图片
Linux 嵌入式 C 实现简单的网络聊天室客户端_第4张图片

服务器端请看:https://blog.csdn.net/qq_39603089/article/details/85080802

你可能感兴趣的:(嵌入式,GTK+,嵌入式Linux,聊天软件,C语言,C/S)