删除ubuntu冗余的开机启动菜单

27 8 月, 2011 by edsionte 2 comments »

相信很多ubuntu的使用者对10.04情有独钟,因为该版本是一个长期支持版(Long-Term Support,LTS)。不过随着系统的更新,开机启动菜单会出现多个内核版本,这样看起来很不清爽而且还占用磁盘空间。本文将描述如何清理这些冗余的内核版本。

删除系统内多余的内核

1.查看当前系统中的内核

我们先查看当前系统中存在那些内核版本。使用如下命令即可查询:

edsionte@edsionte-desktop:~/桌面$ dpkg --get-selections | grep "linux-image"
linux-image-2.6.32-21-generic			install
linux-image-2.6.32-32-generic			install
linux-image-2.6.32-33-generic			install
linux-image-generic				install

dpkg是Debian Linux的软件包管理系统,选项–get-selections即得到匹配包的状态,包的匹配可以由gerp来完成。关于dpkg的更多内容本文稍候将详细说明。

2.查看当前系统的版本

删除其他多余内核版本时,先查看当前系统的版本号以免误删系统。使用uname命令即可查看。

edsionte@edsionte-desktop:~/桌面$ uname -r
2.6.32-33-generic

3.删除冗余的内核版本

当前的内核版本是2.6.32-33,我们删除其他的内核版本即可,使用apt-get remove命令即可。

edsionte@edsionte-desktop:~/桌面$ sudo apt-get remove linux-image-2.6.32-21-generic linux-image-2.6.32-32-generic

然后再使用apt-get autoremove命令可以自动删除/usr/src下的源码头文件目录。重新系统后,可以看到在启动菜单中只剩一个内核版本了。

ubuntu软件包管理器

在上述清楚内核版本的过程中,我们用到了dpkg和apt-get两个包管理工具,这两个软件包管理工具有什么区别呢?

我们常用的apt-get是一个命令式的软件包管理器,该管理器从网络上下载所需软件包,并且解决软件包之间的依赖关系。比如,我们要下载安装软件包A,如果A软件包需要B软件包的支持,那么apt-get install的时候也会同时下载并安装B软件包。

而dkpg(Debian PacKaGe)则是一个底层的软件包管理器,它只用来管理本地的软件包。比如使用dpkg -i yourpkg.deb即可在本地安装软件。

关于这两个包管理器的使用方法可参见这里

基于Socket API的C/S通信:以树形结构显示服务器端目录(2)

11 8 月, 2011 by edsionte 无评论 »

3.客户端的实现

由于客户端采用面向对象的Qt来编码,因此客户端比较容易实现。客户端的主要功能是:以列表形式显示指定目录下的文件以及相关属性;当用户点击文件列表中的某个目录时进入该子目录,并显示子目录下的文件列表;可以返回到当前目录的父目录。

根据上述需求,客户端的界面设计可以参考下图,即通过一个QTableWidget来显示某个目录下的所有文件,该部件的使用方法可以参考Qt Assitant。

接下来重点说明通信部分的功能实现。在服务器程序中,服务器向客户端发送的数据有两种,一种即为文件数,另一种则为所有文件信息形成的字节流。那么在客户端中则相应的有接受函数recvDirInfo()。

void configShowUI::recvDirInfo()
{
    //1.先接受指定目录下文件的数目
    int fileNum = 0;
    if (cli.recvData(&fileNum, sizeof(int)) == 0) {
        ui->statusLabel->setText("ERROR:receive fileNum error!");
    }

    //2.接收指定目录下的文件名序列
    char *dirbuf = NULL;
    if ((dirbuf = (char *)malloc(DATA_MAX * 10)) == NULL) {
        ui->statusLabel->setText("ERROR:malloc error!");
    }
    memset(dirbuf, 0, DATA_MAX * 10);
    if (cli.recvData(dirbuf, DATA_MAX * 10) == 0) {
        ui->statusLabel->setText("ERROR:receive dir info error!");
    } else {
        ui->statusLabel->setText("receive dir info success!");
        this->printCurDir(QString(dirbuf), fileNum);
        free(dirbuf);
    }
}

