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

2019-07-12 20:52发布

class="markdown_views prism-kimbie-light">

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

版权声明: https://blog.csdn.net/qq_39603089/article/details/85080521 简单功能如下
  1. 服务器端接收用户信息,处理后转发给其他用户,如有用户登录或退出,服务器通知所有人;
  2. 群聊:用户发送的信息所有人都可以接收,接收的信息前面显示发送者的昵称和发送时间;
  3. 私聊:可以选定用户发送信息,其他用户看不到,该用户也使用相同的方式回复私聊信息;
  4. 保存和查看聊天记录,仅可查看自己保存的聊天记录,保存和读取聊天记录时需要使用文件锁; 保存和查看聊天记录,聊天记录保存在“./msgsave_昵称”文件中(“昵称”为保存者自己的昵称);
  5. 服务器的出错信息打印输出到屏幕上,同时发送给系统(/var/log/messages)。
使用C/S 模式,参考下图:
图片来自网络 源代码下载链接 与下方所示代码有所改进,添加与完善部分功能
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("不能为空 "); // 提示 不能为空 } 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("不能为空 "); //发送内容不能为空 }else{ if(strcmp(but,"--发送--")==0){ const gchar *name = gtk_entry_get_text(GTK_ENTRY(entryname)); if(strlen(name)==0){ printf("name为空。 "); return; //目标用户不能为空 } sprintf(buf,"%s%s%s%s ","User:",name,":",text); //发送个单一用户‘User:’为标记 if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1) { perror("fail to send"); } return ; }else{ sprintf(buf,"%s%s ","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]='