存档在 2011年7月

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

2011年7月30日

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同时编译多个文件

2011年7月28日

平时在写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

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

使用grep查找指定目录下的关键字

2011年7月24日

之前于童鞋问我如下问题:

在/etc目录下如何查找包含’root’关键字的所有文件?

对于一个指定的文件来说通常使用下面的方法来查找关键字:

grep 'keyword' file

如果你懂得正则表达式,那么还可以对关键字作更细致的匹配。但是利用grep查找指定目录下的所有文件我尚不清楚,通过man手册以及论坛求助基本解决了上面的那个问题。

1.如何递归查找

grep通常只能查找指定数目的文件,如果需要查找某个目录下的所有文件必须添加-R或-r选项。这个选项比较好理解,如果我们要删除一个非空的目录那么也必须对rm命令添加-r或-R选项。因此我们可以通过一下命令来完成本文一开始所提出的问题:

grep -R 'boot' /etc

这样好像已经解决了问题,不过从输出的结果来看有一些匹配并不符合我们的要求。

/etc/grub.d/README:administrator.  For example, you can add an entry to boot another OS as
/etc/grub.d/20_memtest86+:if test -e /boot/memtest86+.bin ; then
/etc/grub.d/20_memtest86+:  prepare_boot_cache="$(prepare_grub_to_access_device ${GRUB_DEVICE_BOOT} | sed -e "s/^/
\t/")"

比如第三行所匹配的root并不是一个单独的词,而是包含在其他字符串中。

2.完全匹配一个词

通过上面的分析我们得知我们需要类似下面的举例那样匹配root关键字:

I am a root
root is me
I have root permission

而不是像下面这样:

rooter is me
I am rooter
I have rooter permission

因此,我们还需要增加其他参数以达到完全匹配一个词的目的。grep中使用-w选项来完全匹配一个单词。因此我们的解决方法修改如下:

grep -R -w 'boot' /etc

3.更友好的改进

通过上面的命令已经可以较好的解决本文一开始提出的问题了。不过,其输出结果很长,不方便我们查看。

如果我们需要知道该关键字位于被查找文件的哪一行,那么可以加入-n选项:

grep -R -w -n 'boot' /etc

有时候我们只需要知道该关键字包含在那个文件中,因此我们加入-l选项:

grep -R -w -l 'boot' /etc

这样以来的确方便了查看,不过由于输出结果在一个屏幕下不能完全显示,因此我们可以用more命令分屏查看,或者直接将输出结果重定位于指定的文件中:

grep -R -w -l 'boot' /etc > ./output.txt

这样就可以方便的查看输出结果了。

Hello,Python!

2011年7月10日

使用Python打印一条“hello,world!”语句是再简单不过的事情了,因为Python特有的交互型解释器可以帮助你完成这项任务。如果你在Ubuntu系统下,首先在终端敲入python就可以进入python解释器,然后输入”hello, world!”即可打印出这句经典的语句。

edsionte@edsionte-laptop:~$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:09:56)
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> "hello, world!"
'hello, world!'

此时,你已经成功的打印了”hello, world!”这条字符串。当你输入这条字符串后,解释器就按照字符串的解释规则去解释它,其结果就是在终端显示出hello, world这条语句。这里特别强调的是,这条语句必须被双引号或者单引号包括在内,如果在解释器中直接输入hello, world!那么将会出现语法错误,比如:

>>> hello, world!
  File "< stdin >", line 1
    hello, world!
                ^
SyntaxError: invalid syntax

其主要原因就是Python解释器此时不知道如何解释这条语句。

除了用双引号或者单引号直接告诉Python解释器”此时你应该解释一条字符串“,通过print命令也可以成功打印这条语句。

>>> print "hello, world!"
hello, world!

至此,你已经成功的使用Python打印了“hello,world!”,不过这一切都是在Python解释器里完成的,当你退出解释器是,这些语句将会消失。按照我们常规的编程习惯,我们通常会将程序以文本的形式保存起来。我们使用vim创建一个hello.py的程序,其内容是:

#!/usr/bin/python2.6
print "hello, world!"

新增的第一条语句告诉操作系统应该使用/usr/bin目录下的Python来执行这些语句。我们再修改该文件的属性,使它有执行的权限。好了,你已经创建了第一个Python脚本,现在开始执行吧:

edsionte@edsionte-laptop:~/mypython$ chmod a+x hello.py
edsionte@edsionte-laptop:~/mypython$ ./hello.py
hello, world!

成功!

adduser命令学习

2011年7月7日

今早试着为我的机子添加一个guest账户,以便方便其他同学的使用。因此我用useradd命令来创建一个guest账户:

edsionte@edsionte-desktop:~$ sudo useradd -d /home/test test

上述命令在系统中创建了一个test用户,并且通过-d选项为该用户指定了登录后的主目录/home/test。创建成功后,该用户的账户信息会保存在/etc/passwd文件当中。/etc/passwd文件用来保存系统中当前所有的用户信息,该文件对所有用户都可见。比如:

……
edsionte:x:1000:1000:edsionte,,,:/home/edsionte:/bin/bash
guest:x:1001:1001:guest,,,,:/home/guest:/bin/bash
test:x:1002:1002::/home/test:/bin/sh
……

在该文件中,每行信息代表一个用户。每个用户的信息由7部分组成:

用户名:加密后的用户密码:用户ID(UID):用户所在组ID(GID):用户全名以及用户信息:用户主目录:该用户登录时所用的命令解释器

从上面test用户的信息可以看到,除了我们显式指定的用户主目录,其余信息都是系统默认的。比如UID和GID相同,test用户的命令解释器选用sh。

用户创建后,还必须为test指定一个登录密码。指定和修改用户密码的命令是passwd。普通用户使用该命令只能对自己修改密码,而超级用户则可以对任何用户修改密码。我们当前的用户为edsionte,通过下面的命令即可为test指定密码:

edsionte@edsionte-desktop:~$ sudo passwd test
输入新的 UNIX 密码:
重新输入新的 UNIX 密码:
passwd:已成功更新密码

由于我们在edsionte用户下去修改test用户的密码,因此必须加sudo使用超级用户权限。

到此位置,test用户就创建完毕了,可是登录到test用户下,打开终端后却出现了以下几个问题:

1.打开终端后,提示信息仅为“$”,并不想通常我们所见到的“edsionte@edsionte-desktop:~$”;

2.在终端使用TAB键后不能自动补全命令,而是TAB健本身的效果;

原来这两个问题与我们刚才指定的命令解释器有关,通常我们使用的shell为bash,而useradd命令默认指定的shell为sh。通过usermod命令可以修改一个用户的相关信息,因此我们在test用户下通过下面的命令修改shell即可:

usermod -s /bin/bash

注销并重新登录test后就可以看到我们通常所熟悉的终端。

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