版权声明: https://blog.csdn.net/qq_39603089/article/details/85080521
简单功能如下
源代码下载链接 与下方所示代码有所改进,添加与完善部分功能
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`
libuuid是一个用于生成UUID的C库,具体用法参考http://linux.die.net/man/3/libuuid,在Ubuntu中,可以用下面的命令安装libuuid:
sudo apt-get install uuid-dev
-lpthread 线程
pkg-config 是一个为已经安装的包提供了include,以及实际库安装的位置编译选项的输出和管理的工具;
–cflags 选项作用为自动获得预处理参数,如宏定义,头文件的位置;
–libs 选项作用为自动获得链接参数,如库及依赖的其它库的位置,文件名及其它一些连接参数;gtk±2.0 选项作用为指定GTK版本。原文:https://blog.csdn.net/tennysonsky/article/details/42708085
服务器端请看:https://blog.csdn.net/qq_39603089/article/details/85080802