045_远程管理Linux系统工具(C++)
1.无需注册登录,支付后按照提示操作即可获取该资料.
2.资料以网页介绍的为准,下载后不会有水印.资料仅供学习参考之用.
密 惠 保
1.3 本课题研究的意义
本课题是为了给系统管理员提供一种远程管理Linux中某些主要服务的工具软件,完成服务配置管理和查看一些重要的数据。由于Linux是开源项目的代表,对Linux的学习有助于对计算机系统和软件理论知识的提高。通过该课题的设计和实现,既解决远程管理Linux系统,又让自己掌握了一些Linux系统编程和Sock编程能力。
1.4 本课题的研究方法
本课题主要是对网络配置、GTK库、Linux系统调用和网络编程方面的研究。采用C/S模式,在客户端采用GTK函数实现用户界面的编写,在服务器端通过Linux的系统调用,来实现本地服务配置,然后再通过Sock编程来实现服务器和客户端的通信,传递配置内容和服务指令。
2 GTK编程
2.1 GTK简介
GTK(GIMP Toolkit,GIMP工具包)是一个用于创造图形用户接口的多平台工具。它包含有基本的空间和一些很复杂的控件:例如文件选择控件。GTK最初是GIMP(GNU图形处理程序)开发的控件集合,然后它不断扩展,直到今天被用于大量的应用程序当中。
【买计算机毕业论文就到www.think58.com】 本文来自think58
[资料来源:http://THINK58.com]
从根本上来说,主循环是由glib实现的。Gtk将glib主循环连接到Gdk的X服务器,并提供一个方便的接口。
Gtk分别用gtk_main()函数和gtk_main_quit()函数来运行主循环和退出主循环,当系统调用了gtk_main()函数后,由于gtk_main()函数可以递归调用,所以需要调用gtk_main_quit()函数才能退出gtk。
gtk_main()函数的所有功能都是监视Gtk程序和与X服务的连接,对同样的时间队列起作用。gtk_main()函数用于阻塞该进程直到满足某些条件。所有的Gtk程序都用这个技巧使应用程序正在运行时main()函数被阻塞,直到用户通过点击鼠标或键盘来产生一个信号,并通过该信号来调用相应的回调函数。
GTK是一个时间驱动工具包,它将在gtk_main()函数中一直等待,直到某个事件的发生或控制权被交给相应的函数。
(1) 信号
控制权的传递是使用“信号”的方法。一旦事件发生,比如鼠标器按钮被按下,被按下的构件(按钮)将引发适当的信号。有一些信号是大多数构件都具备的,比如destory,还有一些是某些构件专有的,比如在按扭的toggled信号。要让一个按钮执行一个操作,我们需要写一段信号处理程序,以捕获它的信号,然后调用相应的回调函数。
这由类似以下所示的函数实现:
Gint gtk_signal_connect(GtkObject *object,
Gchar *name,
GtkSignalFunc func,
gpointer func_data );
上面函数的第3个参数为回调函数,它的形式通常是:
void callback_func( GtkWidget *widget, gpointer callback_data );
(2) 事件
除了上面的信号机制外,还有一些和X事件机制相对应的事件。回调函数也可以和这些事件连接起来应用。将回调函数连接到X的某一个事件,需要使用gtk_signal_connect函数,并使用事件名称作为命名参数。事件的回调函数与信号的回调函数在形式上略有不同:
Void func(GtkWidget *widget,
GdkEvent *event,
gpointer callback_data );
GdkEvent是C中的联合体结构,其类型依赖于发生的事件。要想知道哪一个事件已经引发,可以查看类型参数,因为每个可能的可选事件都有一个反映引发事件的类型参数。将回调函数与一个事件连接起来,需要使用以下形式的函数:
gtk_signal_connect( GTK_OBJECT(button),
"button_press_event"
GTK_SIGNAL_FUNC(button_press_callback),
NULL) ;
这里假定button是一个按钮构件。现在,当鼠标移动到按钮上方,鼠标按钮按下时,将调用button_press_callback函数。回调函数可以作如下声明: think58.com
[资料来源:http://think58.com]
static gintbutton_press_callback( GtkWidget *widget,
GdkEventButton *event,
gpointer data);
2.2 常用的GTK构件
GTK构件可以分为两种。一种有一个相关联的GdkWindow窗口,另一种没有。绝大多数构件都是第一种构件,并且可以显示在GdkWindow窗口。
(1) GtkWindow构件
GtkWindow构件是最大的容器,它实际就是一个窗体构件。但是它只可以容纳一个子构件,所以要让GtkWindow能容纳更多的构件必须使用布局控件来布局。
用下面的函数创建新窗口:
GtkWidget* gtk_window_new (GtkWindowType type);
用下面的构件向窗口中添加子构件:
gtk_container_add (GTK_CONTAINER (window), widget);
(2) 组装构件
组装构件通常是直接从GtkContainer派生而来。这些构件可以有多个子构件,它们的作用就是管理布局。“管理布局”意味着这些容器为它们容纳的子构件分配大小尺寸和位置。例如,GtkVBox将它的子构件在一个垂直的栈内排列。GtkTable构件可以让构件在一个表格上根据单元格定位。
GtkTable(表格构件)是很常用的用于定位的构件。我们用表格构件创建一个网格,把构件放在网格里。构件可以在网格中占据任意多个格子。 [资料来源:http://think58.com]
用gtk_table_new创建一个表格构件:
GtkWidget *gtk_table_new( gint rows,gint columns,gint homogeneous);
要将构件放到表格中,可以使用下列函数:
void gtk_table_attach(GtkTable *table,
GtkWidget *child,
gint left_attach,
gint right_attach,
gint top_attach,
gint bottom_attach,
gint x options,
gint yoptions,
gint xpadding,gint ypadding );
(3) 按钮构件
GtkButton(普通按钮构件)是应用程序中使用最广泛的构件。它一般用于当用户点击它时执行某个动作,其使用和创建也相当简单。
可以用gtk_button_new_with_label()创建带标题的按钮。按钮创建以后就可以用上面所讲述的GTK信号和回调函数或事件机制来实现,当按扭按下或有其他操作时候的回调函数来响应相应的函数或事件。
(4) 文本构件
GtkText(文本构件)允许多行显示或编辑文本。它支持多种颜色以及多种字体的文本,允许它们以任何需要的形式混合显示,还有许多与Emacs兼容的文本编辑命令。文本构件支持完全的剪切/粘贴功能,还包括双击选择一个单词和单击选择整行的功能。
创建新Text构件只有一个函数:
GtkWidget *gtk_text_new( GtkAdjustment *hadj,GtkAdjustment *vadj );
think58好,好think58 [来源:http://www.think58.com]
文本构件有两个主要用途:允许用户编辑一段文本,或向用户显示多行文本。为了在两种操作模式之间进行切换,文本构件有以下函数:
void gtk_text_set_editable( GtkText *text,gint editable );
为了在当前插入点插入文本,可以使用gtk_text_insert函数。插入时可以指定文本的背景色、前景色和字体。
void gtk_text_insert(GtkText *text,
GdkFont *font,
GdkColor *fore,
GdkColor *back,
const char *chars,
gint length );
(5) 标签构件
GtkLabel(标签构件)是GTK中最常用的构件之一,实际上它很简单。因为没有相关联的X窗口,标签构件不能引发信号。如果需要引发信号,可以将它放在一个事件盒构件中,或放在按钮构件中。用以下函数创建新标签构件:
GtkWidget *gtk_label_new(char *str );
唯一的参数是要由标签显示字符串。创建标签构件后,要改变标签内的文本,用以下函数:
void gtk_label_set_text( GtkLabel *lacbel,char *str );
第一参数是前面创建的标签构件(用GTK_LABEL()宏转换),第二个参数是新字符串。如果需要,新字符串所需的空间会做自动调整。在字符串中放置换行符,可以创建多行标签。
本文来自think58
[资料来源:http://think58.com]
(6) 笔记本构件
GtkNotebook(笔记本构件)是互相重叠的页面集合,每一页都包含不同的信息,且一次只有一个页面是可见的。该构件在GUI(图形用户接口)编程中很常用。要说明大量的相关信息,同时把它们分别显示时,使用这种构件是一个很好的方法。许多应用程序的“选项”对话框都使用了这个构件。用下面的函数可以创建新笔记本构件。
GtkWidget *gtk_notebook_new( void );
向笔记本构件中添加页面,主要有两种方法,而且非常相似的,如下:
在笔记本构件中追加页面:
void gtk_notebook_append_page(GtkNotebook *notebook,
GtkWidget *child,
GtkWidget *tab_label );
在笔记本构件中前插页面:
void gtk_notebook_prepend_page(GtkNotebook *notebook,
GtkWidget *child,
GtkWidget *tab_label );
其中child参数是放在笔记本构件上的子构件,tab_label是要添加的页面的标签。子构件必须分别创建,一般是一个容器构件,比如说表格构件。
(7) 分栏列表构件
GtkCList(分栏列表构件)是GtkList(列表构件)的替代品,但它提供更多的特性。分栏列表构件是多列列表构件,它有能力处理数千行的信息。每一列都可以有一个标题,而且可以是活动的,还可以将函数绑定到列选择上。
copyright think58 [资料来源:www.THINK58.com]
创建GtkClist构件的方法和创建其他构件的方法类似。因为GtkCList可以有多列,因而在创建它之前,必须确定要创建的列表的列数。创建分栏列表的函数:
GtkWidget *gtk_clist_new ( gint columns );
GtkWidget *gtk_clist_new_with_titles(gint columns,
gchar *titles[] );
创建列表后,需要向构件中添加一些要显示和操作的数据,用下面的函数可以向列表中添加一些数据行:
gint gtk_clist_prepend( GtkCList *clist,gchar *text[] );
gint gtk_clist_append( GtkCList *clist,gchar *text[] );
用下面的函数可以删除一些数据行:
void gtk_clist_remove( GtkCList *clist,gint row );
与其他构件一样,GtkCList有一些信号供我们使用。GtkCList构件是从容器构件GtkContainer派生的,它有容器所具有的一些信号,还有下面这些附加信号:
select_row:选中一行时引发,该信号传递信息,依次是GtkCList *clist、gint row、gint column、GtkEventButton *event。
unselect_row:用户对一行取消选择,引发这个信号。传递的信息与上一个信号一样。
click_column:选中某一列时引发,传递信息,依次是: GtkCList *clist、gint column。所以,要将一个回调函数连接到select_row信号上,回调函数应该像下面这样: think58.com
[资料来源:http://www.THINK58.com]
void select_row_callback(GtkWidget *widget,gint row,gint column,
GdkEventButton *event,gpointer data);
回调函数用下面的形式连接到信号:
gtk_signal_connect(GTK_OBJECT( clist),
"select_row",
GTK_SIGNAL_FUNC(select_row_callback),
NULL);
以上只是简单介绍了本课题在客户端界面编写的时候可用到的GTK库函数,但实际的GTK比上面的操作还要复杂得多。
3 Linux系统调用
3.1 Linux系统文件
在Linux中最主要的资源就是文件,很多设备都是以文件形式存在的,所以大多数输入/输出都要通过文件读写来实现。也就是说通过一个单一的接口就可以处理外围设备和程序之间的通信。Linux文件类型常见的有:普通文件、目录、字符设备文件、块设备文件、符号链接文件等。很多系统服务和应用服务的配置文件都是以普通文件的形式存在,这些文件很容易通过Linux的系统调用来配置和修改,也可以用文档文件的修改方式来修改。
3.2 如何从文件中读取数据
Linux文件读取有两种方法:一种是通过C语言的标准库函数调用来完成;另一种是通过对Linux的系统调用来完成。文件读取分3步完成:第一步是打开要读取的文件;第二步是把文件读取到内存中去;第三步是关闭打开的文件。
think58.com
[资料来源:http://think58.com]
在Linux中打开一个文件可以通过系统调用open()函数来实现:
int fd = open(char *name,int how);
其中参数name是要打开的函数名字;how是打开的方式;返回值-1为错误,成功就返回一个文件描述符。
Linux系统读取文件内容通过系统调用read()函数来实现,函数原形:
ssize_t numread = read(int fd,void *buff,size_t qty);
第1个参数是打开的文件描述符,第2个参数是存储文件数据的内存地址,第3个参数是读取数据大小。如果读取成功返回值是读取的字节数,失败就返回-1。
系统调用write()函数将内存中的数据写入到文件中去,函数原形:
ssize_t resut = write(int fd,void *buff,zize_t amt);
这个系统将调用buff的数据写到文件中。第1个参数是打开的文件描述符;第2个参数是要写入文件数据的内存地址;第3个参数是写入文件的大小。
进程不需要再对文件进行读写操作时,就要关系文件打开的文件。close()能关闭打开的文件,其函数原形:
int result = close(int fd);
这个系统调用会关闭进程和文件之间的连接。参数是打开的文件描述符号。关闭成功返回0,失败返回-1。
3.3 进程和线程的使用
(1) Linux进程
Linux进程创建很特别。很多其他操作都提供了产生进程的机制,在新的进程空间中创建进程,读取可执行文件。但是Linux不同,它创建的子进程具有和父进程相同的数据、代码段。父进程和子进程间的区别就是进程的pid不同,其他的都一样:
pid_t result = fork(void)
该函数比较特殊,它返回两次函数值,一次返回值是向父进程返回子进程的PID。还有一次是返回0(子进程)。
在使用fork()函数创建进程的时候经常父进程等待子进程结束。系统调用wait(&status)让父进程阻塞直到子进程结束:
pid_t result = wait(&status);
(2) Linux线程
进程为线程提供了运行环境,多个函数可以同时运行,但是他们都是运行在相同的进程中的。pthread_cearte()函数可以创建一个线程:
int pthread_ceate(pthread_t *thread,
pthread_attr_t *attr,
void *(*func)(void*),
void *arg);
4 基本的套接口编程
4.1 概述
让同一网络的不同计算机的进程能够相互通信,首先要确定这些程序如何进行通信。本课题所用的协议是TCP/IP协议。如果客户和服务器处于同一以太网,如图1所示。 本文来自think58 [资料来源:http://www.THINK58.com]
本文来自think58
[资料来源:www.THINK58.com]
think58
copyright think58 [资料来源:http://think58.com]
think58好,好think58 [资料来源:http://think58.com]
[资料来源:http://www.THINK58.com]
[资料来源:http://think58.com]
图1 网络示意图
4.2 简单的网络编程
(1) socket函数
socket函数功能是创建一个套接口描述符,并且指定希望的通信协议(使用IPV4的TCP,或者使用IPV6的UPD等)。
int socket(int family,int type,int protocol);
其中family参数指明协议族,type参数指明套接口类型,protocol参数为某个协议的常数。函数调用成功时候返回一个非负的描述符。
(2) connect函数
TCP客户程序用connect函数来发起和TCP服务程序的连接。这个函数通常是在客户程序中使用。
int connect(int sockfd,
const sgruct sockaddr *servaddr,
socklen_t addrlen);
sockfd是有socket函数返回的一个套接口描述符,第2个和第3个参数分别是一个指向套接口地址结构指针和结构的大小。当函数调用成功的时候返回0,出错就返回-1。
(3) bind函数
该函数是把一个本地协议地址赋予一个套接口,对于网际协议,协议地址是一个32位的IPV4地址和一个16位的TCP端口号的组合。
int bind(int sockfd,const struct aockaddr *myadd,socklen_t addrlen);
第2个参数是一个指向特定协议的地址结构的指针,第3个参数是该地址结构的长度。调用成功返回0,错误返回-1。 think58好,好think58 [资料来源:http://THINK58.com]
(4) listen函数
listen函数只能由TCP服务器调用,他完成两件事:
一是当socket函数创建一个套接口时它是一个主动的套接口,该函数就是把它转换成一个被动的套接口
二是该函数规定了为相应套接口排队的最大的连接个数。
int listen(int sockfd,int backlog);
本函数通常都是在调用socket和bind这两个函数之后。
(5) accept函数
int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
参数cliaddr和addrlen用来返回已经连接的客户端的协议地址。函数返回值为一个非负的描述符表示成功,出错为-1。
这里需要指出的是参数的描述符为监听套接口,就是在等待连接的时候还没有建立连接的套接口,而返回值是连接的新的套接口。也就是能和客户端通信的套接口。区分这两个东西是很重要的。
(6)close函数
套接口编程通常都需要一个close函数来关闭套接口,来中断TCP的连接。
int close(int sockfd);
(7)send()函数
当连接建立了后自然就需要数据的传送和接收,send()函数是用来想连接的另外一端发送数据。
int send(int sockfd,void *buf,int len,int flags);
第1个参数是已经连接的套接口的描述符,第2个参数是发送的数据的内存地址,第3个参数是发送数据的大小,第4个参数是选项。 [资料来源:http://think58.com]
(8)recv()函数
数据发送到接收端了后,就可以用recv()函数来接收由发送端发送过来的数据了。
int recv(int sockfd,void *buf,int len,int flags)
第1个参数是已经连接的套接口的描述符,第2个参数是发送的数据的内存地址,第3个参数是发送数据的大小,第4个参数是选项。
5 程序设计和流程图
5.1 设计思路
本课题是采用C/S模式。客户程序功能是界面的编写,并和服务程序通信。采用GTK编写界面,采用Sock编程实现通信。服务器程序功能是接收客户程序的数据,并且通过收到的数据来完成系统或应用服务程序文件的配置。在Linux中服务配置文件主要是以文本文件的形式存在的,所以通过Linux系统调用很容易修改配置文件,流程如图2。
内容来自think58
[来源:http://think58.com]
think58 [来源:http://think58.com]
copyright think58
[资料来源:www.THINK58.com]
[资料来源:www.THINK58.com]copyright think58 [资料来源:THINK58.com]
内容来自think58 [来源:http://www.think58.com]
内容来自think58
图2 程序总体流程
5.2 服务程序
5.2.1 流程设计
服务程序功能有两个:一是监听端口,接收数据和向客户程序发送数据;二是根据客户程序发送过来的数据对文件修改,其流程图如图3所示。
图3显示了服务程序的流程,accept()是监听端口,等待客户程序的连接,连接建立好后init_clinet()函数从配置文件读取服务器的当前配置文件数据,然后再发送到客户程序,让客户程序通过这些数据来初始化界面。然后再接收客户程序的数据(流程控制字符串),通过判断字符串来控制服务程序的流程(图3中的虚线部分)。
[资料来源:www.THINK58.com]
think58.com [资料来源:http://THINK58.com]
[版权所有:http://think58.com]
think58 [资料来源:http://www.THINK58.com]
[来源:http://www.think58.com]think58
think58.com
内容来自think58 [资料来源:http://think58.com]
think58好,好think58
[资料来源:www.THINK58.com]
think58.com
本文来自think58
[资料来源:www.THINK58.com]
think58.com [资料来源:http://think58.com]
内容来自think58 [资料来源:www.THINK58.com]
图3 服务程序的流程图
5.2.2 功能模块
(1) 系统用户管理操作
和客户程序建立了连接,并且收到了user字符串后,服务程序进入系统用户管理操作模块,接着接收客户程序发送过来的数据(字符串),通过数据来判断是对用户的添加、删除、浏览或修改操作。接收到了“adduser”字符串就进入添加用户流程;接收到“deluser”字符串就进入删除用户流程;接收到“scanuser”字符串就进入了浏览用户流程;接收到“property”字符串就进入到了修改用户流程,系统用户管理操作流程如图4所示。