该函数有两种使用场景,其一为客户端与服务器连接成功时,此时服务器要向客户端发送指定目录下的文件列表(服务器主动发送),因此应该调用该函数。其二为当用户在客户端的文件列表中点击某个子目录时,客户端此时主动要求服务器发送指定子目录下的文件列表(服务器被动发送),此时应该调用该函数。

第二种情景可以采用Qt中的信号–槽机制(可以参考Qt Assitant了解相关概念),当用户点击了文件列表中的某个子目录时,该列表部件(fileWidget)就发送cdNextDir(QString)信号(该信号在),包含列表部件的当前部件(this)接收到这个信号后就做出相应操作,即执行槽函数cdNextDirOperation(QString)。这对信号和槽之间还传递了pathname这个变量,该变量存储了子目录的完整路径。下面的连接函数应该在当前部件的构造函数中完成。

    connect(this->fileWidget, SIGNAL(cdNextDir(QString)), this, SLOT(cdNextDirOperation(QString)));

槽函数cdNextDirOperation(QString)指定的动作很简单,即先向服务器发送“读取下级目录”的指令,再将该子目录的完整路径发送到服务器端,接着再调用recvDirInfo()。可以看到客户端和服务器之间的数据交互是同步发生的。

void configShowUI::cdNextDirOperation(QString pathname)
{
    char cd[] = "cd";
    if (cli.sendData(cd, strlen(cd)) == 0) {
        ui->statusLabel->setText("ERROR: send cd command error!");
        exit(1);
    }

    if (cli.sendData(pathname.toUtf8().data(), pathname.size()) == 0) {
        ui->statusLabel->setText("ERROR: send pathname error!");
        exit(1);
    }

    this->recvDirInfo();
}

在上述的recvDirInfo()中,如果客户端成功接收了服务器发送的字节流数据,那么就调用printCurDir()对字节流进行解析,然后以列表形式显示在客户端。这里的解析过程由具体的需求而定,再次不再赘述。

基于Socket API的C/S通信:以树形结构显示服务器端目录(1)

10 8 月, 2011 by edsionte 无评论 »

在基于socket API的C/S模型中,有时候需要这样的需求:

1.客户端可以即时获取服务器指定目录下的文件列表及属性;
2.在当前目录下点击某个子目录,可以进入并获取该子目录下的文件列表及其属性;
3.在当前目录下可以返回到上一级目录;

如果C/S模型基于FTP协议,那么这个需求就很容易实现,不过需要事先将服务器端的主机设置为FTP服务器。如果C/S模型是基于TCP/IP协议,那么实现起来就有一些麻烦。

本文所描述的C/S模型即基于TCP/IP协议,服务器程序采用Linux C编写,运行在Linux主机上;而客户端采用Qt以及原生的Socket API编写,以友好的交互界面运行在Windows或LInux主机上。

1.服务器程序实现概述

概括的说,服务器和客户端之间的通信总是“客户端发送请求–服务器接受请求并发送数据–客户端接受数据并解析”这样的循环过程。也就是说,服务器需要一直监听客户端发送的数据,根据发送数据的不同服务器作出相应的操作。客户端发送的数据均为字节流,但是根据其功能不同,我们可以将这些字节流分为两种:第一种为指令型数据,这些数据并不是客户和服务器之间真正交互的数据,而是客户端指定服务器做出相应操作的命令;第二种为请求数据,这类数据才是客户端和服务器用来进行数据交互的。

根据上面的理解,服务器和客户之间的交互模型大致是这样的:服务器和客户连接成功后,服务器首先将指定目录下的文件名及文件属性发送至客户端,客户端进行解析生成文件列表;当用户在客户端点击相应子目录(或上级目录名)后,客户端给服务器发送读取子目录(或上级目录)的指令;服务器收到该命令后,读取相应子目录(或上级目录)下的所有文件并发送至客户端。

