提供给识字的人学习。
#include <gtk/gtk.h> // 头文件
#include <string.h>
#include <stdio.h>
#include <netinet/in.h> //定义数据结构sockaddr_in
#include <sys/socket.h> //定义socket函数以及数据结构
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <netdb.h>
#include<sys/sem.h>
#include <unistd.h>
#include <pthread.h>
#include <uuid/uuid.h>
GtkWidget *window; //登录窗口
GtkWidget *home; //主窗口
int clientfd,b_file;
struct sockaddr_in clientaddr;
char user_name[50];
char fname[]=\"/var/tmp/\";
int sem_id;
//↓↓↓↓↓↓↓↓以下为实验8讲稿内sem_com.c定义信息量操作的代码↓↓↓↓↓↓↓↓↓↓↓
//初始化信号量。
int init_sem(int sem_id, int init_value){
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
union semun sem_union;
sem_union.val = init_value;
if(semctl(sem_id, 0, SETVAL, sem_union) == -1){
perror(\"Initialize semaphore\"); //把\"Initialize semaphore\"输出到标准错误stderr。
return -1;
}
return 0;
}
//删除信号量。
int del_sem(int sem_id){
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
union semun sem_union;
if(semctl(sem_id, 1, IPC_RMID, sem_union)==-1){
perror(\"Delete semaphore\"); //把\"Delete semaphore\"输出到标准错误stderr。
return -1;
}
}
//p操作函数。
int sem_p(int sem_id){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1)==-1){
perror(\"P operation\"); //把\"P operation\"输出到标准错误stderr。
return -1;
}
return 0;
}
//v操作函数。
int sem_v(int sem_id){
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
if(semop(sem_id, &sem_b, 1) == -1){
perror(\"V operation\"); //把\"V operation\"输出到标准错误stderr。
return -1;
}
return 0;
}
//↑↑↑↑↑↑↑↑↑↑↑以上为实验8讲稿内sem_com.c定义信息量操作的代码↑↑↑↑↑↑↑↑↑↑↑
//处理登录
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); //向系统申请分配指定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\"); //把\"fail to create socket\"输出到标准错误stderr。
exit(1);
}
bzero(&clientaddr, sizeof(clientaddr)); //置clientaddr的前sizeof(clientaddr)个字节为零且包括‘\\0’。
clientaddr.sin_family = AF_INET; //服务器地址族为对于TCP/IP协议的AF_INET。
clientaddr.sin_port = htons((uint16_t)atoi(\"8787\")); //服务器端口将小端模式改变为大端模式。
clientaddr.sin_addr = *((struct in_addr *)host->h_addr); //服务器地址为0.0.0.0,即任意地址。
if (connect(clientfd, (struct sockaddr *)&clientaddr, sizeof(struct sockaddr)) == -1)
{
perror(\"fail to connect\"); //把\"fail to connect\"输出到标准错误stderr。
exit(1);
}
if ((sendbytes = send(clientfd, text, strlen(text), 0)) == -1)
{
perror(\"fail to send\"); //把\"fail to send\"输出到标准错误stderr。
exit(1);
}
if (recv(clientfd, buff, 7, 0) == -1)
{
perror(\"fail to recv\"); //把\"fail to recv\"输出到标准错误stderr。
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_ (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_ (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;
// 文本框缓冲区。
GtkTextBuffer *bufferuser;
GtkTextBuffer *buffernotice;
//保存信息记录buf。
void savelinetxt(char buf[]){
struct flock lock3; //新建结构体flock为lock3。
lock3.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。
lock3.l_start = 0; //相对位移量。
lock3.l_len = 0; //设置加锁区域的长度。
//以上三个设置可以为整个文件加锁。
lock3.l_type = F_WRLCK; //初始化l_type为写入锁。
lock3.l_pid = -2; //初始化l_pid。
b_file = open(fname,O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); //打开fname,并将文件描述符存在b_file。
fcntl(b_file, F_SETLKW, &lock3); //以阻塞的方式为b_file设置结构为lock3的文件锁。
write(b_file, buf, strlen(buf)); //向b_file写入buf。
fcntl(b_file, F_UNLCK, &lock3); //给b_file解锁。
close(b_file); //关闭文件描述符b_file。
}
//根据button的值发送消息时的自我维护。
void sendtouser(GtkButton *button, gpointer entry){
char *buf; //用来存储内容。
buf = (char *)malloc(1024); //向系统申请分配指定1024个字节的内存空间。
memset(buf, 0, 1024); //将buf中当前位置后面的1024个字节用0替换。
int sendbytes; //用来记录发送的字节数。
const gchar *text = gtk_entry_get_text(GTK_ENTRY(entry)); //获得行编辑entry的内容并静态建立text指针进行指定。
const char *but = gtk_button_get_label(button); //获得获取按钮button文本内容并静态建立but指针进行指定。
if(strlen(text)==0){ //如果text的长度为0
printf(\"不能为空\\n\"); //打印内容。
return;
}else{
if(strcmp(but,\"--发送--\")==0){ //比较but与\"--发送--\",若相同返回0;若相同。
const gchar *name = gtk_entry_get_text(GTK_ENTRY(entryname)); //获得行编辑entryname的内容并静态建立name指针进行指定。
if(strlen(name)==0){ //如果name的长度为0
printf(\"name为空。\\n\"); //打印内容。
return;
}
sprintf(buf,\"%s%s%s%s\\n\",\"User:\",name,\":\",text); //将内容写入buf。
if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1) //将buf由指定的socket端口clientfd传给对方主机并将发送的字节数存进sendbytes;如果发送失败。
{
perror(\"fail to send\"); //把\"fail to send\"输出到标准错误stderr。
}
memset(buf, 0, 1024); //将buf中当前位置后面的1024个字节用0替换。
sprintf(buf,\"你对< %s >说:%s\\n\",name,text); //将内容写入buf。
savelinetxt(buf); //保存信息记录buf。
GtkTextIter start,end; //新建保存文字在buffer中位置的结构start和end。
sem_p(sem_id); //信号量减1。
gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(bufferuser),&start,&end); //得到当前buffer中开始位置,结束位置的ITER。
gtk_text_buffer_insert(GTK_TEXT_BUFFER(bufferuser),&start,buf,strlen(buf));
//向文本框插入文字。start,end分别为文本框文字开始位置和结束位置的iter,插入文本的长度为strlen(buf)。
sem_v(sem_id); //信号量加1。
return ;
}else{
sprintf(buf,\"%s%s\\n\",\"All::\",text); //将内容写入buf。
if ((sendbytes = send(clientfd, buf, strlen(buf), 0)) == -1) //将buf由指定的socket端口clientfd传给对方主机并将发送的字节数存进sendbytes;如果发送失败。
{
perror(\"fail to send\"); //把\"fail to send\"输出到标准错误 stderr。
}
sprintf(buf,\"你对所有人说:%s\\n\",text); //将内容写入buf。
savelinetxt(buf); //保存信息记录buf。
GtkTextIter start,end; //新建保存文字在buffer中位置的结构start和end。
sem_p(sem_id); //信号量减1。
gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(bufferuser),&start,&end); //得到当前buffer中开始位置,结束位置的ITER。
gtk_text_buffer_insert(GTK_TEXT_BUFFER(bufferuser),&start,buf,strlen(buf)); //向文本框插入文字。start,end分别为文本框文字开始位置和结束位置的iter,插入文本的长度为strlen(buf)。
sem_v(sem_id); //信号量加1。
return ;
}
}
}
//
void savetxt(GtkButton *button, gpointer entry){
struct flock lock1; //新建结构体flock为lock1。
lock1.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。
lock1.l_start = 0; //设置位移量。
lock1.l_len = 0; //设置加锁区域的长度。
//以上三个设置可以为整个文件加锁。
lock1.l_type = F_WRLCK; //初始化l_type为写入锁。
lock1.l_pid = -1; //初始化l_pid。
struct flock lock2; //新建结构体flock为lock2。
lock2.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。
lock2.l_start = 0; //设置位移量。
lock2.l_len = 0; //设置加锁区域的长度。
//以上三个设置可以为整个文件加锁。
lock2.l_type = F_RDLCK; //初始化l_type为读取锁。
lock2.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); //将内容写入buf。
src_file = open(fname, O_RDONLY);//打开fname,并将文件描述符存在src_file。
dest_file = open(txt_name,O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);//打开txt_name,并将文件描述符存在dest_file。
if (src_file< 0 || dest_file< 0)//如果源文件与目标文件打开失败。
{
return;
}
fcntl(dest_file, F_SETLKW, &lock1);//以阻塞的方式为dest_file设置结构为lock1的文件锁。
fcntl(src_file, F_SETLKW, &lock2);//以阻塞的方式为src_file设置结构为lock2的文件锁。
while ((real_read_len = read(src_file, buff, sizeof(buff))) > 0)//读取源文件sec_file的sizeof(buff)个字节的数据并存在buff中,并记录真实读取的字节数。
{
write(dest_file, buff, real_read_len);//向dest_fie写入buff内real_read_len字节的数据。
}
fcntl(dest_file, F_UNLCK, &lock1);//给dest_file解锁。
fcntl(src_file, F_UNLCK, &lock2);//给src_file解锁。
close(dest_file);//关闭文件描述符dest_file。
close(src_file);//关闭文件描述符src_file。
}
//处理arg并调用相应的函数转发;
void *strdeal(void *arg){
char sign[10];
char buf[1024];
char s[1024];
struct flock lock; //新建结构体flock为lock1
lock.l_whence = SEEK_SET; //相对位移量的起点设置为文件头0。
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)); //将s中当前位置后面的strlen(s)个字节用0替换。
memset(sign, 0, strlen(sign)); //将sign中当前位置后面的strlen(sign)个字节用0替换。
memset(buf, 0, strlen(buf)); //将buf中当前位置后面的strlen(buf)个字节用0替换。
if(recv(clientfd, s, 1024, 0) <= 0) //把clientfd的接收缓冲中的1024字节的数据copy到s中;如果接收错误。
{
perror(\"fail to recv\"); //把\"fail to recv\"输出到标准错误stderr。
close(clientfd); //关闭socket端口。
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){ //比较sign与\"User\",若相同返回0;若相同。
b_file = open(fname,O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); //打开fname,并将文件描述符存在b_file。
fcntl(b_file, F_SETLKW, &lock); //以阻塞的方式为b_file设置结构为lock的文件锁。
write(b_file, buf, strlen(buf)); //向b_file写入buf。
fcntl(b_file, F_UNLCK, &lock); //给b_file解锁。
close(b_file); //关闭文件描述符b_file。
GtkTextIter start,end; //新建保存文字在buffer中位置的结构start和end。
sem_p(sem_id); //信号量减1。
gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(bufferuser),&start,&end); //得到当前buffer中开始位置,结束位置的ITER。
gtk_text_buffer_insert(GTK_TEXT_BUFFER(bufferuser),&start,buf,strlen(buf)); //向文本框插入文字。start,end分别为文本框文字开始位置和结束位置的iter,插入文本的长度为strlen(buf)。
sem_v(sem_id); //信号量加1。
}else{ //若sign与\"User\"不同。
GtkTextIter start,end; //新建保存文字在buffer中位置的结构start和end。
gtk_text_buffer_get_bounds(GTK_TEXT_BUFFER(buffernotice),&start,&end);
if(strcmp(buf,\"over\")==0){ // 收到over说明服务停止,所以程序退出
strcpy(buf,\"服务停止,程序即将退出\\n\"); //将\"服务停止,程序即将退出\\n\"复制进buf。
sem_p(sem_id); //信号量减1。
gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffernotice),&start,buf,strlen(buf)); //向文本框插入文字。start,end分别为文本框文字开始位置和结束位置的iter,插入文本的长度为strlen(buf)。
sem_v(sem_id); //信号量加1。
sleep(2); //休眠2秒。
close(clientfd); //关闭socket端口。
un (fname); //删除fname。
exit(0);
}else{
sem_p(sem_id); //信号量减1。
gtk_text_buffer_insert(GTK_TEXT_BUFFER(buffernotice),&start,buf,strlen(buf)); //向文本框插入文字。start,end分别为文本框文字开始位置和结束位置的iter,插入文本的长度为strlen(buf)。
sem_v(sem_id); //信号量加1。
}
}
}
}
//主界面
void homepage(int argc,char *argv[]){
char *buf;
buf = (char *)malloc(1024); //向系统申请分配指定1024个字节的内存空间。
memset(buf, 0, 1024); //将buf中当前位置后面的1024个字节用0替换。
gtk_init(&argc, &argv); //初始化。
home = gtk_window_new(GTK_WINDOW_TOPLEVEL); //创建顶层窗口。
gtk_window_set_ (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); //固定窗口的大小
g_signal_connect(home, \"destroy\", G_CALLBACK(gtk_main_quit), NULL); // \"destroy\" 和 gtk_main_quit 连接
GtkWidget *fixed = gtk_fixed_new(); //创建一个固定容器
gtk_container_add(GTK_CONTAINER(home), fixed); // 将布局容器放窗口中。
GtkWidget *label_one;
GtkWidget *label_two;
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);
//创建按钮bsend
GtkWidget *bsend = gtk_button_new_with_label(\"--发送--\");
gtk_fixed_put(GTK_FIXED(fixed), bsend,325,300);
//创建按钮send_all
GtkWidget *send_all = gtk_button_new_with_label(\"--群发--\");
gtk_fixed_put(GTK_FIXED(fixed), send_all,390,300);
//创建按钮save
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); //休眠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);
sem_id = semget(ftok(\"/\", \'a\'), 1, 0666|IPC_CREAT);
init_sem(sem_id, 1);
login(argc,argv);
homepage(argc,argv);
un (fname); //删除临时储存消息记录文件
return 0;
}
继续阅读与本文标签相同的文章
上一篇 :
重建二叉树
下一篇 :
暴涨突破1万美元后,比特币价格跌破9400美元
-
AI+5G科技创新 视频行业呈现轻应用化趋势
2026-05-14栏目: 教程
-
1.98亿滴滴用户添加了紧急联系人 每天百万个订单行程分享给亲友
2026-05-14栏目: 教程
-
工程院院士刘韵洁:5G前景很大,但主要是行业应用
2026-05-14栏目: 教程
-
陆奇:看好5G技术,但应用好5G还需要时间
2026-05-14栏目: 教程
-
在Visual Studio中使用clang-tidy进行代码分析
2026-05-14栏目: 教程
