这段时间做了一个比较简单的即时通信软件,就把这个过程记录一下吧,一方面可以加深一下自己对这个项目的印象,另一方面也希望可以帮助到各位正在学习这一块内容的博友!!!
文章目录
代码实现
Client
client.h
#include <gtk/gtk.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define OURPORT 8088 //定义端口号
extern gint sd; //套接字句柄
extern struct sockaddr_in s_in; //套接字数据结构
extern gchar username[64]; //用户名
extern gchar buf[1024];//写缓冲区
extern gchar get_buf[1048]; //读缓冲区
extern gboolean isconnected; //定义逻辑值表示是否连接
extern GtkWidget *text;
extern GtkTextBuffer *buffer; //显示对话内容的文本显示缓冲区
extern GtkWidget *message_entry; //显示输入消息的单行录入控件
extern GtkWidget *name_entry; //输入用户名的单行录入控件
extern GtkWidget *login_button; //登录按钮
void on_destroy(GtkWidget *widget, GdkEvent *event, gpointer data);
void on_button_clicked(GtkButton *button, gpointer data);
gboolean do_connect(char *username);
void on_send (gpointer *name);
void create_window(char *name);
client.c
#include "client.h"
//连接多人聊天服务器
gboolean do_connect(char *username)
{
GtkTextIter iter;
gint slen;
sd = socket(AF_INET,SOCK_STREAM,0);
if(sd < 0)
{
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,"打开套接字时出错!\n",-1);
return FALSE;
}
s_in.sin_family = AF_INET;
s_in.sin_port = OURPORT;
slen = sizeof(s_in);
if(connect(sd,&s_in,slen) < 0)
{
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,"连接服务器时出错!\n",-1);
return FALSE;
}
else
{
isconnected = TRUE;
return TRUE;
}
}
//向服务器发送数据
void on_send (gpointer *name)
{
GtkTextIter iter;
const char* message;
if(isconnected==FALSE) return;
message = gtk_entry_get_text(GTK_ENTRY(message_entry));
sprintf(buf,">%s\n\n",message);
write(sd,buf,1024);//发送数据给服务器
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,buf,-1);
read(sd,username,64);
gtk_text_buffer_get_end_iter(buffer,&iter);
gtk_text_buffer_insert(buffer,&iter,username,-1);
sprintf(buf,"%s\n\n","");
gtk_entry_set_text(GTK_ENTRY(message_entry),"");//清除单行录中的文字
}
//关闭主窗口时执行
void on_delete_event (GtkWidget *widget, GdkEvent* event, gpointer data)
{
close(sd);//关闭
gtk_main_quit();
}
//创建聊天窗口
void create_window(char *name)
{
gpointer n=(gpointer)name;
GtkWidget *window;
GtkWidget *vbox, *hbox, *button, *label, *view;
if(!g_thread_supported())
g_thread_init(NULL); //初始线程
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),name);
gtk_window_set_default_size(GTK_WINDOW(window),800,500);
g_signal_connect(G_OBJECT(window),"delete_event",
G_CALLBACK(on_delete_event),NULL);
gtk_container_set_border_width(GTK_CONTAINER(window),10);
vbox = gtk_vbox_new(FALSE,0);
gtk_container_add(GTK_CONTAINER(window),vbox);
hbox = gtk_hbox_new(FALSE,0);
gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,5);
view = gtk_scrolled_window_new(NULL,NULL);
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(view),
GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
text = gtk_text_view_new();
gtk_box_pack_start(GTK_BOX(vbox),view,TRUE,TRUE,5);
gtk_container_add(GTK_CONTAINER(view),text);
buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
hbox = gtk_hbox_new(FALSE,0);
gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,5);
label = gtk_label_new("输入消息:");
gtk_box_pack_start(GTK_BOX(hbox),label,FALSE,FALSE,5);
message_entry = gtk_entry_new();
gtk_box_pack_start(GTK_BOX(hbox),message_entry,FALSE,FALSE,5);
button = gtk_button_new_with_label("发送");
gtk_box_pack_start(GTK_BOX(hbox),button,FALSE,FALSE,5);
g_signal_connect(G_OBJECT(button),"clicked",
G_CALLBACK(on_send),n);
gtk_widget_show_all(window);
gdk_threads_enter();
gtk_main();
gdk_threads_leave();
}
list.h
#include <gtk/gtk.h>
enum {
NAME_COLUMN,
IP_COLUMN,
PORT_COLUMN,
STATUS_COLUMN,
NBR_COLUMNS
};
void view_onRowActivated (GtkTreeView *treeview,GtkTreePath *path,GtkTreeViewColumn *col,gpointer userdata);
static GtkWidget *create_view_and_model(void);
static GtkTreeModel *create_and_fill_model(void);
int list(char *name );
list.c
#include "list.h"
#include"client.h"
//当双击好友昵称时触发的事件
void view_onRowActivated (GtkTreeView *treeview,GtkTreePath *path,GtkTreeViewColumn *col,gpointer userdata)
{
GtkTreeModel *model;
GtkTreeIter iter;
model = gtk_tree_view_get_model(treeview);
if (gtk_tree_model_get_iter(model, &iter, path))
{
gchar *name;
gint *ip;
gint *port;
gtk_tree_model_get(model, &iter, NAME_COLUMN, &name, -1);
gtk_tree_model_get(model, &iter, IP_COLUMN, &ip, -1);
gtk_tree_model_get(model, &iter, PORT_COLUMN, &port, -1);
do_connect(name);
create_window(name);
g_free(name);
}
}
//通过迭代生成好友列表
static GtkTreeModel *create_and_fill_model(void)
{
GtkTreeStore *treestore;
GtkTreeIter toplevel, child;
treestore = gtk_tree_store_new(NBR_COLUMNS, G_TYPE_STRING,G_TYPE_INT,G_TYPE_INT,G_TYPE_STRING);
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel, NAME_COLUMN, "我的好友", -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "瓜仔", IP_COLUMN,1111,PORT_COLUMN,0,STATUS_COLUMN,"在线",-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "胖仔",IP_COLUMN, 2222,PORT_COLUMN,1,STATUS_COLUMN,"在线",-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "三火", IP_COLUMN,3333,PORT_COLUMN,2,STATUS_COLUMN,"在线",-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "耿妹子", IP_COLUMN,4444,PORT_COLUMN,3,STATUS_COLUMN,"在线",-1);
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel, NAME_COLUMN, "陌生人", -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "张三", STATUS_COLUMN,"离线",-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "李四", STATUS_COLUMN,"离线",-1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "王五", STATUS_COLUMN,"离线",-1);
gtk_tree_store_append(treestore, &toplevel, NULL);
gtk_tree_store_set(treestore, &toplevel, NAME_COLUMN, "黑名单", -1);
gtk_tree_store_append(treestore, &child, &toplevel);
gtk_tree_store_set(treestore, &child, NAME_COLUMN, "邢文璇", STATUS_COLUMN,"离线",-1);
return GTK_TREE_MODEL(treestore);
}
//通过渲染器进行好友列表的显示
static GtkWidget *create_view_and_model(void)
{
GtkTreeViewColumn *col;
GtkCellRenderer *renderer;
GtkWidget *view;
GtkTreeModel *model;
view = gtk_tree_view_new();
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_title(col, "好友列表");
gtk_tree_view_column_set_resizable (col, TRUE);
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width (col, 180);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer, "text", NAME_COLUMN);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model);
col = gtk_tree_view_column_new();
gtk_tree_view_column_set_resizable (col, TRUE);
gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
gtk_tree_view_column_set_fixed_width (col, 100);
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
renderer = gtk_cell_renderer_text_new();
gtk_tree_view_column_pack_start(col, renderer, TRUE);
gtk_tree_view_column_add_attribute(col, renderer, "text",STATUS_COLUMN);
model = create_and_fill_model();
gtk_tree_view_set_model(GTK_TREE_VIEW(view), model);
g_object_unref(model);
return view;
}
int list(char *name)
{
GtkWidget *view;
GtkWidget *window;
GtkTreeSelection *selection;
GtkWidget *vbox;
GtkWidget *statusbar;
// gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_title(GTK_WINDOW(window), name);
gtk_window_set_default_size(GTK_WINDOW(window),357,688);
vbox = gtk_vbox_new(FALSE, 2);
gtk_container_add(GTK_CONTAINER(window), vbox);
view = create_view_and_model();
selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 1);
statusbar = gtk_statusbar_new();
gtk_box_pack_start(GTK_BOX(vbox), statusbar, FALSE, TRUE, 1);
g_signal_connect(view, "row-activated", (GCallback) view_onRowActivated, NULL);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
main.c
#include<stdio.h>
#include<gtk/gtk.h>
#include<stdlib.h>
#include<string.h>
#include"list.h"
#include"client.h"
/**************************************************************************************/
static gboolean release_resourse(GtkWidget *widget,GdkEvent *event,gpointer data);
void chang_background(GtkWidget *widget, int w, int h, const gchar *path);
void callback_button(GtkWidget *button,GtkWidget *entry[3]);
gint sd; //套接字句柄
struct sockaddr_in s_in; //套接字数据结构
gchar username[64]; //用户名
gchar buf[1024]; //写缓冲区
gchar get_buf[1048]; //读缓冲区
gboolean isconnected = FALSE; //定义逻辑值表示是否连接
GtkWidget *text;
GtkTextBuffer *buffer; //显示对话内容的文本显示缓冲区
GtkWidget *message_entry; //显示输入消息的单行录入控件
GtkWidget *name_entry; //输入用户名的单行录入控件
GtkWidget *login_button; //登录按钮
/*************************************************************************************/
/*************************************************************************************/
GtkWidget *window1;
/*************************************************************************************/
/***************************************更换背景***************************************/
void chang_background(GtkWidget *widget, int w, int h, const gchar *path)
{
gtk_widget_set_app_paintable(widget, TRUE); //允许窗口可以绘图
gtk_widget_realize(widget);
gtk_widget_queue_draw(widget);
GdkPixbuf *src_pixbuf = gdk_pixbuf_new_from_file(path, NULL);
GdkPixbuf *dst_pixbuf = gdk_pixbuf_scale_simple(src_pixbuf, w, h, GDK_INTERP_BILINEAR);
GdkPixmap *pixmap = NULL;
gdk_pixbuf_render_pixmap_and_mask(dst_pixbuf, &pixmap, NULL, 128);
gdk_window_set_back_pixmap(widget->window, pixmap, FALSE);
g_object_unref(src_pixbuf);
g_object_unref(dst_pixbuf);
g_object_unref(pixmap);
}
/***************************************回应登录***********************************/
void callback_button(GtkWidget *button,GtkWidget *entry[3])
{
const gchar *name = NULL;
const gchar *password = NULL;
gboolean btn_state;
name = gtk_entry_get_text((GtkEntry *)entry[0]);
password = gtk_entry_get_text((GtkEntry *)entry[1]);
if(strcmp(name,"123456") == 0 && strcmp(password,"123456") == 0)//初始的账号和密码
{
gtk_widget_hide(window1);
list(name);
}
else
{
GtkWidget *dialog;
GtkWidget *label;
dialog = gtk_dialog_new();//创建一个对话框
gtk_window_set_title(GTK_WINDOW(dialog), "错误提示");
gtk_widget_set_usize(dialog, 300, 100);//设置对话框大小
label = gtk_label_new("用户名或密码错误!请重新输入");
gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0);
g_signal_connect(G_OBJECT(dialog), "delete_event", G_CALLBACK(release_resourse), NULL);
gtk_widget_show_all(dialog);
gtk_main();
}
}
/**************************************关闭窗口回应**********************************/
static gboolean release_resourse(GtkWidget *widget,GdkEvent *event,gpointer data)
{
gtk_main_quit();
return FALSE;
}
/**************************************登录界面*********************************************/
void main(int argc,char *argv[])
{
GtkWidget *label1;
GtkWidget *label2;
GtkWidget *button1;
GtkWidget *fixed;
GtkWidget *entry1;
GtkWidget *entry2;
GtkWidget *entry[3];
gtk_init(&argc,&argv);
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title((GtkWindow*)window1,"QQ");//标题
gtk_widget_set_usize(window1,540,375);
gtk_container_set_border_width(GTK_CONTAINER(window1),10);
gtk_window_set_position(GTK_WINDOW(window1),GTK_WIN_POS_CENTER);
chang_background(window1, 540, 375, "2.jpg");//登录界面用到的图片
//创建一个固定布局,创建标签和按钮并把标签按钮和行编辑都放在这个固定布局里面
button1 = gtk_button_new_with_label("登录");
label1 = gtk_label_new("用户");
label2 = gtk_label_new("密码");
entry1 = gtk_entry_new_with_max_length(20);
entry2 = gtk_entry_new_with_max_length(20);
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window1),fixed);
gtk_fixed_put(GTK_FIXED(fixed),label1,30,150);
gtk_fixed_put(GTK_FIXED(fixed),label2,30,205);
gtk_fixed_put(GTK_FIXED(fixed),entry1,110,150);
gtk_fixed_put(GTK_FIXED(fixed),entry2,110,205);
gtk_fixed_put(GTK_FIXED(fixed),button1,110,280);
gtk_entry_set_visibility(GTK_ENTRY(entry2),FALSE);//设置密码不可见,entry2是一个编辑密码行,所以肯定不可见
gtk_widget_set_size_request(button1,310,50);
gtk_widget_set_size_request(label1,100,50);
gtk_widget_set_size_request(label2,100,50);
gtk_widget_set_size_request(entry1,310,50);
gtk_widget_set_size_request(entry2,310,50);
entry[0] = entry1;
entry[1] = entry2;
entry[2] = window1;
/***************写回调函数**********************************************************/
g_signal_connect(G_OBJECT(button1),"pressed",G_CALLBACK(callback_button),&entry);
g_signal_connect(G_OBJECT(window1),"delete_event",G_CALLBACK(release_resourse),NULL);
gtk_widget_show_all(window1);
gtk_main();
}
makefile
CC=gcc
all:
$(CC) -Wall -g -o main main.c client.c list.c `pkg-config --cflags --libs gtk+-2.0 gthread-2.0`
server
server.c
#include <glib.h>
#include <stdio.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#define OURPORT 8088
#define MAX_USERS 8
//定义用户数据结构
struct _client {
gint sd;
gboolean in_use;
gchar name[64];
gchar buf[1024];
};
typedef struct _client client;
//定义用户数据区
client user[MAX_USERS];
//定义服务线程
void do_service (gpointer id)
{
gint j;
char tobuf[1024];
while(read(user[GPOINTER_TO_INT(id)].sd,
user[GPOINTER_TO_INT(id)].buf,1024)!=-1)
{
sprintf(tobuf,"%s\n\n",
user[GPOINTER_TO_INT(id)].buf);
for(j=0; j<MAX_USERS; j++)
{
if(user[j].in_use)
{
write(user[j].sd,tobuf,1024);
g_print("%s",tobuf);
}
}
}
//
user[GPOINTER_TO_INT(id)].in_use = FALSE;
close(user[GPOINTER_TO_INT(id)].sd);
//exit(0);
}
int main(int argc, char* argv[])
{
gint sd, newsd;
struct sockaddr_in *sin;
gint slen;
gint count = 0;
gint flags;
gchar buf[1024];
gchar tobuf[1024];
gint length,i,j;
if(!g_thread_supported())
g_thread_init(NULL);
else
g_print("thread not supported\n");
sd = socket(AF_INET,SOCK_STREAM,0);
if(sd == -1)
{
g_print("create socket error!\n");
return -1;
}
sin = g_new(struct sockaddr_in,1);
sin->sin_family = AF_INET;
sin->sin_port = OURPORT;
slen = sizeof(struct sockaddr_in);
if(bind(sd,sin,slen)<0)
{
g_print("bind error!\n");
return -1;
}
if(listen(sd,8)<0)
{
g_print("listen error!\n");
return -1;
}
for(i=0; i<MAX_USERS; i++)
user[i].in_use = FALSE;
flags = fcntl(sd,F_GETFL);
fcntl(sd,F_SETFL,flags&~O_NDELAY);
for(;;)
{
newsd = accept(sd,sin,&slen);
if(newsd == -1)
{
g_print("accept error!\n");
break;
}
else
{
if(count >= MAX_USERS)
{
sprintf(buf,"用户数量过多服务器不能连接。\n");
write(newsd,buf,1024);
close(newsd);
}
else
{
flags = fcntl(user[i].sd,F_GETFL);
fcntl(user[i].sd,F_SETFL,O_NONBLOCK);
user[count].sd = newsd;
user[count].in_use = TRUE;
read(newsd,user[count].name,64);
//创建为用户服务的线程
g_thread_create((GThreadFunc)do_service,
(gpointer)count,TRUE,NULL);
count++;
}
}
}//for(;;)
close(sd);
g_free(sin);
}
makefile
CC = gcc
all:
$(CC) -o server server.c `pkg-config --cflags --libs glib-2.0 gthread-2.0`
部分截图
登录窗口
好友列表
聊天窗口
使用方法
- 首先把服务器端的代码运行起来,然后等待客户端发送的请求
- 其次运行客户端的代码,然后进行登录,由于没有使用数据库,所以没有注册功能,有能力的朋友可以自己添加
- 登录的初始账号和密码都是123456,可以根据自己的喜好在代码中进行修改
- 输入消息即可在聊天窗口和服务器端收到
以上就是博主对这次项目的一些简单总结,代码都是博主运行过的,如果大家在使用的过程中遇到什么问题可以给博主留言,看到必回,如果有帮助到各位的话记得点赞加关注哦!!!