例程十八_GUI_pygtk
Ø 功能
l python gtk 入门,做最简单的界面:含普通窗口,按扭,回调函数,窗口关闭时退出的程序
Ø 知识点
l python 是解释性语言,无需编译,即可直接执行
l python 有类定义,结构清晰 (this->self) ,没声明,直接实现
l 无 {} 来分界类和函数,只用缩进,所以不能使用 tab 在代码中缩进
Ø 示例
l 环境
ubuntu 8.04 系统,安装 python 及相关工具(如果安装不全,运行程序时会有提示,按提示安装缺少的包即可)
l 源码
#!/usr/bin/env python
import pygtk // 导入必要的库
pygtk.require('2.0')
import gtk // 导入必要的库
class test: // 主要的类声明,以下缩进后的都是类的实现
def test(self, widget, data=None): // 定义按钮按下的响应函数
print "Hello" // 打印hello
def destroy(self, widget, data=None): // 定义窗口销毁的响应函数
gtk.main_quit() // 退出应用
def __init__(self): // 构造函数
self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) // 建立窗口
self.window.connect("destroy", self.destroy) // 连接窗口销毁事件及其响应函数
self.window.show() // 显示窗口
self.button = gtk.Button("testme") // 建立按钮
self.button.connect("clicked", self.test, None) // 连接按钮及其点击的响应函数
self.button.show() // 显示按钮
self.window.add(self.button) // 把按件加入窗口
def main(self): // 定义类中的main 函数
gtk.main()
if __name__ == "__main__": // 主函数
testme = test() // 建立一个test 类的实例,此时调用构造函数
testme.main() // 运行类中的 main 函数
l 执行
$ chmod 755 ./a.py
$ ./a.py
或
$ python a.py
即可看到界面
Ø 参考
l python example
http://www.andrew.cmu.edu/user/skey/research_prev/checker/working%20now/gui/pygtk2tutorial/pygtk2tutorial/ch-GettingStarted.html#sec-HelloWorld
l 完整例程下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/GUI/case%7C_pythongtk.tgz
下载 case_pythongtk.tgz
例程十七_IPC_pipe
Ø 功能
l 进程间通讯 (IPC)
l 常用于数据流的传输
Ø 知识点
l 管道文件是一种特殊的文件,通常一个进程在一端写中,另一进程在另一端读出数据,从而达到进程间通讯
l 注意问题
u 管道文件不占磁盘空间
u 管道有缓冲区,可以设置其大小
u 由于管道中数据是流式的,所以可能引起一次写入,多次读出,或多次写入一次读出的问题,只能通过数据结构设计解决此问题
u 管道读取方式分为阻塞和不阻塞,不阻塞时,读取返回错误值
Ø 示例
l 源码
u 发送方 send.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define TMPPIPE "/tmp/tmppipe" // 管道文件名
int main(int argc, char **argv)
{
int fd;
char str[256] = "msg...";
if (argc > 1)
strcpy(str, argv[1]);
if ((fd = open(TMPPIPE, O_WRONLY)) != -1) // 以写方式打开管道文件
{
printf("send: %s/n", str);
write(fd, str, strlen(str) + 1); // 写数据
close(fd);
}
}
u 接收方 recv.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#define TMPPIPE "/tmp/tmppipe"
int main(int argc, char **argv)
{
int fd;
char str[256];
unlink(TMPPIPE); // 删除可能存在的原管道文件
mkfifo(TMPPIPE, 0666); // 建立管道文件
if ((fd = open(TMPPIPE, O_RDONLY)) != -1) // 以读方式打开管道文件
{
read(fd, str, 256); // 从管道中读取数据
printf("recv: %s/n", str);
close(fd);
}
}
l 编译
gcc send.c -o send
gcc recv.c -o recv
l 执行结果
在一个终端运行 recv 等待接收数据变化
另一个终端运行 send ,参数是要发送的字串(注意 send, recv 使用同一用户)
此时在 recv 终端看到收到发送过来的字串
Ø 参考
l 源码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/IPC/case%7C_pipe.tgz
例程十六_IPC_msg
Ø 功能
l 进程间通讯 (IPC)
l 具有较强的实时性
Ø 知识点
l 进程间相互发送信息,通常一方等待消息,另一方发送时,它立即收到
l 注意 send 和 recv 信息时的长度参数是不含类型长度的
Ø 示例
l 源码
u 发送方 send.c
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
long type; // message type
char data[256];
} msg_info;
int main(int argc, char **argv)
{
int msg_id;
int msg_type = 8;
msg_info info;
key_t key;
char str[256] = "msg...";
if (argc > 1)
strcpy(str, argv[1]);
if ((key = ftok("/home", 1)) == -1) // 生成 key ,第一个参数是某个路径(有时候用程序名),第二个参数一般指开的第几个共享内存
perror("ftok"), exit(-1);
if ((msg_id = msgget(key, IPC_CREAT | 0660)) == -1) // 打开共享内存句柄
perror("msgget"), exit(-1);
info.type = msg_type;
strcpy(info.data, str);
if (msgsnd(msg_id, &info, sizeof(msg_info) - sizeof(long), 0) == -1) // 发送信息,注意写入长度不含 type 长度
perror("msgsnd"), exit(-1);
else
printf("send: %s/n", info.data);
return 0;
}
u 接收方 recv.c
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
long type; // message type
char data[256];
} msg_info;
int main(int argc, char *argv[])
{
int msg_id;
int msg_type = 8;
key_t key;
msg_info info;
if ((key = ftok("/home", 1)) == -1)
perror("ftok"), exit(-1);
if ((msg_id = msgget(key, IPC_CREAT | 0660)) == -1)
perror("msgget"), exit(-1);
if (msgrcv(msg_id, &info, sizeof(msg_info) - sizeof(long), msg_type, 0) == -1) // 读取信息,注意读取长度不含 type 长度
perror("msgrecv"), exit(-1);
else
printf("recv: %s/n", info.data);
return 0;
}
l 编译
gcc send.c -o send
gcc recv.c -o recv
l 执行结果
在一个终端运行 recv 等待接收数据变化
另一个终端运行 send ,参数是要发送的字串
此时在 recv 终端看到收到发送过来的字串
Ø 参考
l 源码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/IPC/case%7C_msg.tgz
例程十五_IPC_shm
Ø 功能
l 进程间通讯 (IPC)
l 通常用于进程间共享和交换数据
Ø 知识点
l 两个不同进程通过 key 共享同一内存
Ø 示例
l 源码
u 发送方 send.c
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
int mem_id;
key_t key;
char str[256] = "msg...";
void *mem_addr;
if (argc > 1)
strcpy(str, argv[1]);
if ((key = ftok("/usr", 1)) == -1) // 生成 key ,第一个参数是某个路径(有时候用程序名),第二个参数一般指开的第几个共享内存
perror("ftok"), exit(-1);
if ((mem_id = shmget(key, 1024, IPC_CREAT)) == -1) // 打开 / 开辟共享内存
perror("shmget"), exit(-1);
if ((mem_addr = (void*)shmat(mem_id, 0, 0)) == (void*)(-1)) // 映射到变量
perror("shmat"), exit(-1);
memcpy(mem_addr, str, 256); // 向共享内存中写数据
printf("send: %s/n", str);
return 0;
}
u 接收方 recv.c
#include <sys/ipc.h>
#include <sys/msg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv)
{
int mem_id;
key_t key;
char str[256];
void *mem_addr;
if ((key = ftok("/usr", 1)) == -1)
perror("ftok"), exit(-1);
if ((mem_id = shmget(key, 1024, IPC_CREAT)) == -1)
perror("shmget"), exit(-1);
if ((mem_addr = (void*)shmat(mem_id, 0, 0)) == (void*)(-1))
perror("shmat"), exit(-1);
memcpy(str, mem_addr, 256); // 从共享内存中读数据
printf("recv: %s/n", str);
shmctl(mem_id, IPC_RMID, NULL); // 删除共享内存
return 0;
}
l 编译
gcc send.c -o send
gcc recv.c -o recv
l 执行结果
在终端执行 send 参数是你想发送的信息 ( 也可不带参,发送 msg…)
在终端执行 recv ,可以看到你之前发送的信息
Ø 参考
l 源码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/IPC/case%7C_shm.tgz
例程十四_IPC_mmap
Ø 功能
l 进程间通讯 (IPC)
l 实现简单,用于数据共享
Ø 知识点
l mmap 将某个文件内容映射到内存中,对该内存区(变量)存取即直接对该文件内容读写,两个进程映射同一文件,实现内存共享
Ø 示例
l 源码
u 发送方 send.c
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#define TMP_MAP_FILE "/tmp/mapfile" // 被映射的文件
#define TMP_MAP_SIZE 256 // 被映射的文件长度
main(int argc, char **argv)
{
int fd;
char *p_map;
char str[256] = "msg...";
if (argc > 1)
strcpy(str, argv[1]);
fd = open(TMP_MAP_FILE, O_CREAT | O_RDWR | O_TRUNC, 00777);
lseek(fd, TMP_MAP_SIZE, SEEK_SET);
write(fd, "", 1); // 若文件刚被创建,则长度为 0, 加入空字格,以扩展文件长度
p_map = (char *) mmap(NULL, TMP_MAP_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0); // 内存映射, MAP_SHARE 允许其它程序共享
close(fd);
strcpy(p_map, str); // 把内容复制到共享内存中
munmap(p_map, TMP_MAP_SIZE); // 解除映射
}
u 接收方 recv.c
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#define TMP_MAP_FILE "/tmp/mapfile"
#define TMP_MAP_SIZE 256
main(int argc, char **argv)
{
int fd;
char *p_map;
fd = open(TMP_MAP_FILE, O_CREAT | O_RDWR, 00777);
p_map = mmap(NULL, TMP_MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
printf("recv: %s/n", p_map);
munmap(p_map, TMP_MAP_SIZE);
}
l 编译
gcc send.c -o send
gcc recv.c -o recv
l 执行结果
在终端执行 send 参数是你想发送的信息 ( 也可不带参,发送 msg…)
在终端执行 recv ,可以看到你之前发送的信息
Ø 参考
l 源码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/IPC/case%7C_mmap.tgz
例程十三_GUI_抓背景图
Ø 功能
l 获取当前屏幕的背景(只含背景图)
l 主要用于软件中的特殊效果的图像合成
Ø 知识点
l 根窗口:用户桌面中垫在最下边的特殊窗口,它是背景图所在的窗口,一般由窗口管理器管理
l 背景图是窗口的一个属性,可由用户程序设置和获取
l gdk 对窗口的操作都是对 X 函数的封装,即都可以 XLib 中找到对应函数实现
l 背景图大小可能与屏幕大小不一致,所以在操作前可能需要叠放和拉伸
Ø 示例
l 源码
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
GdkDisplay *display = NULL;
GdkScreen *screen = NULL;
GdkWindow *window = NULL;
GdkColormap *cmap = NULL;
GdkPixmap *pixmap = NULL;
GdkPixbuf *pixbuf = NULL;
Pixmap *prop_data = NULL;
GdkAtom prop_type;
int w, h;
gtk_init(&argc, &argv);
screen = gdk_screen_get_default(); // 获取默认的 screen
display = gdk_screen_get_display(screen); // 获取 screen 对应的 display
window = gdk_screen_get_root_window(screen); // 获取根窗口
cmap = gdk_screen_get_default_colormap(screen); // 获取 screen 的色彩表
if (!gdk_property_get(window, // 获取窗口图片
gdk_atom_intern_static_string
("_XROOTPMAP_ID"),
gdk_x11_xatom_to_atom(XA_PIXMAP), 0, 10,
FALSE, &prop_type, NULL, NULL,
(guchar **) & prop_data))
return -1;
if ((prop_type == GDK_TARGET_PIXMAP) && prop_data && prop_data[0]) {
pixmap = gdk_pixmap_foreign_new_for_display(display,
prop_data
[0]);
gdk_drawable_set_colormap(pixmap, cmap);
gdk_drawable_get_size(pixmap, &w, &h); // 获取图片长宽
pixbuf =
gdk_pixbuf_get_from_drawable(NULL, pixmap, cmap,
0, 0, 0, 0, w, h); // 获取 pixbuf 数据
gdk_pixbuf_save(pixbuf, "bg.jpg", "jpeg", NULL, "quality",
"100", NULL); // 将图片存为 jpg 格式
g_object_unref(pixmap);
g_object_unref(pixbuf);
free(prop_data);
}
}
l 编译
g++ main.c -o main ` pkg-config gtk+-2.0 --libs --cflags`
l 执行结果
同目录下生成名为 screen.jpg 的图片文件
注意最好以根用户权限运行此程序
注意如果设背景为单色,而非图片,则无法获取图片
Ø 参考
l 源码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/GUI/case%7C_getbg.tgz
例程十二_GUI_抓屏
Ø 功能
l 获取当前屏幕显示内容(含背景图及各窗口图标的叠加)
l 主要用于抓屏
Ø 知识点
l 根窗口:用户桌面中垫在最下边的特殊窗口,它是背景图所在的窗口,一般由窗口管理器管理
l 使用 gdk_pixbuf_get_from_drawable 函数可抓取任一窗口的当前图像
l 可以修改本程序在抓图前加入 sleep(5) ,则程序可以 5 秒后自动抓图
Ø 示例
l 源码
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
int main(int argc, char *argv[])
{
GdkScreen *screen = NULL;
GdkWindow *window = NULL;
GdkPixbuf *pixbuf = NULL;
int w = -1;
int h = -1;
gtk_init(&argc, &argv);
screen = gdk_screen_get_default(); // 获取默认的 screen
window = gdk_screen_get_root_window(screen); // 获取根窗口
w = gdk_screen_get_width(screen); // 取得屏幕长宽
h = gdk_screen_get_height(screen);
pixbuf =
gdk_pixbuf_get_from_drawable(NULL, window, NULL,
0, 0, 0, 0, w, h); // 抓图
gdk_pixbuf_save(pixbuf,
"screen.jpg", "jpeg", NULL, "quality", "100",
NULL); // 将图片存为 jpg 格式
g_object_unref(pixbuf); //
}
l 编译
g++ main.c -o main ` pkg-config gtk+-2.0 --libs --cflags`
l 执行结果
同目录下生成名为 bg.jpg 的图片文件
注意最好以根用户权限运行此程序
Ø 参考
l 源码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/GUI/case%7C_screenshots.tgz
例程十一_GUI_用glade3做gtk界面
Ø 功能
l 使用 glade3 工具生成 gtk 界面
Ø 知识点
l glade3 与 glade2 差别
glade2 生成代码,而 glade3 生成 xml 文件
l 如何运行 glade3 生成的 xml 界面
借助 libglade 库读取 xml 文件,并生成控件
Ø 示例
l 用工具画界面
运行工具
$ glade-3
u 建立一个窗口 ( 左侧工具栏 -> 项层 -> 窗口 )
u 放置一个固定容器在窗口中(左侧工具栏 -> 容器 -> 固定的)
u 放置一个铵钮在容器中(左侧工具栏 -> 控制和显示 -> 按钮)
u 保存,名称为 test.glade
l 源码
在 test3.glade 的同级目录下,编写源码 main.c
#include <gtk/gtk.h>
#include <glade/glade.h>
int main(int argc, char **argv)
{
GladeXML *gxml;
GtkWidget *window;
gtk_init(&argc, &argv);
gxml = glade_xml_new("test.glade", NULL, NULL); // 读取 xml 文件
window = glade_xml_get_widget(gxml, "window1"); // 获取名为 window1 控件的指针
g_object_unref(G_OBJECT(gxml));
gtk_widget_realize(window);
gtk_widget_show(window);
gtk_main();
return 0;
}
l 编译
gcc main.c -o main `pkg-config --libs --cflags gtk+-2.0 libglade-2.0`
l 执行结果
./main
显示 gtk 建立的窗口
Ø 参考
l 程序下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_glade3.tgz
例程之十_GUI_建立X窗口
Ø 功能
l 用 X 函数直接建立窗口
l 通过此例程了解 X 窗体建立的过程,以及作为简单测试程序的雉形
Ø 知识点
l XOpenDisplay
参数为 NULL ,说要取得默认的 display
l XCreateWindow
建立 XWindow
l XMapWindow
显示 XWindow 相当于其它 GUI 中的 show 函数
l XFlush
使之前的操作立即生效
Ø 示例
l 源码
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
main()
{
Display *display;
Window window;
XSetWindowAttributes attr;
display = XOpenDisplay(NULL); // 取默认的 display
window = XCreateWindow(display, XDefaultRootWindow(display), // 建立窗口
100, 100, 300, 300, 2, XDefaultDepth(display, 0),
InputOutput, CopyFromParent, CWBackPixel, &attr);
XStoreName(display, window, "my test!"); // 设置窗口 title
XMapWindow(display, window); // 显示窗口( show )
XFlush(display); // 使以上动作立即生效
getchar(); // 等待用户输入
XDestroyWindow(display, window); // 销毁窗口
XFlush(display);
XCloseDisplay(display); // 关闭 display
}
l 编译
gcc -o main main.c -L/usr/X11R6/lib -lX11
l 执行结果
在一个终端运行 ./main ,即可看到弹出窗口
在该终端再输入一个字符回车,窗口关闭
Ø 参考
l 代码下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_xwin.tgz
l 详细资料
http://fanqiang.chinaunix.net/a4/b8/20010601/220800315.html
例程之九_GUI_获取窗口的进程PID
Ø 功能
l 获取窗口所在进程的 PID
Ø 知识点
l 得到默认的 display 指针: XOpenDisplay
l 通过字串取对应的 AtomID : XInternAtom
l 获取窗的某一属性: XGetWindowProperty
Ø 示例
l 源码
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <string.h>
#define MAX_PROPERTY_VALUE_LEN 4096 // 取得属性值最大长度
int main(int argc, char **argv)
{
Display *disp = XOpenDisplay(NULL); // 获取默认的 display 指针
Window win = NULL;
int ret = -1;
Atom xa_prop_name;
Atom xa_ret_type;
int ret_format;
unsigned long ret_nitems;
unsigned long ret_bytes_after;
unsigned long tmp_size;
unsigned char *ret_prop;
int id;
if (argc < 2)
{
printf("please input windowID, such as: ./main 0x240001e (use command xwininfo)/n");
return -1;
}
ret = sscanf(argv[1], "0x%0x", &win); // 从程序参数中获取 xwindow id
if (ret < 1)
{
printf("please input windowID, such as: ./main 0x240001e (use command xwininfo)/n");
return -1;
}
printf("xwindow id: 0x%0x/n", win);
xa_prop_name = XInternAtom(disp, "_NET_WM_PID", False); // 取对应字串的 AtomID
if (XGetWindowProperty(disp, win, xa_prop_name, 0, // 获取窗口属性
MAX_PROPERTY_VALUE_LEN / 4,
False, XA_CARDINAL, &xa_ret_type, // XA_CARDINAL 为数值类型
&ret_format, &ret_nitems, &ret_bytes_after,
&ret_prop) != Success) // 后五个参数是返回值
{
printf("Cannot get %s property./n", "_NET_WM_PID");
return NULL;
}
else
{
memcpy(&id, ret_prop, 4); // 类型传换
printf("window pid: %d/n", id);
}
}
l 编译
$ g++ main.cpp -o main ` pkg-config glib-2.0 --libs --cflags` -lX11
l 执行结果
u 在一个终端打开一个带窗口界面的程序
u 使用以下命令获得窗口的 xwindow id
$ xwininfo |grep “Window id”
运行命令后用鼠标点击程序窗口
u 运行我们编写的程序,并将十六进程的 xwindow id 作为参数传给程序
$ ./main 0x240001e
Ø 参考
l 程序下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_x11%7C_pid.tgz
例程之八_IPC_socket_tcp
Ø 功能
l 进程间通讯 (IPC)
Ø 知识点
l 通讯的实现:通过 socket 的 TCP 连接实现, C/S 架构,发送方为 client 端,接收端为 server 端, server 打开某个端口等待连接, client 连接 server ,以实现通讯和传输数据
l 通讯需要用户有足够的权限
l 通讯双方可以互传数据,但实现相对于其它通讯方式来说比较复杂
Ø 示例
l 源码
u 发送方 send.c
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#define MSG_PORT 5432 // socket 端口
int open_socket() // 打开 socket 端口
{
int sfd = -1;
int ret = -1;
struct sockaddr_in addr;
sfd = socket(AF_INET, SOCK_STREAM, 0); // 建立 TCP 连接
if (sfd <= 0)
return -1;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = 0;
addr.sin_port = htons(MSG_PORT);
ret = connect(sfd, (struct sockaddr *) &addr, sizeof(addr)); // 连接
if (ret >= 0)
return sfd;
close_socket(sfd);
return -1;
}
int close_socket(int sfd) // 关闭 socket
{
if (sfd >= 0)
close(sfd);
}
int main(int argc, char **argv)
{
int sfd = -1;
int ret = -1;
char str[256] = "default";
if (argc > 1)
strcpy(str, argv[1]);
sfd = open_socket();
if (sfd > 0) {
ret = write(sfd, str, strlen(str)); // 发送字串
printf("send: %s (%d)/n", str, ret);
close_socket(sfd);
} else {
perror("socket");
}
}
u 接收方 recv.c
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MSG_PORT 5432
int main(int argc, char *argv[])
{
int sfd, cfd;
struct sockaddr_in my_addr, peer_addr;
int peer_addr_size;
char str[256] = "";
int ret = -1;
sfd = socket(AF_INET, SOCK_STREAM, 0); // 建立 TCP 连接
if (sfd == -1)
{
perror("socket");
return -1;
}
memset(&my_addr, 0, sizeof(struct sockaddr_in));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = 0;
my_addr.sin_port = htons(MSG_PORT);
if (bind(sfd, (struct sockaddr *) &my_addr, // 绑定
sizeof(struct sockaddr_in)) == -1)
{
perror("bind");
return -1;
}
if (listen(sfd, 10) == -1) // 监听
{
perror("listen");
return -1;
}
peer_addr_size = sizeof(struct sockaddr_in);
cfd = accept(sfd, (struct sockaddr *) &peer_addr, &peer_addr_size); // 等待连接
if (cfd == -1)
{
perror("accept");
return -1;
}
while(ret <= 0)
{
ret = read(cfd, str, 256); // 读取数据
}
printf("recv: %s (%d)/n", str, ret);
if (ret < 0)
perror("recv");
close(cfd); // 关闭连接
close(sfd);
}
l 编译
gcc send.c -o send
gcc recv.c -o recv
l 执行结果
在一个终端运行 recv 等待接收数据变化
另一个终端运行 send ,参数是要发送的字串
此时在 recv 终端看到收到发送过来的字串
Ø 参考
l 例程下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_socket.tgz
例程之七_IPC_system_signal
Ø 功能
l 进程间通讯 (IPC)
Ø 知识点
l 通讯的实现:使用系统底层的信号机制
l 只能传输简单的信号,不能传输数据
l 供用户使用的信号只有 SIGUSR1 和 SIGUSR2 两个
l 使用方法十分简单
Ø 示例
l 源码
u 发送方 send.c
#include <stdio.h>
#include <signal.h>
int main(int argc, char **argv)
{
FILE *pp = NULL;
char str[256];
int pid = 0;
// 获取 recv 程序所对应的进程号
if ((pp = popen("ps -e|grep recv|awk '{print $1}'", "r")) != NULL) {
if (fgets(str, 25, pp) != NULL) {
pid = atoi(str);
if (pid > 0) {
printf("now send usr1 signal/n");
kill(pid, SIGUSR1); // 发送信号
}
}
pclose(pp);
}
return 0;
}
u 接收方 recv.c
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
static void killfun(int signo) // 信号回调函数
{
printf("recv kill signal %d/n", signo);
exit(1);
}
int main(void)
{
if (signal(SIGUSR1, killfun) == SIG_ERR) // 注册 usr1 信号的回调函数
perror("can't catch SIGUSR1");
while (1) { // 循环等待
printf("wait.../n");
usleep(500000);
}
}
l 编译
gcc send.c -o send
gcc recv.c -o recv
l 执行结果
在一个终端运行 recv 等待接收数据变化
另一个终端运行 send
此时在 recv 终端看到收到发送过来的信号
Ø 参考
l 例程下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_signal.tgz
例程之六_IPC_DBUS_signal
Ø 功能
l 进程间通讯 (IPC)
l signal 方式主要用于非阻塞的数据广播
(注意不要传大量数据,否则会引起消息阻塞)
Ø 知识点
l 通讯的实现: C/S 架构,用户进程为 client 端,后台运行着 dbus-daemon 进程为 server 端,用于侦测信息传输及时通知另一进程
l 基本概念:总线,服务,对象,接口,方法和信号
u 总线 (bus) :系统 (System bus) 、会话 (Session bus)
会话总线:同一次登录后用户的各个进程间通讯
系统总线:不同用户及与系统间通讯
u 服务 (service) :每个总线有一个或多个服务
以点分隔 , 至少两个点以避免重名,没层次关系,反序像 java
u 对象 (object) :每个服务有一个或多个对象
对反斜杠 ’/’ 分隔 , 像路径,只是名没层次关系
u 接口 (interface) :每个服务有一个或多个接口
以点分隔,一般接口和对象名差不多
u 方法 (method) 和信号 (signal)
上面都是对传输规则和传输目的的指定,方法和信号是用户真正输的数据
方法是阻塞的点对点的
信号是非阴阻塞的可广播的 ( 本例程主要介绍信号 )
l 一般程序不直接使用 D-Bus 的接口。 DBus-glib 是 GTK 版本的 dbus 接口封装,下面例程就是使用 DBus-glib 实现的
Ø 示例
l 源码
u 发送程序 send.c
#include <dbus/dbus.h>
#include <glib.h>
#include <string.h>
#include <stdlib.h>
#define TEST_OBJECT "/com/test/notifications"
#define TEST_INTERFACE "com.test.notifications"
#define TEST_SIGNAL "set_msg"
int main(int argc, char **argv)
{
DBusConnection *bus;
DBusError error;
DBusMessage *message;
char *p;
p = (char*) malloc (256 * sizeof(char));
strcpy(p, "default");
if (argc > 1)
strcpy(p, argv[1]);
dbus_error_init(&error);
bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (!bus) {
g_warning("Failed to connect to the D-BUS daemon: %s",
error.message);
dbus_error_free(&error);
return -1;
}
dbus_connection_setup_with_g_main(bus, NULL);
message = dbus_message_new_signal(TEST_OBJECT, TEST_INTERFACE, TEST_SIGNAL);
dbus_message_append_args(message,
DBUS_TYPE_STRING, &p, DBUS_TYPE_INVALID);
dbus_connection_send(bus, message, NULL);
dbus_message_unref(message);
free(p);
return 0;
}
u 接收程序 recv.c
#include <glib.h>
#include <dbus/dbus.h>
#include <stdio.h>
#define TEST_OBJECT "/com/test/notifications"
#define TEST_INTERFACE "com.test.notifications"
#define TEST_SIGNAL "set_msg"
static DBusHandlerResult
signal_filter(DBusConnection * connection, DBusMessage * message,
void *user_data)
{
GMainLoop *loop = user_data;
if (dbus_message_is_signal(message, TEST_INTERFACE, TEST_SIGNAL))
{
DBusError error;
char *s;
dbus_error_init(&error);
if (dbus_message_get_args
(message, &error, DBUS_TYPE_STRING, &s,
DBUS_TYPE_INVALID)) {
printf("dbus received: %s/n", s);
} else {
printf
("dbus received, but error getting message: %s/n",
error.message);
dbus_error_free(&error);
}
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
int main(int argc, char **argv)
{
GMainLoop *loop;
DBusConnection *bus;
DBusError error;
char str[256];
loop = g_main_loop_new(NULL, FALSE);
dbus_error_init(&error);
bus = dbus_bus_get(DBUS_BUS_SESSION, &error);
if (!bus) {
g_warning("Failed to connect to the D-BUS daemon: %s",
error.message);
dbus_error_free(&error);
return 1;
}
dbus_connection_setup_with_g_main(bus, NULL);
sprintf(str, "type='signal',interface='%s'", TEST_INTERFACE);
dbus_bus_add_match(bus, str, &error);
dbus_connection_add_filter(bus, signal_filter, loop, NULL);
g_main_loop_run(loop);
return 0;
}
l 编译
gcc send.c -o send `pkg-config --libs --cflags dbus-1 glib-2.0 dbus-glib-1 dbus-1`
gcc recv.c -o recv `pkg-config --libs --cflags dbus-1 glib-2.0 dbus-glib-1 dbus-1`
l 执行结果
在一个终端运行 recv 等待接收数据变化
另一个终端运行 send ,参数是要发送的字串
此时在 recv 终端看到收到发送过来的字串
Ø 参考
l 例程下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_dbus%7C_signal.tgz
例程之五_IPC_gconf
Ø 功能
l 配置及管理应用程序相关的数据,功能类似注册表
l 进程间通讯 (IPC)
Ø 知识点
l 通讯的实现: C/S 架构,用户进程为 client 端,后台运行着 gconfd-2 进程为 server 端,用于侦测配置文件的变化,及时通知另一进程(另一进程会注册它所关心值的回调函数,值改变时回调函数被调用)
l 存储配置的实现: gconf 是以配置文件方式存储,格式为纯文本的文件( Key/Value ),它存储在 $HOME/.gconf/ 目录下,所以只用于同一用户数据的交互
l 读取配置项时,可按类型直接读取值,也可以先读取数据项是何种类型
l 调试工具 gconf-editor ,使用它可以看到该用户的所有配置项及配置某项
Ø 示例
l 源码
u 发送方 send.c
#include <gconf/gconf-client.h>
#include <string.h>
#define TEST_ITEM "/extra/test/txt" // 配置关键字(相当于 key 值)
#define TEST_DIR "/extra/test" // 配置存放的上层目录,对应 $HOME/.gconf 下的目录结构
int main(int argc, char **argv)
{
GConfClient *client;
char str[256] = "default";
if (argc > 1)
strcpy(str, argv[1]); // 用程序参数作为设置值
gconf_init(argc, argv, NULL); // gconf client 端的初始化
client = gconf_client_get_default(); // 获得 gconf 句柄
gconf_client_set_string(client, TEST_ITEM, str, NULL); // 设置 String 类型的值
return 0;
}
u 接收方 recv.c
#include <gconf/gconf-client.h>
#include <stdio.h>
#define TEST_ITEM "/extra/test/txt"
#define TEST_DIR "/extra/test"
void key_changed_callback(GConfClient * client, // 配置值改变时被回调的函数
guint cnxn_id,
GConfEntry * entry, gpointer user_data)
{
gchar *str;
str = gconf_client_get_string(client, TEST_ITEM, NULL); // 获得 String 型的设置值
if (str) {
printf("recv: %s/n", str);
g_free(str);
}
}
int main(int argc, char **argv)
{
GConfClient *client;
static GMainLoop *main_loop = NULL;
gconf_init(argc, argv, NULL);
client = gconf_client_get_default();
gconf_client_add_dir(client, // 设置上层目录,以备侦测配置值的变化
TEST_DIR, GCONF_CLIENT_PRELOAD_NONE, NULL);
gconf_client_notify_add(client, TEST_ITEM, // 注册回调函数
(GConfClientNotifyFunc)
key_changed_callback, NULL, NULL, NULL);
main_loop = g_main_loop_new(NULL, FALSE); // gconf 需要依赖 glib 的主循环
g_main_loop_run(main_loop);
g_main_loop_unref(main_loop);
return 0;
}
l 编译
gcc -o send send.c `pkg-config --cflags --libs gconf-2.0`
gcc -o recv recv.c `pkg-config --cflags --libs gconf-2.0`
l 执行结果
在一个终端运行 recv 等待接收数据变化
另一个终端运行 send ,参数是要发送的字串
此时在 recv 终端看到收到发送过来的字串
Ø 参考
l 例程下载
http://cid-f8aecd2a067a6b17.skydrive.live.com/self.aspx/.Public/case%7C_gconf.tgz
例程之四_GUI_GTK三维效果的实现
Ø 功能
l 使用 clutter 在 Gtk 窗口中实现三维效果,通常用于过渡效果和三维界面
Ø 知识点
l clutter 原理
clutter 底层调用 opengl 、 glx 等库,实现三维效果,而向上层提供了统一简单 ( 比 opengl 简单很多 ) 的接口,支持常用的 GUI(gtk/qt 等 )
l clutter 中 stage( 舞台 ) 和 actor( 演员 ) 的概念
每个 gtk window 可以包含一个或多个 stage ,每个 stage 有它的大小,颜色等属性
一个 stage 里又包含一个或多个 actor , actor 来实现具体动画
l 总在顶层显示
clutter 所在的窗口应该是最顶层的,因为在动画过程中, clutter 产生的画面将盖在其它任何窗口之上
Ø 示例
l 环境
ubuntu 8.04 系统,安装 clutter-0.8.0.tar.bz2 和 clutter-gtk-0.8.0.tar.gz 包
(尽量安装源码包,因为其中包括一些例程,可以参考)
l 源码
#include <gtk/gtk.h>
#include <clutter/clutter.h>
#include <clutter-gtk/gtk-clutter-embed.h>
#include <clutter-gtk/gtk-clutter-util.h>
static gboolean rotate_timeout(ClutterActor * image) // 动画的实现函数,被定时器回调
{
static int rotate = 0; // 角度
rotate += 5;
if (rotate > 360)
rotate = 0;
clutter_actor_set_rotation(image, CLUTTER_X_AXIS, rotate, 0, 0, 0); // 绕X轴旋转,后三个参数分别是旋转轴心的 x, y, z 值
return TRUE;
}
int main(int argc, char *argv[])
{
ClutterActor *stage1, *tex1, *tex2;
GtkWidget *window, *clutter;
GdkPixbuf *pixbuf;
if (gtk_clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS) // 初始化 clutter
g_error("Unable to initialize GtkClutter");
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit),
NULL);
clutter = gtk_clutter_embed_new(); // 建立 clutter 实例
gtk_widget_set_size_request(clutter, 320, 240); // 设定 clutter 大小
stage1 = gtk_clutter_embed_get_stage(GTK_CLUTTER_EMBED(clutter)); // 取 stage
tex1 = clutter_texture_new_from_file("bg.jpg", NULL); // 读取背景图片,此函数在 clutter-0.8.0 以上版本中使用的, 0.6.0 此功能使用函数 clutter_texture_new_from_pixbuf
clutter_actor_set_position(tex1, 0, 0); // 设置 actor 相对于 stage 位置
clutter_stage_add(stage1, tex1);
clutter_actor_show(tex1);
tex2 = clutter_texture_new_from_file("img.jpg", NULL); // 读取被旋转的图片
clutter_actor_set_position(tex2,
(320 - clutter_actor_get_width(tex2)) / 2, 120); // 设置图片旋转位置
clutter_group_add(CLUTTER_GROUP(stage1), tex2);
gtk_container_add(GTK_CONTAINER(window), clutter); // 把 clutter 加入 gtk window
gtk_widget_show_all(window);
clutter_actor_show_all(stage1);
g_timeout_add(150, (GSourceFunc) rotate_timeout, tex2); // 加入定时器,以旋转
gtk_main();
return 0;
}
l 编译
g++ main.cpp -o main ` pkg-config gtk+-2.0 clutter-gtk-0.8 --libs --cflags`
l 执行结果
后面显示背景图,前面的画面绕X轴旋转
Ø 参考
l clutter 介绍
http://jserv.sayya.org/clutter/clutter-overview.pdf
l 完整例程下载(含图片及 Makefile )
http://cid-f8aecd2a067a6b17.skydrive.live.com/browse.aspx/.Public?uc=1&isFromRichUpload=1
下载 case_gtkclutter.tgz
例程之三_GUI_无边框GTK窗口的拖拽
Ø 功能
l 无标题栏 ( 无边框 )GTK 窗口的拖拽
Ø 知识点
l 窗口内部接收和处理鼠标拖动事件,同时移动窗口位置
Ø 示例
l 源码
#include <gdk/gdkcursor.h>
#include <gtk/gtk.h>
#define TEST_W 100
#define TEST_H 80
gboolean drag = FALSE; // 只在左键按下时拖动窗体
int nX = 0;
int nY = 0;
static gint button_press_event(GtkWidget * widget,
GdkEventButton * event, gpointer data)
{
if (event->button == 1) // 判断是否左键按下
{
drag = TRUE;
nX = event->x; // 取得鼠标相对于窗口的位置
nY = event->y;
}
return TRUE;
}
static gint button_release_event(GtkWidget * widget, GdkEventButton * event,
gpointer data) // 鼠标抬起事件
{
if (event->button == 1)
drag = FALSE;
return TRUE;
}
static gint motion_notify_event(GtkWidget * widget, GdkEventButton * event,
gpointer data) // 鼠标移动事件
{
if (drag)
{
int x, y;
GtkWidget *window = (GtkWidget *) data;
gtk_window_get_position((GtkWindow *) window, &x, &y); // 取窗体绝对坐标
gtk_window_move((GtkWindow *) window, x + event->x - nX,
y + event->y - nY); // 移动窗体
}
return TRUE;
}
int main(int argc, char **argv)
{
GtkWidget *window;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_decorated(GTK_WINDOW(window), FALSE); // 去掉边框
gtk_widget_set_size_request(window, TEST_W, TEST_H);
gtk_widget_set_events(window, // 设置窗体获取鼠标事件
GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
gtk_signal_connect(GTK_OBJECT(window), "button_press_event",
(GtkSignalFunc) button_press_event, window); // 加入 事件回调
gtk_signal_connect(GTK_OBJECT(window), "motion_notify_event",
(GtkSignalFunc) motion_notify_event, window);
gtk_signal_connect(GTK_OBJECT(window), "button_release_event",
(GtkSignalFunc) button_release_event, window);
gtk_widget_show_all(window);
gtk_main();
return TRUE;
}
l 编译
gcc main.cpp –o main `pkg-config gtk+-2.0 –libs –cflags`
l 执行结果
显示窗口,按下鼠标左键时可以拖动
例程之二_Debug_显示当前函数调用关系
Ø 功能
l 程序异常退出时,显示当时的函数调用关系
l 显示当前函数正在被哪个函数调用
Ø 知识点
l backtrace 函数显示当前进程堆栈信息(当时函数调用关系)
Ø 示例
l 源码
#include <stdio.h>
void xy_print_backtrace(void)
{
void *bt[500];
int i = 0;
int bt_size;
char **syms;
bt_size = backtrace(bt, 500); // 获取当前堆栈信息,最多 500 层函数
syms = (char**)backtrace_symbols(bt, bt_size); // 把堆栈信息转化成字符串数组
while (i < bt_size)
{
printf("%s/n", syms[i]);
++i;
}
free(syms);
}
void xy_level2()
{
xy_print_backtrace();
}
void xy_level1()
{
xy_level2();
}
int main(int argc, char **argv)
{
xy_level1();
}
l 编译
gcc main.c -o main –rdynamic
// 注意加编译参数-rdynamic ,才能正常使用backtrace
l 执行结果
./main(xy_print_backtrace+0x26) [0x80486ba]
./main(xy_level2+0xb) [0x804870e]
./main(xy_level1+0xb) [0x804871b]
./main(main+0x16) [0x8048733]
/lib/tls/i386/cmov/libc.so.6(__libc_start_main+0xe0) [0xb7dcf450]
./main [0x8048631]
例程之一_Debug_规范化打印信息
Ø 功能
l 规范化程序打印信息
l 打印当前运行程序所对应的源码位置
l 统计程序运行时间
Ø 知识点
l 接收可变参数 (va_start, vfprintf, va_end)
l 打印当前位置的源码文件名及行数
l 获取当前时间 ( 微秒 )
Ø 示例
l 源码
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
#include <sys/time.h>
#define _DEBUG // 方便显示或隐藏打印信息
void xy_debug(const char *format, ...) // 接收可变参数
{
#ifdef _DEBUG
va_list args;
time_t timep;
struct tm *p;
struct timeval tv;
if (format == NULL)
return;
time(&timep); // 取当前时间(年月日时分秒)
p = localtime(&timep); // 获取对应本地时区的时间
gettimeofday(&tv, NULL); // 获取微秒时间
fprintf(stderr, "[%d:%d:%d.%06d] ", p->tm_hour, p->tm_min, p->tm_sec,
tv.tv_usec); // 分别使用两个函数获取时间,省略除法计算,节约运行时间
va_start(args, format); // 接收可变参数
vfprintf(stderr, format, args);
va_end(args);
fflush(stderr); // 同步缓冲区,使程序立即显示
#endif
}
main()
{
xy_debug("(%s:%d) test_%d/n", __FILE__, __LINE__, 100); // 用法如同 printf
// __FILE__ 当前文件名的宏定义, __LINE__ 当前行数的宏定义
}
l 编译
gcc main.c -o main
l 执行结果
[22 : 3 : 56.100282] ( main.c:35 ) test_100