基于Linux的即时通信软件

这段时间做了一个比较简单的即时通信软件,就把这个过程记录一下吧,一方面可以加深一下自己对这个项目的印象,另一方面也希望可以帮助到各位正在学习这一块内容的博友!!!



代码实现

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`

部分截图

登录窗口

在这里插入图片描述

好友列表

在这里插入图片描述

聊天窗口

在这里插入图片描述

使用方法

  1. 首先把服务器端的代码运行起来,然后等待客户端发送的请求
  2. 其次运行客户端的代码,然后进行登录,由于没有使用数据库,所以没有注册功能,有能力的朋友可以自己添加
  3. 登录的初始账号和密码都是123456,可以根据自己的喜好在代码中进行修改
  4. 输入消息即可在聊天窗口和服务器端收到


以上就是博主对这次项目的一些简单总结,代码都是博主运行过的,如果大家在使用的过程中遇到什么问题可以给博主留言,看到必回,如果有帮助到各位的话记得点赞加关注哦!!!

  • 4
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

"WBB"

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值