在上述过程中,服务器对应的代码模型如下:

int server_dir(int client_fd)
{
	sendDir(client_fd, "/home/edsionte/");

	while (1) {
		memset(recvbuf, 0, CONFIGSTRUCT_LENGTH);
          	if (recv(client_fd, recvbuf, CONFIGSTRUCT_LENGTH, 0) < 0) {
	          	my_error("recv", errno, __LINE__);
           	}

		if (strcmp(recvbuf, "command1") == 0) {
			do_command1();
		} else if (strcmp(recvbuf, "cd") == 0) {
			sendNextDir(client_fd);
		} else if (strcmp(recvbuf, "command2") == 0) {
			do_command2();
		}
                …………
                else {
			do_anotherthing();
		}

	return 1;
}

比如客户端请求获得/home/edsionte/目录下的所有文件列表,那么一开始先通过sendDir函数获得该目录下的所有文件。接着服务器通过进入一个死循环而实现无限监听客户端到功能,服务器根据从客户端接受的不同“指令”从而做出相应处理。比如当服务器检测到客户端发送的数据为“cd”时,则进入sendNextDir函数,该函数即获得/home/edsionte/下某个子目录下的所有文件。

2.服务器的具体实现

在实现服务器的过程中主要涉及三个函数,下面将依次对所涉及的函数进行说明。

readDir()读取pathname目录下的所有文件,将文件以其相关属性保存到dirbuf中,将该目录下的文件数保存到num中。该函数具体的实现过程容易理解:先打开pathname目录,再利用循环依次读取该目录下的所有文件及相应的文件属性。并且在读取的过程中,依次将文件名和文件属性按照一定的格式拼接成一个字符流。

int readDir(char *pathname, char *dirbuf)
{
	DIR *mydir;
	struct dirent *myentry;
	struct stat mystat;
	char filepath[DATA_MAX];
	int num = 1;

	if ((mydir = opendir(pathname)) == NULL) {
		my_error("opendir", errno, __LINE__);
	}

	if (pathname[strlen(pathname) - 1] != '/')
		pathname[strlen(pathname)] = '/';
	strcpy(dirbuf, pathname);
	strcat(dirbuf, "\n");
	strcat(dirbuf, "..:1\n");

	while ((myentry = readdir(mydir)) != NULL) {	
		if ((strcmp(myentry->d_name, ".") == 0) || (strcmp(myentry->d_name, "..") == 0))
			continue;
		memset(filepath, 0, DATA_MAX);
        	strcpy(filepath, pathname);
		strcat(dirbuf, myentry->d_name);

		strcat(filepath, myentry->d_name);
		if (lstat(filepath, &mystat) == -1) {
			my_error("stat", errno, __LINE__);
		}
	
		if (S_ISDIR(mystat.st_mode)) {
			strcat(dirbuf, ":1");
		} else {
			strcat(dirbuf, ":0");
		}

		strcat(dirbuf, "\n");
		num++;
	}//while

	return num;
}

sendDir()先开辟一定大小的内存空间dirbuf,再通过readDir()获得相应字节流;再通过客户端套接字client_fd,将pathname目录下的文件数和该目录下的数据字节流发送至客户端。

void sendDir(int client_fd, char *pathname)
{
	int num;

	char *dirbuf = NULL;
	if ((dirbuf = (char *)malloc(DATA_MAX * 10)) == NULL) {
		my_error("malloc", errno, __LINE__);
	}
	memset(dirbuf, 0, DATA_MAX * 10);
	num = readDir(pathname, dirbuf);
	if (send(client_fd, &num, sizeof(num), 0) == -1) {
		my_error("send", errno, __LINE__);
	}

	if (send(client_fd, dirbuf, strlen(dirbuf), 0) == -1) {
		my_error("send", errno, __LINE__);
	}
	free(dirbuf);
}

当服务器接收到客户端“进入下级目录”的命令(程序中用字节流“cd”表示)时就掉用sendNextDir()。该函数先接收客户端发送来的下级目录的完整路径,再调用sendDir()。

void sendNextDir(int client_fd)
{
	char pathname[DATA_MAX];
	memset(pathname, 0, DATA_MAX);
	if (recv(client_fd, pathname, DATA_MAX, 0) == -1) {
		my_error("recv", errno, __LINE__);
	}

	sendDir(client_fd, pathname);
}

通过上述四个函数就可以基本实现本文一开始提出的需求。

基于socket API的C/S通信:将Qt程序从Linux移植到windows

30 7 月, 2011 by edsionte 无评论 »

Qt是一个跨平台的GUI开发语言,它是对C++在图形设计方面上的一种扩充。Qt本身包含一系列用来设计图形界面的类,并且对C++原有的类都进行了再次封装。如果你的程序采用Qt的类库,那么源程序在不同平台重新编译即可;如果程序中使用某些C++或C库函数,那么在移植时就需要针对不同平台作一些改动。

本文所举例的移植代码是基于socket API的C/S模型的客户端,该客户端的原始代码在linux系统下完成(以下称为“源代码”),现在要将其移植到windows平台下(以下称“移植代码”)。源代码的界面部分采用Qt完成,而通信部分则是采用原始的socket接口。

1.加入动态库

在linux系统中,socket API属于libc库,因此只需在程序中加头文件即可。而在windows系统下,由于socket接口是继承Unix系统而来,因此需要加入wsock32库,并且加上相应的头文件winsock2.h。加入该库具体的做法是在工程问价中加入下面的语句:

LIBS += -lwsock32

2.初始化套接字库

在windows下有两套socket API,一种是经过C++封装的csocket类,而另一种则是原始的socket接口,为了降低移植的复杂性,我们采用后者。

在将wsock32动态库加入移植程序后,socket接口还不能使用,因为在这之前必须使用WSAStartup函数对该库进行初始化。具体的初始化代码可以参考如下:

    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(1,1);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {
     return;
    }

    if ( LOBYTE(wsaData.wVersion) != 1 ||
           HIBYTE(wsaData.wVersion) != 1 ) {
     WSACleanup();
     return;

WSAStartup函数有两个参数,wVersionRequested用来向该函数传递socket接口调用者可以使用socket的最高版本号,通过MAKEWORD宏即可完成版本号的组装,该变量的低字节指定主版本号而低字节指定次版本号。wsaData用来接收socket函数执行期间的一些数据。
当WSAStartup函数初始化动态库成功时返回0,否则返回-1。

当初始化成功后,还要再确认已加载的动态库的版本是否和我们所指定的动态库版本相吻合,如果不符合,则通过WSACleanup函数清除已加载的动态库。

动态库加载成功后,接下来就可以使用socket接口函数了,当使用完毕时,需要用WSACleanup函数卸载动态库。

3.更改套接字描述符

在使用一系列socket接口之前,必须使用socket函数创建套接字。在linux下,原始的socket函数返回的套接字描述符是整型,但是在windows下,该函数返回的套接字描述符是SOCKET类型。因此源代码和移植代码参考如下:

源代码:

    int sockfd;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        ui->statusLabel->setText("ERROR:socket connceting fail!");
        exit(1);
    }

移植代码:

    SOCKET sockfd;
    if ((cli.sockfd = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
        ui->statusLabel->setText("ERROR:socket connceting fail!");
        exit(1);
    }

既然在移植代码中套接字描述符不是整型,那么其错误返回值也应该不是-1而是标准的错误返回值INVALID_SOCKET。

4.close函数

源代码中关闭套接字选用close(),而移植程序相应的使用closesocket()。并且此时用WSACleanup()关闭已加载的动态库。

进过上述几个部分的修改,此时移植程序在windows下即可编译成功。

使用gcc同时编译多个文件

28 7 月, 2011 by edsionte 无评论 »

平时在写C程序的时候都是将所有代码全部写入一个.c文件中,这样写对小程序来说是适合的。但是对于比较大的项目,将所有代码写在一个文件不利于代码的维护。

最近由于项目需求,将一个服务器程序按照功能整理成了几个.c文件和.h文件。该服务器程序包含以下文件:

.
|-- config_system.c
|-- generic_function.c
|-- log_system.c
|-- server.c
`-- server.h

代码整理完毕之后,开始对源文件进行重新编译。在编译的过程中遇到了一些问题,现在总结如下。

1. 自定义头文件的使用

server.h文件包含服务器程序中所有自定义函数原型的声明和一些常量定义,将该自定义函数放在当前程序的目录下即可。自定义头文件的使用方法就和普通头文件的使用方法一样,加在程序最开始的位置即可,不过要将包含头文件的尖括号<>改成双引号“ ”。

通过这个实践,也可以更好的理解包含头文件的两种方法。对于尖括号包含的头文件,gcc在系统默认的目录中(/usr/include)查找相应的头文件;对于双引号包含的头文件,编译器首先会在当前目录下或指定的目录下(如果有指定)去查找头文件,如果当前目录下没有该头文件则去默认的头文件目录下查找。

刚才我们提到了头文件的指定目录,在大型项目中,头文件可能会更多,往往将头文件单独放在一个目录当中,此时应该使用-Idirname选项来告诉编译器到指定的目录dirname中去查找头文件。比如我们将server.h放在head目录中再进行编译:

gcc -Ihead config_system.c server.c log_system.c generic_function.c -o server

选项I代表include,该选项的作用阶段是预处理阶段。

2. 多个源文件的编译

我们可以先将每个源文件编译成目标文件,最后再生成一个可执行文件。比如:

edsionte@edsionte-desktop:~/server$ gcc -c server.c
edsionte@edsionte-desktop:~/server$ gcc -c config_system.c
edsionte@edsionte-desktop:~/server$ gcc -c log_system.c
edsionte@edsionte-desktop:~/server$ gcc -c generic_function.c
edsionte@edsionte-desktop:~/server$ gcc server.o config_system.o log_system.o generic_function.o -o server

也可以直接使用 一条命令:

edsionte@edsionte-desktop:~/server$ gcc server.c generic_function.c log_system.c config_system.c -o server

不管是那种方法都可以编译源文件,最后连接成可执行文件。在第一种方法中,单独使用-c选项只是编译源文件,并不涉及链接的过程,因此源文件是否包含main函数我们并不关心。第二种方法是将编译链接通过一条命令来完成。

其实在使用gcc编译程序时,整个编译过程分为预编译、编译、汇编和链接四个步骤。上述两种方法都自动完成了前两个步骤。

3. extern的使用

按照上面的命令进行编译程序,出现了下面的错误提示:

config_system.c:80: error: ‘config_filename’ undeclared (first use in this function)

这条错误是在说明config_filename这个变量未声明。config_filename作为一个全局变量在generic_function.c文件中已经声明并定义过,那么如何让编译器知道该变量已经定义过并且可以在config_system.c文件中使用?此时extern关键字就派上了用场。

extern的作用是声明一个变量,它的作用仅仅是声明,而不是像定义该变量时那样为其分配内存空间。一个变量在整个程序中只能定义一次,但是却可以声明多次。

4. 使用脚本编译并运行服务器程序

由于将代码按照功能分离后,每次编译都需要很长一串的命令,因此写个脚本就显得很有必要了。下面的脚本先将原有的可执行文件(如果存在的话)删除,再编译程序,最后运行可执行文件。

#!/bin/bash
test server && rm server
gcc server.c config_system.c log_system.c generic_function.c -o server
echo "compliing success!"
echo "sever is running.."
./server

通过这次“分离”代码的实践,给我最大的感受就是平时看似懂的概念只有到真正使用的时候才真的开始理解。

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key