用gtk与winsock制作的局域网聊天工具[整理]
用gtk与winsock制作的局域网聊天工具
//这是func.h的内容,包含了客户端所用到的头文件:
#include
#include
#include
#include
#include
#pragma comment(lib, "ws2_32.lib") extern const gchar *buf_chat; extern char buf_list[10000]; extern char buf_send[10000]; extern char buf_recv[10000]; extern char MY_IP[100];
extern char chat[20];
extern char mytime[20];
extern char list[15];
extern char Tag[10];
extern SOCKADDR_IN address; extern gint flag;
extern gint tag;
extern time_t rawtime;//获取系统当前时间 extern struct tm * timeinfo; //--------------------------------------------------------
extern GtkWidget *window; extern GtkWidget *dialog; extern GtkWidget *window_start; extern GtkTextBuffer *buffer; //--------------------------------------------------------
gchar *str(char *);//将GB2312码转化为UTF-8码
gchar *srt(char *);//将UTF-8码转化为GB2312码
void store_add(GtkListStore *,char *);//给列
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
模型添加一行
void buffer_add(GtkTextBuffer *,char *);//给文本框添加一行
gint send_message(gpointer );//发送消息给服务器
void check(int *);//检测主机是否在线,用一个新的线程阻塞监听服务器
void check_chat(gpointer );//更新文本框聊天信息
gint check_list(gpointer );//更新上线主机列表 gint start(int *);//判断主机是否上线,若超时则提示
void my_quit();//退出程序并删除服务器主机列表上本机IP
//这是func.c的内容,包括了客户端所用到的函数:
#include "Func.h"
gchar *str(char *string)//将GB2312码转化为UTF-8码
{
return(g_locale_to_utf8(string,-1,NULL,NULL,NULL));
}
gchar *srt(char *string)//将UTF-8码转化为GB2312码
{
return(g_locale_from_utf8(string,-1,0,0,0));
}
void store_add(GtkListStore *store,char *str)//给列表模型添加一行
{
GtkTreeIter iter;//定义列表模型里列的行号
gtk_list_store_append(store,&iter);//在列表模型里添加一行
gtk_list_store_set(store,&iter,0,str,-1);//给新行设置属性
}
void buffer_add(GtkTextBuffer *data,char *str)//给文本框添加一行
{
GtkTextIter iter;
gtk_text_buffer_get_end_iter(data,&iter);//使迭代标志指向文本缓冲的末尾
gtk_text_buffer_insert(data,&iter,str,-1);//往缓冲区的末尾添加文本
}
gint send_message(gpointer data)//发送消息给服务器
{
char
*check_buf="\001";//---------------------------------------
-----------------------|
SOCKET
socket_client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //定
义socket句柄
connect(socket_client,(SOCKADDR*)&address,sizeof(address));
send(socket_client,check_buf,100,0);//-------------------------------------------------|
buf_chat=srt(gtk_entry_get_text(GTK_ENTRY(data)));//获取
entry文本
strcpy(buf_send,"\005");
strcat(buf_send,MY_IP);
strcat(buf_send,buf_chat);
send(socket_client,buf_send,10000,0);
closesocket(socket_client);
gtk_entry_set_text(data,"");
return TRUE;
}
void check(int *flag)//检测主机是否在线,并发送本机IP到主机
列表
{
char
*check_buf="\002";//--------------------------------------------------------------|
SOCKET
socket_client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(!connect(socket_client,(SOCKADDR*)&address,sizeof(address)))
{
send(socket_client,check_buf,100,0);//---------------------------------------------|
send(socket_client,MY_IP,100,0);//像服务端发送本机IP
closesocket(socket_client);
*flag=0;
}
_endthread();
}
void check_chat(gpointer data)//更新文本框聊天信息
{
int i=0;
int j=0;
char
*check_buf="\006";//---------------------------------------
-----------------------|
SOCKET
socket_client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //定义socket句柄
if(!connect(socket_client,(SOCKADDR*)&address,sizeof(add
ress)))
{
send(socket_client,check_buf,100,0);//------------------
---------------------------|
sprintf(Tag,"%d",tag);
send(socket_client,Tag,strlen(Tag),0);//发送本地文本框消息个数
//这里的"Tag"一定要作为strlen()来发送,否则会与服务器断开连接
recv(socket_client,Tag,100,0);//接收服务器消息个数
sscanf(Tag,"%d",&i);//把服务器消息个数同步到本地消息
个数
if(i!=tag)
{
tag=i;
i=0;
recv(socket_client,buf_recv,10000,0);
//收到的消息有两条,要去掉收到的第一条消息(算法不成
功)
char *p;
char *q;
p=buf_recv;
q=buf_recv;
while(*(p+j))
{
if(p[j]=='\005')
{
q=p+j;
i++;
}
if(i==2) break;
j++;
}
strcpy(buf_recv,q);//获得服务器最后一条消息
i=0;
p=buf_recv;
q=buf_recv;
while(buf_recv[i])//得到消息内的IP段
{
if(i==1) q=p+i;//IP头
if(i==15) p=p+i;//消息头
i++;
}
strcpy(chat,q);
chat[15]=0;
time(&rawtime);//把时间加在IP后面
timeinfo=localtime(&rawtime);
sprintf(mytime,"(%d:%d:%d)",timeinfo->tm_hour,timeinfo->
tm_min,timeinfo->tm_sec);
strcat(chat,mytime);
i=strlen(mytime);
chat[15+i]='\n';
chat[16+i]=0;
buffer_add(data,str(chat));
buffer_add(data,str(p));
buffer_add(data,"\n");
}
strcpy(buf_recv,"");
strcpy(chat,"");
strcpy(mytime,"");
closesocket(socket_client);
}
_endthread();
}
gint check_list(gpointer data)//更新上线主机的列表
{
_beginthread(check,0,&flag);//更新前发送本机IP
_beginthread(check_chat,0,buffer);
char
*check_buf="\003";//--------------------------------------------------------------|
SOCKET
socket_client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(connect(socket_client,(SOCKADDR*)&address,sizeof(address)))
{
dialog=gtk_message_dialog_new(window,GTK_DIALOG_MODAL,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,str("连接服务器失败~"));
gtk_window_set_title(GTK_WINDOW(dialog), str("发生错
误"));
gtk_dialog_run(GTK_DIALOG(dialog));
gtk_widget_hide_all(GTK_WINDOW(dialog));
gtk_list_store_clear(data);
store_add(data,str("主机离线"));
}
else//若服务器在线则更新主机列表
{
gtk_list_store_clear(data);//连接成功后清空本地主机列
表
send(socket_client,check_buf,100,0);//---------------------------------------------|
recv(socket_client,buf_list,10000,0);//获取服务器在线
主机列表
int i=0;
char *p;
p=buf_list;//用外部指针去访问字符串里被隐藏的下标指针位置
while(*p)
{
while(i<=15)
{
list[i]=p[i];
i++;
}
list[i-1]=0;
i=0;
store_add(data,list);
p=p+15;
}
strcpy(buf_list,"");
strcpy(list,"");
closesocket(socket_client);
}
return TRUE;
}
void my_quit()//退出程序并删除服务器主机列表上本机IP
{
char
*check_buf="\004";//---------------------------------------
-----------------------|
SOCKET
socket_client=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
connect(socket_client,(SOCKADDR*)&address,sizeof(address
));
send(socket_client,check_buf,100,0);//------------------
-------------------------------|
send(socket_client,MY_IP,100,0);
closesocket(socket_client);
gtk_main_quit();
}
gint start(int *flag)//判断主机是否上线,若超时则提示
{
if(*flag==0)
{
gtk_widget_destroy(window_start);
gtk_widget_show_all(window);
return FALSE;
}
(*flag)++;
if((*flag)>=30)//如果连接超时出现警告窗口
{
gtk_window_set_keep_above(GTK_WINDOW(window_start),FALSE
);
dialog=gtk_message_dialog_new(window_start,GTK_DIALOG_MODAL
,GTK_MESSAGE_ERROR,GTK_BUTTONS_OK,str("连接服务器失败~"));
gtk_window_set_title(GTK_WINDOW(dialog),str("发生错误"));
gtk_dialog_run(GTK_DIALOG(dialog));
//gtk_widget_destroy(dialog);//销毁警告窗口,继续程序
gtk_widget_hide_all(GTK_WINDOW(dialog));//隐藏警告窗
口,继续程序
gtk_widget_destroy(GTK_WINDOW(window_start));
gtk_widget_show_all(GTK_WINDOW(window));
return FALSE;
}
return TRUE;
}
//这是客户端的主函数和全局变量的定义,主函数中包含大量的gtk+
的构建:
#include "Func.h" const gchar *buf_chat; char buf_list[10000]; char buf_send[10000]; char buf_recv[10000]; char MY_IP[100];
char chat[20];
char mytime[20];
char list[15];
char Tag[10];
SOCKADDR_IN address; GtkWidget *window; GtkWidget *dialog; GtkWidget *window_start; GtkTextBuffer *buffer; gint flag=1;
gint tag=0;
time_t rawtime;//获取系统当前时间
struct tm * timeinfo;
int main(int argc, char *argv[])
{
//定义
-----------------------------------------------------------
--------------------------------------------
GtkWidget *button;//声明构件指针
GtkWidget *entry;
GtkWidget *table;
GtkWidget *frame;
GtkWidget *progressBar;
GtkWidget *view_chat;
GtkWidget *view_list;//定义列表
GtkListStore *view_list_store;//定义列表模型
GtkCellRenderer *view_list_renderer_text;//定义列表渲染器为文本渲染
GtkTreeViewColumn *view_list_column;//定义列表行
GtkWidget *scrolled_chat;
GtkWidget *scrolled_list;
//环境
-----------------------------------------------------------
--------------------------------------------
WSADATA WASdata;//初始化winsock环境
WSAStartup(MAKEWORD(2,2),&WASdata);
address.sin_family=AF_INET;
address.sin_port=htons(60000);
address.sin_addr.s_addr=inet_addr("172.16.104.123");
gtk_init(&argc,&argv);
//声明
-------------------------------------------------------------------------------------------------------
entry=gtk_entry_new();//定义构件指针
button=gtk_button_new_with_label(str("确定"));
window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
window_start=gtk_window_new(GTK_WINDOW_TOPLEVEL);
progressBar=gtk_progress_bar_new();
table=gtk_table_new(10,18,TRUE);
view_chat=gtk_text_view_new();//显示聊天信息的文本框
buffer=gtk_text_view_get_buffer(GTK_TEXT_VIEW(view_chat)
);//获得文本框的缓冲区
view_list_renderer_text=gtk_cell_renderer_text_new();
view_list_column=gtk_tree_view_column_new();
view_list_store=gtk_list_store_new(1,G_TYPE_STRING);//创
建列表模型为1列,字符串型
view_list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(view_list_store));//把列表模型加入列表并创建
scrolled_chat=gtk_scrolled_window_new(NULL,NULL);
scrolled_list=gtk_scrolled_window_new(NULL,NULL);
//属性
-------------------------------------------------------------------------------------------------------
gtk_widget_set_size_request(GTK_WINDOW(window_start),180
,20);//设置窗口最小大小
gtk_window_set_resizable(GTK_WINDOW(window_start),FALSE);//使窗口大小不可改变
gtk_window_set_decorated(GTK_WINDOW(window_start),FALSE);//去掉边框
gtk_window_set_position(GTK_WINDOW(window_start),GTK_WIN_POS_CENTER);
gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window),320,240);//窗口默认大小,会被改变
gtk_container_border_width(GTK_CONTAINER(window),5);
gtk_window_set_title(GTK_WINDOW(window),"Client");
gtk_progress_bar_pulse(GTK_PROGRESS_BAR(progressBar));//设置进度条显示方式
gtk_table_set_row_spacings(GTK_TABLE(table),5);//table行
列间隔
gtk_table_set_col_spacings(GTK_TABLE(table),5);
gtk_scrolled_window_set_policy(scrolled_chat,GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);//滚动窗口滚动条出现策略
gtk_scrolled_window_set_policy(scrolled_list,GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
gtk_text_view_set_editable(view_chat,FALSE);//设置文本框
不可编辑
gtk_text_view_set_wrap_mode(view_chat,GTK_WRAP_CHAR);//
设置文本框水平超出界面就换行
gtk_tree_view_column_pack_start(view_list_column,view_list_renderer_text,FALSE);//把列表渲染器关联列表列
gtk_tree_view_append_column(GTK_TREE_VIEW(view_list),view_list_column);//把列表列关联列表模型
gtk_tree_view_column_add_attribute(view_list_column,view_list_renderer_text,"text",NULL);//为列表渲染器添加属性
gtk_tree_view_column_set_title(view_list_column,str("在
线主机"));
//组装
-------------------------------------------------------------------------------------------------------
gtk_container_add(GTK_CONTAINER(window_start),progressBar);//将构件装进盒子
gtk_container_add(GTK_CONTAINER(scrolled_chat),view_chat);//为文本框添加滚动条
gtk_container_add(GTK_CONTAINER(scrolled_list),view_list);
gtk_table_attach(GTK_TABLE(table),scrolled_chat,0,12,0,8,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
gtk_table_attach(GTK_TABLE(table),scrolled_list,12,18,0,8,GTK_EXPAND|GTK_FILL,GTK_EXPAND|GTK_FILL,0,0);
gtk_table_attach(GTK_TABLE(table),entry,0,12,8,10,GTK_EXPAND|GTK_FILL,GTK_EXPAND,0,0);
gtk_table_attach(GTK_TABLE(table),button,12,18,8,10,GTK_
EXPAND|GTK_FILL,GTK_EXPAND,0,0);
gtk_container_add(GTK_CONTAINER(window),table);
//信号
-------------------------------------------------------------------------------------------------------
g_signal_connect_swapped(G_OBJECT(button),"clicked",G_CALLBACK(send_message),entry);
g_signal_connect(G_OBJECT(entry),"activate",G_CALLBACK(send_message),entry);
g_signal_connect(G_OBJECT(window),"delete_event",G_CALLBACK(my_quit),NULL);
//初始化
-------------------------------------------------------------------------------------------------------
//本机IP初始化
-------------------------------------------
int i=0;
struct hostent *Host;
struct in_addr addr;
gethostname(MY_IP,100);//获取本机主机名
Host=gethostbyname(MY_IP);//用主机名获取主机IP
addr.s_addr=*(u_long*)Host->h_addr_list[0];//格式转换
strcpy(MY_IP,inet_ntoa(addr));
while(MY_IP[i]) i++;
while(i<=15)
{
if(i<=14) MY_IP[i]=32;
else MY_IP[i]=0;
i++;
}
//------------------------------------------------------
-
gtk_widget_show_all(window_start);
gtk_window_set_keep_above(GTK_WINDOW(window_start),TRUE)
;//使进度条窗口显示在所有窗口上面
gtk_timeout_add(500,G_CALLBACK(start),&flag);//重复检测主机状态,直到确认在线。
gtk_timeout_add(3500,G_CALLBACK(check_list),view_list_st
ore);//重复检测在线主机列表
gtk_main();//执行GTK
return 0;
}
//这是服务器的那端
#include
#include
#include
#pragma comment(lib, "ws2_32.lib") char buf[10000];
char buf_list[10000];
char buf_chat[10000];
char *check_make_list="\002"; char *check_read_list="\003"; char *check_dele_list="\004"; char *check_make_chat="\001"; char *check_read_chat="\006"; char number[10];
char list[15];
int tag=0;
int compstr(char *p,char *q) {
int n=0,m=0,k=0;
while(*(p+n)) n++;
while(*(q+m)) m++;
if(n==m)
{
while(*(p++)==*(q++)) k++;
if((k-1)>=m) return 1;
else return 0;
}
else return 0;
}
void myaccept(SOCKET socket_link) {
recv(socket_link,buf,100,0);
if(compstr(buf,check_make_chat))//接收主机消息
{
recv(socket_link,buf,10000,0);
strcat(buf_chat,buf);
tag++;//服务器消息队列加1
}
if(compstr(buf,check_read_chat))//发送主机聊天信息(算法不成功)
{
int num=0;
recv(socket_link,buf,100,0);//接收主机消息个数与服务器对比
sscanf(buf,"%d",&num);//把主机消息个数转为数字作标记
sprintf(number,"%d",tag);//把消息个数赋值到字符串与主
机消息个数比较
send(socket_link,number,strlen(number),0);//使主机消
息个数与服务器同步
if(!compstr(buf,number))
{
int i=0;
int j=0;
char *p;
char *q;
p=buf_chat;
q=buf_chat;
while(*(p+j))
{
if(p[j]=='\005')
{
q=p+j;
i++;
}
if(i==num) break;
j++;
}
send(socket_link,q,strlen(q),0);//这里q用strlen()
来发送
//所发送的消息只是服务器的最后两条(算法不成功)
}
strcpy(number,"");
}
if(compstr(buf,check_make_list))//添加在线列表
{
recv(socket_link,buf,100,0);
char *p;
int i=0;
int k=0;
p=buf_list;
while(*p)//检查在线主机是否重复
{
while(i<=15)
{
list[i]=p[i];
i++;
}
list[i-1]=0;
i=0;
if(compstr(buf,list)) k=1;
p=p+15;
}
if(k==0) strcat(buf_list,buf);
}
if(compstr(buf,check_read_list))//发送在线列表
{
send(socket_link,buf_list,10000,0);
}
if(compstr(buf,check_dele_list))//把离线的主机从列表中删除
{
recv(socket_link,buf,100,0);
char *p;
char *q;
int i=0;
p=buf_list;
while(*p)//标记主机所在下标位置
{
while(i<=15)
{
list[i]=p[i];
i++;
}
list[i-1]=0;
i=0;
if(compstr(buf,list))
{
q=p;
p=p+15;
break;
}
p=p+15;
}
strcpy(q,p);//把主机从列表中删除
strcpy(buf_list,q);
}
closesocket(socket_link);
_endthread();
}
int main()
{
WSADATA WASdata;//定义存储初始化信息的全局结构体
WSAStartup(MAKEWORD(2,2),&WASdata);//初始化winsock环境
SOCKET
socket_server=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);//定义socket句柄
SOCKET socket_link;
SOCKADDR_IN address;//定义地址结构体
address.sin_family=AF_INET; //定义Internet地址簇
address.sin_port=htons(60000); //定义端口
address.sin_addr.s_addr=inet_addr("0.0.0.0"); //定义地址
bind(socket_server,(SOCKADDR*)&address,sizeof(address));
//将socket与端口、地址绑定
listen(socket_server,10);//使socket进入监听队列
while(1)
{
socket_link=accept(socket_server,NULL,NULL);//对socket指定的地址进行监听
_beginthread(myaccept,0,socket_link);
}
closesocket(socket_server);
WSACleanup();
return 0;
}
//本来是想做linux与windows通用的程序的,但是socket编程以winsock比较容易,所以就先拿winsock来练手了.