linux ps 命令查看进程状态

Posted on 2012年8月09日 22:01

linux上进程有5种状态:
1. 运行(正在运行或在运行队列中等待)
2. 中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号)
3. 不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)
4. 僵死(进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放)
5. 停止(进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行)

ps工具标识进程的5种状态码:
D 不可中断 uninterruptible sleep (usually IO)
R 运行 runnable (on run queue)
S 中断 sleeping
T 停止 traced or stopped
Z 僵死 a defunct ("zombie") process


注: 其它状态还包括W(无驻留页), <(高优先级进程), N(低优先级进程), L(内存锁页).


使用ps格式输出来查看进程状态:
ps -eo user,stat..,cmd


user 用户名
uid 用户号
pid 进程号
ppid 父进程号
'size' 内存大小, Kbytes字节.
v'size' 总虚拟内存大小, bytes字节(包含code+data+stack)
share 总共享页数
nice 进程优先级(缺省为0, 最大为-20)
priority(pri) 内核调度优先级
pmem 进程分享的物理内存数的百分比
trs 程序执行代码驻留大小
rss 进程使用的总物理内存数, Kbytes字节
time 进程执行起到现在总的CPU暂用时间
stat 进程状态
cmd(args) 执行命令的简单格式


例子:
查看当前系统进程的uid,pid,stat,pri, 以uid号排序.
ps -eo pid,stat,pri,uid --sort uid


查看当前系统进程的user,pid,stat,rss,args, 以rss排序.
ps -eo user,pid,stat,rss,args --sort rss

附:

PROCESS STATE CODES
Here are the different values that the s, stat and state output specifiers (header "STAT" or "S") will display to describe the state of a process.
D Uninterruptible sleep (usually IO)
R Running or runnable (on run queue)
S Interruptible sleep (waiting for an event to complete)
T Stopped, either by a job control signal or because it is being traced.
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z Defunct ("zombie") process, terminated but not reaped by its parent.

For BSD formats and when the stat keyword is used, additional characters may be displayed:
< high-priority (not nice to other users)
N low-priority (nice to other users)
L has pages locked into memory (for real-time and custom IO)
s is a session leader
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
+ is in the foreground process group

PHP 配置服务器crontab

Posted on 2012年8月02日 21:18

<?php

class Ssh2_crontab_manager
{
    private $connection;
    private $path;
    private $handle;
    private $cron_file;

    function __construct($host=NULL, $port=NULL, $username=NULL, $password=NULL)
    {
        $path_length = strrpos(__FILE__, "/");
        $this->path = substr(__FILE__, 0, $path_length) .'/';
        $this->handle = 'crontab.txt';
        $this->cron_file = "{$this->path}{$this->handle}";
        
        try
        {
            if (is_null($host) || is_null($port) || is_null($username) || is_null($password))
                throw new Exception("Please specify the host, port, username and password!");
            
            $this->connection = @ssh2_connect($host, $port);
            if (!$this->connection)
                throw new Exception("The SSH2 connection could not be established.");
            
            $authentication = @ssh2_auth_password($this->connection, $username, $password);
            if (!$authentication)
                throw new Exception("Could not authenticate '{$username}' using password: '{$password}'");            
        }
        catch (Exception $e)
        {
            $this->error_message($e->getMessage());
        }
    }
    
    public function exec()
    {
        $argument_count = func_num_args();
        
        try
        {
            if (!$argument_count)
                throw new Exception("There is nothing to execute, no arguments specified.");
            
            $arguments = func_get_args();
            
            $comment_string = ($argument_count > 1) ? implode(" && ", $arguments) : $arguments[0];
            
            $stream = @ssh2_exec($this->connection, $comment_string);
            if (!$stream)
                throw new Exception("Unable to execute the specified commands: <br>{$comment_string}");
        }
        catch (Exception $e)
        {
            $this->error_message($e->getMessage());
        }
        
        return $this;
    }
    
    public function write_to_file($path=NULL, $handle=NULL)
    {
        if (!$this->crontab_file_exists())
        {
            $this->handle = (is_null($handle)) ? $this->handle : $handle;
            $this->path   = (is_null($path)) ? $this->path     : $path;
            $this->cron_file = "{$this->path}{$this->handle}";
            
            $init_cron = "crontab -l > {$this->cron_file} && [ -f {$this->cron_file} ] || > {$this->cron_file}";
            
            $this->exec($init_cron);
        }
        
        return $this;
    }
    
    public function remove_file()
    {
        if ($this->crontab_file_exists()) $this->exec("rm {$this->cron_file}");
        return $this;
    }
    
    public function append_cronjob($cron_jobs=NULL)
    {
        if (is_null($cron_jobs))
            $this->error_message("Nothing to append! Pleasae specify a cron job or an array of cron jobs.");
        
        $append_cronfile = "echo '";
        $append_cronfile .= (is_array($cron_jobs)) ? implode("\n", $cron_jobs) : $cron_jobs;
        $append_cronfile .= "' >> {$this->cron_file}";
        
        $install_cron = "crontab {$this->cron_file}";
        
        $this->write_to_file()->exec($append_cronfile, $install_cron)->remove_file();
        
        return $this;
    }
    
    public function remove_cronjob($cron_jobs=NULL)
    {
        if (is_null($cron_jobs))
            $this->error_message("Nothing to remove! Please specify a cron job or an array of cron jobs.");
        $this->write_to_file();
        
        $cron_array = file($this->cron_file, FILE_IGNORE_NEW_LINES);
        
        if (empty($cron_array))
            $this->error_message("Nothing to remove! The crontab is already empty.");
        
        $original_count = count($cron_array);
        
        if (is_array($cron_jobs))
        {
            foreach ($cron_jobs as $cron_regex)
                $cron_array = preg_grep($cron_regex, $cron_array, PREG_GREP_INVERT);
        }
        else
        {
            $cron_array = preg_grep($cron_jobs, $cron_array, PREG_GREP_INVERT);
        }
        
        return ($original_count == count($cron_array)) ? $this->remove_file() :
                $this->remove_crontab()->append_cronjob($cron_array);
    }
    
    public function remove_crontab()
    {
        $this->exec("crontab -r")->remove_file();
        return $this;
    }
    
    private function crontab_file_exists()
    {
        return file_exists($this->cron_file);
    }
    
    private function error_message($error)
    {
        die("<pre style='color:#EE2711'>ERROR: {$error}</pre>");
    }
    
}


$crontab = new Ssh2_crontab_manager('localhost', '22', 'hewenxiang', '123456');

$new_cronjobs = array(
    '0 0 1 * * home/path/to/command/the_command.sh',
    '30 8 * * 6 home/path/to/command/the_command.sh >/dev/null 2>&1'
);

$crontab->append_cronjob($new_cronjobs);

$cron_regex = array(
        '/0 0 1 \* \*/',
        '/home\/path\/to\/command\/the_command\.sh/'
);

$crontab->remove_cronjob($cron_regex);
 

jQuery center

Posted on 2012年7月22日 15:07

本来jQuery有一个certer插件,不过少了一个功能,就是基于视口(ViewPort)的居中.

改写一下,代码如下:

Js代码  收藏代码
  1. jQuery.fn.center = function(f) {  
  2.     return this.each(function(){  
  3.         var p = f===false?document.body:this.parentNode;  
  4.         if ( p.nodeName.toLowerCase()!= "body" && jQuery.css(p,"position") == 'static' )  
  5.             p.style.position = 'relative';  
  6.         var s = this.style;  
  7.         s.position = 'absolute';  
  8.         if(p.nodeName.toLowerCase() == "body")  
  9.             var w=$(window);  
  10.         if(!f || f == "horizontal") {  
  11.             s.left = "0px";  
  12.             if(p.nodeName.toLowerCase() == "body") {  
  13.                 var clientLeft = w.scrollLeft() - 10 + (w.width() - parseInt(jQuery.css(this,"width")))/2;  
  14.                 s.left = Math.max(clientLeft,0) + "px";  
  15.             }else if(((parseInt(jQuery.css(p,"width")) - parseInt(jQuery.css(this,"width")))/2) > 0)  
  16.                 s.left = ((parseInt(jQuery.css(p,"width")) - parseInt(jQuery.css(this,"width")))/2) + "px";  
  17.         }  
  18.         if(!f || f == "vertical") {  
  19.             s.top = "0px";  
  20.             if(p.nodeName.toLowerCase() == "body") {  
  21.                 var clientHeight = w.scrollTop() - 10 + (w.height() - parseInt(jQuery.css(this,"height")))/2;  
  22.                 s.top = Math.max(clientHeight,0) + "px";  
  23.             }else if(((parseInt(jQuery.css(p,"height")) - parseInt(jQuery.css(this,"height")))/2) > 0)  
  24.                 s.top = ((parseInt(jQuery.css(p,"height")) - parseInt(jQuery.css(this,"height")))/2) + "px";  
  25.         }  
  26.     });  
  27. };  

使用,

Js代码  收藏代码
  1. $(expr).center();  
  2. $(expr).center(false);  

如果expr 的 parentNode 是body 或者参数是 false的话就会真的 ViewPort 居中.

另外加的偏移量 10 是个偷懒的方法,比如有了滚动条,会好一些,没有的话视觉上也差别不大,不过没有这个偏移量又有 滚动条的话 视觉上就不太舒服了

 

svn 常用命令

Posted on 2012年7月12日 15:15

SVN(Subversion)是一个自由、开源的项目源代码版本控制工具。目前,绝大多数开源软件和企业代码管理,都使用SVN作为代码版本管理软件。

Subversion将文件存放在中心版本库里,这个版本库很像一个普通的文件服务器。不同的是,它可以记录每一次文件和目录的修改情况,这样就可以在需要的回滚时,将数据恢复到以前的版本,并可以查看数据的更改细节。

SVN 官方网址:Apache Subversion

SVN 服务器配置:安装SVN服务器

 

一、 SVN常用命令

1、将文件checkout到本地目录
svn checkout path(path是服务器上的目录)
简写:svn co

2、往版本库中添加新的文件
svn add file

3、将改动的文件提交到版本库
svn commit -m “LogMessage” [-N] [--no-unlock] PATH(如果选择了保持锁,就使用–no-unlock开关)
简写:svn ci

4、加锁/解锁
svn lock -m “LockMessage” [--force] PATH
svn unlock PATH

5、更新到某个版本
svn update -r m path
简写:svn up

6、查看文件或者目录状态
1)svn status path(目录下的文件和子目录的状态,正常状态不显示)
2)svn status -v path(显示文件和子目录状态)
简写:svn st

7、删除文件
svn delete path -m “delete test fle”
简写:svn (del, remove, rm)

8、查看日志
svn log path

9、查看文件详细信息
svn info path

10、比较差异
svn diff path(将修改的文件与基础版本比较)
svn diff -r m:n path(对版本m和版本n比较差异)
简写:svn di

11、将两个版本之间的差异合并到当前文件
svn merge -r m:n path

12、SVN 帮助
svn help
svn help ci

 

 

二、 SVN不常用命令
13、版本库下的文件和目录列表
  svn list path    显示path目录下的所有属于版本库的文件和目录简写:svn ls

14、创建纳入版本控制下的新目录
svn mkdir: 创建纳入版本控制下的新目录。
用法: 
1、mkdir PATH...
每一个以工作副本 PATH 指定的目录,都会创建在本地端,并且加入新增调度,以待下一次的提交。
2、mkdir URL... 创建版本控制的目录。 
每个以URL指定的目录,都会透过立即提交于仓库中创建。在这两个情况下,所有的中间目录都必须事先存在。

15、恢复本地修改
svn revert: 恢复原始未改变的工作副本文件 (恢复大部份的本地修改)。
用法: revert PATH... 注意: 本子命令不会存取网络,并且会解除冲突的状况。但是它不会恢复被删除的目录

16、代码库URL变更
svn switch (sw): 更新工作副本至不同的URL。
用法: 
1、switch URL [PATH]        
更新你的工作副本,映射到一个新的URL,其行为跟“svn update”很像,也会将      服务器上文件与本地文件合并。这是将工作副本对应到同一仓库中某个分支或者标记的方法。 
2、switch --relocate FROM TO [PATH...]   
改写工作副本的URL元数据,以反映单纯的URL上的改变。当仓库的根URL变动     (比如方案名或是主机名称变动),但是工作副本仍旧对映到同一仓库的同一目录时使用     这个命令更新工作副本与仓库的对应关系。

17、解决冲突
svn resolved: 移除工作副本的目录或文件的“冲突”状态。
用法: resolved PATH... 注意: 本子命令不会依语法来解决冲突或是移除冲突标记;它只是移除冲突的相关文件,然后让 PATH 可以再次提交。

18、输出指定文件或URL的内容。
svn cat 目标[@版本]...如果指定了版本,将从指定的版本开始查找。 svn cat -r PREV filename > filename (PREV 是上一版本,也可以写具体版本号,这样输出结果是可以提交的)
 

 

三、 SVN其它命令

虽然不像本章先前讨论过的那些命令那么常用,但是有时你也需要这些命令。 

svn cleanup
当 Subversion修改你的工作副本时(或者任何在.svn中的信息),它尝试尽可能做到安全。在改变一个工作副本前,Subversion把它的意 图写到一个日志文件中。接下来它执行日志文件中的命令来应用要求的修改。最后,Subversion删除日志文件。从架构上来说,这与一个日志文件系统 (journaled filesystem)类似。如果一个 Subversion操作被打断(例如,进程被杀掉了,或机器当掉了)了,日志文件仍在硬盘上。重新执行日志文件,Subversion可以完成先前开始 的操作,这样你的工作副本能回到一个可靠的状态。 

以下是svn cleanup所做的:它搜索你的工作副本并执行所有遗留的日志,在这过程中删除锁。如果Subversion曾告诉你你的工作副本的一部分被“锁定”了,那么你应该执行这个命令。另外, svn status会在锁定的项前显示L。 

$ svn status
L    somedir
M   somedir/foo.c 

$ svn cleanup
$ svn status
M      somedir/foo.c

svn import
使用svn import是把未版本化的文件树复制到资料库的快速办法,它需要创建一个临时目录。 

$ svnadmin create /usr/local/svn/newrepos
$ svn import mytree file:///usr/local/svn/newrepos/some/project
Adding         mytree/foo.c
Adding         mytree/bar.c
Adding         mytree/subdir
Adding         mytree/subdir/quux.h

Committed revision 1.

上面的例子把在some/project目录下mytree目录的内容复制到资料库中。 

$ svn list file:///usr/local/svn/newrepos/some/project
bar.c
foo.c
subdir/

注意在导入完成后,原来的树没有被转化成一个工作副本。为了开始工作,你仍然需要svn checkout这个树的一个新的工作副本。

 

 

四、SVN 常用命令一览表

命令 功能 使用格式
checkout 检出 svn  co  URL
up 更新到当前URL的末端 svn  up
switch 更新到某一tag/branch svn  switch  (tag/分支)URL
add 增加 svn  add  文件名
rm 删除文件 svn  rm 文件名
删除目录 svn  rm 目录名
diff 与base版本(最后检出或者更新到的版本)对比 svn  diff
与版本库中最新版本对比 svn  diff  -r  head
当前工作副本,两个版本之间对比 svn  diff  -r  reversion1:reversion2
版本库中任意两个tag做对比 svn   diff    (tag1)URL    (tag2)URL
ci 提交 svn ci -m "commit log"
log 查看当前工作副本log svn  log
只查看指定版本的log svn  log  -r
打印log所有附加信息 svn  log  -v
查看当前tag/branch版本详情 svn  log --stop-on-copy -v
info 查看当前工作副本所在URL svn  info
status 查看工作副本的状态 svn st
查看文件的taglist svn命令不支持,可执行cs taglist
tag 新增tag svn cp . (tag)URL
删除tag svn rm (tag)URL -m "commit log"
覆盖已经存在的tag 不支持
分支开发 创建branch svn  cp  (基线版本)URL (分支)URL  -m "commit log"
删除branch svn rm (分支)URL   -m "commit log"
同步 svn co (主干)URL
cd ~/wc
svn merge (主干)URL (待同步tag)URL
svn ci -m "commit log"
svn cp (主干)URL (以_PD_BL_MAIN结尾的tag)URL -m"commit log"
合并 svn co (合并目标)URL
cd ~/wc
svn merge (基线版本tag)URL  (上线tag)URL
svn ci -m "commit log"
svn cp (合并目标)URL (上线tag_MERGE_的tag对应)URL -m"commit log"

 

二叉树

Posted on 2012年7月09日 15:31
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

struct Node *createnode(long value);
struct Node *addnode(long value, struct Node *pNode);

void listnodes(struct Node *pNode);
void freenodes(struct Node *pNode);

struct Node
{
    long item;
    int count;
    struct Node *pLeft;
    struct Node *pRight;
};

int main(void)
{
    long newvalue = 0;
    struct Node *pRoot = NULL;
    char answer = 'n';

    do 
    {
        printf("Enter the node value: ");
        scanf(" %ld", &newvalue);
        if(pRoot == NULL)
            pRoot = createnode(newvalue);
        else
            addnode(newvalue, pRoot);
        printf("\nDo you want to enter another (y or n)?\n");
        scanf(" %c", &answer);
    }
    while (tolower(answer) == 'y');

    printf("The values in ascending sequence are:\n");
    listnodes(pRoot);
    freenodes(pRoot);

    return 0;
}

struct Node *createnode(long value)
{
    struct Node *pNode = (struct Node *)malloc(sizeof(struct Node));
    pNode->item = value;
    pNode->count = 1;
    pNode->pLeft = pNode->pRight = NULL;
    return pNode;
}

struct Node *addnode(long value, struct Node *pNode)
{
    if (pNode == NULL)
        return createnode(value);

    if (value == pNode->item)
    {
        ++pNode->count;
        return pNode;
    }

    if (value < pNode->item)
    {
        if (pNode->pLeft == NULL)
        {
            pNode->pLeft = createnode(value);
            return pNode->pLeft;
        }
        else
            return addnode(value, pNode->pLeft);
    }
    else
    {
        if (pNode->pRight == NULL)
        {
            pNode->pRight = createnode(value);
            return pNode->pRight;
        }
        else
            return addnode(value, pNode->pRight);
    }
}

void listnodes(struct Node *pNode)
{
    if (pNode->pLeft != NULL)
        listnodes(pNode->pLeft);

    for (int i = 0; i < pNode->count; i++)
        printf("%10ld\n", pNode->item);

    if (pNode->pRight != NULL)
        listnodes(pNode->pRight);
}

void freenodes(struct Node *pNode)
{
    if (pNode == NULL)
        return;
    if (pNode->pLeft != NULL)
        freenodes(pNode->pLeft);
    if (pNode->pRight != NULL)
        freenodes(pNode->pRight);
    free(pNode);
}

python的文件锁

Posted on 2012年6月24日 19:27



"""
文件锁测试
"""

import fcntl
import time

fp = open('hello.txt','w')
fcntl.flock(fp, fcntl.LOCK_EX)
print '文件锁开始执行'
time.sleep(100)
fcntl.flock(fp, fcntl.LOCK_UN)
fp.close()

memcached 启动参数的中文解释

Posted on 2012年6月12日 22:04

在线地址:https://github.com/liuxd/MyTranslation/blob/master/translation/memcached-1.4.man

全文内容:

memcached 1.4.2
-p <num>      监听的TCP端口(默认: 11211)
-U <num>      监听的UDP端口(默认: 11211, 0表示不监听)
-s <file>     用于监听的UNIX套接字路径(禁用网络支持)
-a <mask>     UNIX套接字访问掩码,八进制数字(默认:0700)
-l <ip_addr>  监听的IP地址。(默认:INADDR_ANY,所有地址)
-d            作为守护进程来运行。
-r            最大核心文件限制。
-u <username> 设定进程所属用户。(只有root用户可以使用这个参数)
-m <num>      单个数据项的最大可用内存,以MB为单位。(默认:64MB)
-M            内存用光时报错。(不会删除数据)
-c <num>      最大并发连接数。(默认:1024)
-k            锁定所有内存页。注意你可以锁定的内存上限。
              试图分配更多内存会失败的,所以留意启动守护进程时所用的用户可分配的内存上限。
              (不是前面的 -u <username> 参数;在sh下,使用命令"ulimit -S -l NUM_KB"来设置。)
-v            提示信息(在事件循环中打印错误/警告信息。)
-vv           详细信息(还打印客户端命令/响应)
-vvv          超详细信息(还打印内部状态的变化)
-h            打印这个帮助信息并退出。
-i            打印memcached和libevent的许可。
-P <file>     保存进程ID到指定文件,只有在使用 -d 选项的时候才有意义。
-f <factor>   块大小增长因子。(默认:1.25)
-n <bytes>    分配给key+value+flags的最小空间(默认:48)
-L            尝试使用大内存页(如果可用的话)。提高内存页尺寸可以减少"页表缓冲(TLB)"丢失次数,提高运行效率。
              为了从操作系统获得大内存页,memcached会把全部数据项分配到一个大区块。
-D <char>     使用 <char> 作为前缀和ID的分隔符。
              这个用于按前缀获得状态报告。默认是":"(冒号)。
              如果指定了这个参数,则状态收集会自动开启;如果没指定,则需要用命令"stats detail on"来开启。
-t <num>      使用的线程数(默认:4)
-R            每个连接可处理的最大请求数。
-C            禁用CAS。
-b            设置后台日志队列的长度(默认:1024)
-B            绑定协议 - 可能值:ascii,binary,auto(默认)
-I            重写每个数据页尺寸。调整数据项最大尺寸。

jinjia2 中文文档

Posted on 2012年5月29日 00:44

补充:

1. 在模板中设置自定义变量:

    {% set variable_name = value %}

   比如设置{% set username = 'Jack' %}

  那么在设置之后就可以使用{{ username }}得到输出Jack

 

 

 

section 1 API

1 jinja2的Hello world:

import jinja2 as jj

template = jj.Template(‘Hello {{where}}’)

template.render(where = ‘World’)

基础:

2.1 jinja2中有一个核心对象: template Environment(模板环境), 这个类的实例被用于存储配置信息, 全局对象, 从文件系统或其他位置加载模板, 甚至如果你使用Template的构造器创建一个String类型的模板的时候, 也会自动的创建一个Environment对象.

2.2 Environment一般在一个应用中只创建一个, 当系统中有不同的配置需求的时候, 就需要创建多个Environment对象相互支持.

创建一个Environment对象: env = Environment(loader = FileSystemLoader(‘templates’))…意思是创建一个template Environment对象, 使用的加载器loader是FileSystemLoader类型, 可以加载的模板是当前工作目录下的templates目录下的模板文件.

加载一个模板: template = env.get_template(‘mytemplate.txt’), 使用env的模板环境加载名为mytemplate.txt的模板文件.

渲染一个模板: template.render(name = ‘Jack’), 渲染模板template, 传入了模板参数name值为Jack

高级api

3.1 class Environment(block_start_string = ‘{%’, block_end_string = ‘%}’, variable_start_string = ‘{{’, variable_end_string = ‘}}’, comment_start_string = ‘{#’, comment_end_string = ‘#}’, line_statement_prefix = None, trim_blocks = False, extensions = (), optimized = True, undefined = <class ‘jinja2.runtime.Undefined’>, finalize = None, autoescape = False, loader = None, cache_size = 50, auto_reload = True):  下面单独列出的属性解释不都是构造器中的参数

3.1.1 block_start_string = ‘{%’, block_end_string = ‘%}’, variable_start_string = ‘{{’, variable_end_string = ‘}}’, comment_start_string = ‘{#’, comment_end_string = ‘#}’用来定义块, 变量, 注释的开始和结束标记.

3.1.2 line_statement_prefix: 单行语句前缀

3.1.3 trim_blocks: 布尔值, 块后面的第一行将会被移除(块, 非变量标签), 默认是False

3.1.4 extensions: Jinja的扩展对象列表, 可以是导入路径的字符串或扩展类

3.1.5 optimized: 布尔值, 表明优化是否允许.

3.1.6 undefined: jinja2.runtime.Undefined或其子类的实例, 用来描绘模板中的值为undefined的变量

3.1.7 finalize: 一个可调用对象, 用来最终确定一个变量, 默认使用apply, 需要HTML转码时可以设置为escape

3.1.8 autoescape: 设置为true就打开了XML/HTML的自动escape功能

3.1.9 cache_size: 缓存大小, 默认50, 意思是如果有超过50个模板被加载器加载, 就会清理刚刚使用过的模板.  如果设置为0, 表示所有的模板都会被重编译, 也就是不使用缓存, 如果设置为-1, 表示缓存不会被清理.

3.1.10 auto_reload: 如果模板的文件发生了变化, 加载器会重新编译该模板进行加载.

3.1.11 shared: 使用Template的构造器创建一个模板的时候, 会自动的创建一个Environment对象, 这个Environment对象就是一个共享的Environment对象, 很多模板都共享着同一个匿名Environment对象, 对于共享Environment对象而言, shared属性为True, 其他情况都是False

3.1.12 sandboxed: SandboxedEnvironment的实例对象此属性为True

3.1.13 filters: 一个字典描述的filters过滤器集合, 如果非模板被加载的时候, 可以安全的添加filters或移除较早的.

3.1.14 tests: 一个字典描述的测试函数集合, 如果非模板被加载, 可以安全的修改这个字典.

3.1.15 globals: 一个字典描述的全局变量集合, 这些变量可以在所有的模板中使用, 并且不能被模板重写(如果optimizer设置为允许). 如果非模板被加载, 它可以安全的修改这个字典.

3.1.16 from_string(source, globals = None, template_class = None): 从一个字符串加载一个模板, 将source指定的字符串转换成一个模板对象.   该方法是一个实例方法

3.1.17 join_path(template, parent): 不知道用途. 联接一个模板和父模板.  通过默认的查找查找关联的根, 这个方法返回没有修改过的template参数, 但是, 如果paths可以随父模板改变, 这个函数就可以用来得到模板的真实名称.(模板父名称+模板名称???), 子类可以重写这个方法实现自己的模板路径联接

3.1.18 get_template(name, parent = None, globals = None): 加载一个模板, 如果parent参数不是None, 会调用join_path()方法得到模板的真实名称在加载之前.  如果加载器配置了这个方法就会加载name指定的模板.  globals参数用来提供编译时的全局模板参数, 未来可能会允许优化的渲染templates的部分在编译时……….编译不允许有TemplateNotFound异常

3.1.19 overlay(block_start_string = missing, block_end_string = missing, variable_start_string = missing, variable_end_string = missing, comment_start_string = missing, comment_end_string = missing, line_statement_prefix = missing, trim_blocks = missing, extensions = missing, optimized = missing, undefined = missing, finalize = missing, autoescape = missing, loader = missing, cache_size = missing, auto_reload = missing):  获取一个新的Environment, 该对象继承获得所有当前Environment对象的配置属性, 调用此方法传递的参数会覆盖原有的配置属性.

3.2 class Template(): 核心的模板对象, 用来提供使用构造器创建模板对象, 构造器参数和Environment的相同, 但是不能提供loader, 使用构造器创建的模板对象有一个临时的Environment对象, 这个对象和所有其他使用构造器创建的Template共享.   下面介绍Template中的主要成员:

3.2.1 globals: 模板中使用的全局变量???

3.2.2 name: 返回加载模板时指定的名称(如果是FileSystemLoader加载的, 会返回文件名.)

3.2.3 render(*args, **kwargs): 接受关键字参数或字典作为参数.  返回渲染模板的结果字符串

3.2.4 stream(*args, **kwargs): 接受参数同上, 返回一个TemplateStream对象, TemplateStream做类似生成器的工作.

3.2.5 generate(*agrs, **kwargs): 对于很大的模板, 使用generate接口并不会一次渲染整个模板, 而是返回一个生成器, 每次next的时候, 使用yield方式生成一个模板片段.

3.2.6 module: 是一个TemplateModule对象, 该对象对用于运行时导出模板变量.例如:

t = Template(“{% macro foo() %}42{% endmacro %}”)

t.module.foo()就会返回42

3.2.7 make_module(vars = None, shared = False): 不传递参数相当于module属性.  传递参数功能未知.

3.3 class TemplateStream(gen): 一个模板流, 和python的生成器工作原理基本相同, 但是, 这里可以通过更多项的缓存, 以减少迭代次数., 默认是不支持缓存的, 可以通过enable_buffering()开启缓存

3.3.1 disable_buffering(): 展示输出缓存

3.3.2 enable_buffering(size = 5): 开启缓存, 缓存的项数默认是5, 缓存发生的时机在yielding之前

4 Undefined Types: Environment的构造器中提供了设置接口, 如果在模板中查找不到一个名称或不能访问一个属性的时候, 就会创建一个Undefined对象并返回.

4.1 class Undefined(hint = None, obj = None, name = None): 默认的Undefined类型, 这个undefined类型只能被打印, 遍历, 布尔判断, 其他的任何访问都会引发UndefinedError.

4.2 class DebugUndefined(hint = None, obj = None, name = None): 用来打印返回debug信息的Undefined类型

4.3 class StrictUndefined(hint = None, obj = None, name = None):

上下文环境:

5.1 class Context(environment, parent, name, blocks): 用来保存模板的上下文环境变量. 不支持直接创建实例, 因为它是在模板的不同评估阶段自动创建的.  Context对象是不可变的, 不可以在parent中修改, 只允许在创建template代码的时候在vars上修改. 模板的filters和全局函数被标记为contextfunction(), 当前活动的context会作为第一个参数被传递给这些全局函数, 并且是只读的.

上下文环境支持只读的字典操作: get, __getitem__, __contains__, 然而__getitem__失败之后不会发生KeyError, 而是返回一个Undefined对象.

属性解释:

5.3.1 parent: 一个只读的字典, 其中存放全局变量, 这些变量可能来自另外的Context实例, Enviroment.globals或者Template.globals. 他们都是不可修改的.

5.3.2 vars: 模板的本地变量. 是字典类型,  包含了来自parent的environment和 context functions作为本地的可以从模板修改和导出的变量. 模板将会修改这个字典在评估template的时候, 但是filters和context functions是只读的.

5.3.3 environment: 加载这个模板的environment对象

5.3.4 exported_vars: 这个集合包含所有template导出的名称, 名称的值在vars字典中.  可以通过get_exported()方法获得一个新的导出变量字典.

5.3.5 name: 拥有这个context的模板的加载名

5.3.6 blocks: 一个当前已经映射的block字典. keys是所有block的名字, 值是所有block的注册值. 最后一项是当前活动的block

5.3.7 super(name, current): 渲染一个父级的block

5.3.8 get(key, default = None): 获取模板context中的一项, 如果key不存在, 返回default

5.3.9 get_exported(): 返回一个exporeted variables字典

5.3.10 get_all(): 返回一个完整的context拷贝(包含所有的全局变量)

加载器

所有的加载器继承自BaseLoader, 可以通过继承BaseLoader并重写get_source方法创建自己的加载器.

6.2 class BaseLoader(): 所有加载器的基类….Environment 提供的get_template方法会调用加载器的get_source()加载模板.

6.2.1 get_source(environment, template): 获取模板的源, 文件名和reload帮助器.  接受environment和模板名, 返回一个元组(source, filename, uptodate), 如果加载不到, 可以触发TemplateNotFound异常.    返回的source是模板的unicode或ASCII字符串形式.  文件名必须是真正在文件系统中存在的文件全路径, 如果不是从文件系统加载可以返回None, 最后一个返回uptodate是一个函数, 如果自动reloading允许, 就会在模板的源发生改变的时候自动调用这个方法. 这个函数不接受任何参数, 因此需要在函数中保留一些原来的状态, 可以使用闭包来保存状态.  如果这个uptadate方法返回的是False, 模块就会被重新加载一次.

6.2.2 load(environment, name, globals = None): 甲子啊一个模板, 这个方法从缓存中查找一个模板或调用get_source()加载.  子类可以重写这个方法来改变加载方式.

其他的jinja2内建加载器:

6.3.1 class FileSystemLoader(searchpath, encoding = ‘utf-8’): 从文件系统加载

6.3.2 class PackageLoader(package_name, package_path = ‘templates’, encoding = ‘utf-8’): 从包中加载

6.3.3 DictLoader(mapping): 从指定字典中加载

6.3.4 FunctionLoader(load_func): 调用指定的函数加载, 该函数返回一个元组tuple(source, filename, uptodatefunc), 其中uptodatefunc同BaseLoader.get_source()返回中的uptodatefunc, 用来在重加载时判断是否需要重加载.  返回False会进行重加载

6.3.5 PrefixLoader(mapping, delimiter = ‘/’): 前缀加载器, mapping是一个字典, {‘loader1’: loader1, ‘loader2’: loader2}, 则加载’loader1/template.html会调用loader1加载器, delimiter是一个间隔字符串, delimiter之前的就会被作为mapping的key获取具体的加载器.

6.3.6 ChoiceLoader(loaders): 接受一个加载器列表, 在加载模板的时候, 如果使用其中一个加载器加载失败就会选用下一个加载器继续加载.

公共工具: 用来自定义filter和函数

7.1 environmentfilter(f): 装饰器, 标记为environment依赖的filter, filter会作为filter的第一个参数被传递.

7.2 contextfilter(f): 装饰器, 标记为context依赖filter, 当前context会作为第一个参数被传递给filter.

7.3 environmentfunction(f): 该装饰器被用于标记一个可调用对象作为enviroment的可调用对象,

7.4 contextfunction(f): 该装饰器被用于标记一个可调用对象作为context的可调用对象,

7.5 escape(s): 转换(&, <, >, “)字符成为HTML的安全序列.  返回的是一个Markup对象, 由于Markup是标记为安全的不需要escape的, 所以, 一个串是不可能被二次escape的.  如果需要自动转码, 可以将environment的finalize属性设置为escape

7.6 clear_caches(): 清理缓存的接口.

7.7 class Markup()  标记一个string包含的html/xml是安全的, 不需要escape….

异常:

8.1 TemplateError(): 所有模板异常的基类

8.2 UndefinedError(): 模板尝试操作一个Undefined类型的时候触发异常

8.3 TemplateNotFound(name): 模板查找不到的异常, 参数是模板的名称

8.4 TemplateSyntaxError(message, lineno, filename): 模板内部有语法错误的时候触发, message是异常消息, lineno是异常发生的行号, filename是发生异常的模板的文件名.

8.5 TemplateAssertionError(message, lineno, filename): 不懂

自定义过滤器: 在模板中使用{{ obj | myfilter(args)}}对应的就会调用过滤器:

def myfilter(obj, args):

       pass

自定义过滤器的注册: environment.filters[‘filter_name’] = filter, 内建的filters可以通过调用environment.filters获取.

注册为environment过滤器使用装饰器environmentfilter

注册为context过滤器使用装饰器contextfilter

自定义测试: 在模板中使用{% if obj is test_name %}的方式进行测试.

10.1 test返回的必须是True或False.

10.2 test的注册: environment.tests[‘test_name’] = test

同样可以使用environment.tests获取所有内建的tests

注册为environment过滤器使用装饰器environmentfunction??

注册为context过滤器使用装饰器contextfunction???

全局名称空间: Environment.globals, 或Template.globals

section 2 Template Designer

概要:

一个模板就是一个简单的文本文件, 它可以生成任何基于文本的格式(HTML, XML, CSV, LaTeX 等等), 没有明确的扩展名, .html, .xml 或者其他任何都可以.

模板包含变量(或表达式), 当模板被评估(解析)的时候这些变量会被替换成值.

模板有一些标签, 用来控制模板解释时的逻辑.

模板的语法多是使用Django和python创造的.

1.5 {% %}用来执行语句, {{ }}用来返回一个变量的值.

变量:

可以使用.号来获取对象的属性

支持切片操作

如果访问的对象在模板中没有被找到, 就返回一个预定义的Undefined对象.

过滤器:

变量的修改可以使用过滤器.

过滤器的语法是使用管道操作符|, 过滤器可以多重嵌套使用, 前面的过滤器的返回值将会作为下一个过滤器的第一个输入参数.   这里的管道和linux的管道概念上是类似的, 将前面的表达式的返回值交给后面的表达式(或函数)处理

测试: 和filter类似, is前面的值将会被作为test的第一个参数传入, 如果还有其他的参数, test名称后面加空格跟随其他参数, 或者使用方法调用符”()”进行参数的传入.

注释: {#  #}中间包含注释语句.

模板继承: 通过在一个框架模板中定义一些公用的输出, 在通过定义子块block达到代码共享, 这样, 其他的模板继承框架模板, 就不用再写重复的公用代码.   其中的block在父模板中可以有实现, 在子类中通过重写达到多态效果.

定义block: {% block block_name %} {% endblock %}

子模板中继承父模板: {% extends ‘base.html’ %}, 这一句告诉模板引擎, 这个模板继承另一个模板, 当解析这个模板的时候, 首先会本地化它的父模板. extends标签如果有则必须是模板中的第一个标签.

在同一个模板中, 不允许有重名的block

在模板中, 可以通过self获取自身的引用来调用指定名字的block内容, 例如{{self.block_name }}

在block中, 如果重写了父模板的block, 又期望显示父模板的内容(当只是对父模板做了一些填充性的扩展需求发生时), 可以使用{{super()}}来显示父模板中该block的内容

7 HTML Escaping

当从模板生成一个HTML的时候, 会由于变量包含一些HTML标签带来危险. 对于这个问题有两种解决方法: 手动转码每个变量或者通过默认指定自动转码所有的变量

7.2 Jinja2对两种解决方法都支持, 具体使用哪个根据应用程序的配置, 默认的配置是非自动转码.

默认使用手动转码的原因:

自动转码会对所有的变量都进行转码, 对于很多的变量实际上是不包含HTML元素的, 也就不会产生安全影响, 这样转码就会对性能带来影响.

变量的安全信息是非常脆弱的, 可能会因为转码和解码导致数据丢失.

使用手动转码的工作方式:

对于可能包含>, <, &, “等字符的不可靠变量进行转码

转码使用管道调用filter: {{user.username | e}}

使用自动转码的工作方式: 会对所有未设置为安全的变量进行转码

8 For:  {% for user in users %} {% endfor %}: 下面列出for循环块内部可用的变量

8.1 loop.index: 当前迭代索引, 1开始

8.2 loop.index0: 当前迭代索引, 0开始

8.3 loop.revindex: 当前迭代到结束还有多少个元素, 包含当前元素

8.4 loop.revindex: 当前迭代到结束还有多少个元素, 不包含当前元素.

8.5 loop.first: 当前迭代元素是否是第一个元素

8.6 loop.last: 当前迭代元素是否是最后一个元素

8.7 loop.length: 迭代对象一共有多少个元素

8.8 loop.cycle: 返回一个包装的Cycle对象. 该对象描述迭代的当前状态.

9 If: {% if bool_expression %}

10 Macros: {% macro macro_name(args, kwargs) %} 内部可以使用传入的参数, 就像普通的方法的方法体 {% endmacro %}

10.1 Macros的调用: {{macro_name(*args, **kwargs)}}  返回的是定义的macro的标签体内的输出.

内置属性:  可以在{% macro %}和{% endmacro %}之间使用的属性

10.2.1 varargs: 如果调用时传递的位置参数多于定义的位置参数, 多出的会以一个元组的形式保存在这个变量中

10.2.2 kwargs: 如果调用时传递的关键字参数多于定义的关键字参数, 多出的会以一个字典的形式保存在这个变量中

10.2.3 caller: 调用这个macro对象的call对象, 该对象是一个可调用对象, 可以使用{{caller()}}的方式获取call中的自己输出.

外部可调用属性:  可以在macro定义外部通过macro_name.field_name调用的属性

10.3.1 name: 获取macro定义的名称

10.3.2 arguments: 获取定义中的所有参数名称, 返回一个元组

10.3.3 defaults: 获取所有定义中的有默认值的参数的默认值.

10.3.4 catch_kwargs: 如果为True表示macro的定义可以接受扩展关键字参数, 也就是可以接受在定义中没有指定的参数

10.3.5 catch_varargs: 如果为True表示macro的定义可以接受扩展位置参数, 也就是可以接受在定义中没有指定的参数

10.3.6 caller: 布尔类型, 表明在macro定义中是否调用了caller()对象

11 Call: {% call macro_name(*args, **kwargs)%}内部可以定义自己的输出{% endcall %},

用来对macro定义的函数的扩展.

在调用的时候, 还可以使用参数传递:

在macro定义中调用时, 使用{{call(*args, **kwargs)}}的方式进行调用, 就会把参数传递给{% call %}

在{% call %}中需要接受参数时使用{% call(*args, **kwargs) macro_name(*args, **kwargs) %}

12 Assignments: 为模板指定别名的语法: {% navigation = [(‘index.html’, ‘index’), (‘about.html’, ‘about’)] %}

13 Block: 定义模板中的块

14 Include: 将预定义的模板引入到当前模板中, 语法是{% include ‘template_name’%}, 这个标签没有结束符, 默认会传递当前context到被引入的模板

15 Import: {% import ‘template_name’ as new_name %} 将一个模板引入作为新的变量, 就跟模块的引入相似, 引入后可以使用.点号操作符获取其中定义的macro等元素, 也可以使用from-import语法{% from ‘template_name’ import element_name as new_name %}将模板中的元素引入并赋予一个新的名称.  默认不会传递当前的context到被引入的模板

16 Import Context Behavior:

16.1 {% import ‘template_name’ with context %}, 引入template并传递当前context, 同样, from-import的引入方法也使用with context语法

16.2 {% include ‘template_name’ without context %}, 包含一个template, 但是不传递当前context

17 Expressions:

17.1 “string”引号中间的就是字符串

17.2 42 / 42.23: 直接用数值表示float, int

17.3 [1, 2, 3, 4]: 列表

17.4 (1, 2, 3, 4): 元组

17.5 {‘one’: 1, ‘two’: 2} 字典

17.6 true/false

18 +, -, *, /, **, //, %等数学运算符都支持.

逻辑运算符and, or, not也同样支持

其他操作

20.1 in 判断元素是否在集合中

20.2 | 管道操作符, 默认使用Apply调用一个方法

20.3 ~  字符串连接

20.4 ( )  调用可调用对象

20.5 . 和 [ ]获取属性

三元操作: value1 if expression else value2支持

内建filter: (对于内建filter的别名, 可以在jinja2的filters.py文件中查找)

22.1 abs(number)  返回一个值的绝对值

22.2 batch(value, lincount, fill_with = None): 接受的value是一个序列对象, linecount表明个数, 最后生成一个列表, 列表个数为linecount, 如果提供的value长度小于linecount, 不足的使用fill_with填充

22.3 capitalize(string): 接受一个字符串, 将其转换为首字母大写, 其他字母小写的形式返回

22.4 center(value, width = 80): 接受一个字符串, 将其至于80的长度中居中, 不足的字符使用空格填充

22.5 default(value, default_value = u””, boolean = False): 返回value指定的变量的值, 如果value是Undefined那么返回default_value指定的值, boolean意义未知

22.6 dictsort(value, case_sensitive = False, by = ‘key’): value为要遍历的字典, case_sensitive指示是否立即加载, 设置为False表示延时加载, by表示以什么排序, 可以通过设置by = ‘value’来以值排序.

22.7 escape(string): 返回一个转码的安全HTML

22.8 filesizeformat(value): 接受一个数值, 返回人易读的文件大小表示.

22.9 first(sequence): 返回序列的第一个元素

22.10 float(value, default = 0.0): 将接受到的value转换成float类型, 如果转换失败返回指定的default值

22.11 forceescape(value): 强制进行HTML转码, 也就是说不检查要转码的字符串是否是标记为安全的,这样可能会发生二次转码

22.12 format(value, *attribute): 类似于字符串格式化’%d     %s’这样的功能value是格式定义, attribute不定参数接受占位符代表的值.

22.13 groupby(value, attribute): 按照指定的共有属性将集合进行分组, 返回元组组成的列表, 元组中第一个元素是用来分组的属性的值, 第二个元素是分组得到的所有原集合元素的列表.

22.14 indent(string, width = 4, indentfirst = False): 将接受到的string, 每行缩进width指定的字符数, indentfirst用来指定首行是否缩进.

22.15 int(value, default = 0): 将接受到的value转换成int型, 如果转换失败, 返回default指定的值

22.16 join(value, d = u””): 接受一个序列类型的对象, 向序列中进行插空d指定的字符串返回一个字符串

22.17 last(seq): 返回指定序列的最后一个元素

22.18 length(obj): 返回序列或者字典的项数

22.19 list(value): 将接受到的value转换成一个list

22.20 lower(string): 将接受到的字符串转换成小写形式.

22.21 pprint(value, verbose = False): 漂亮的打印一个变量的值, 多用于调试, verbose表示是否显示冗长的信息

22.22 random(seq): 接受一个序列对象, 随机返回其中的一个元素

22.23 replace(string, old, new, count = None): 接受一个字符串, 将其中的old表示的子串替换成new指定的子串, 从左到右替换count次, 如果count不指定, 则替换一次

22.24 reverse(value): 接受一个可迭代对象, 返回逆序的迭代器

22.25 round(value, precision = 0, method = ‘common’): 舍去运算, 接受一个值, precision表示精度(小数点后保留几位), method可以取值common | ceil | floor, 分别表示四舍五入 | 进位 | 舍去

22.26 safe(value): 标记传入的value值是安全的, 使用escape转码时不会发生二次转码

22.27 slice(value, slices, fill_width = None): 切片, 接受一个可迭代对象, 返回slices指定的前n个元素, 不足n个使用fill_width指定的对象进行填充

22.28 sort(value, reverse = False): 接受一个序列对象, 进行排序, reverse指定是否逆序

22.29 string(obj): 接受一个对象, 转换成一个string字符串

22.30 striptags(values): 接受一个字符串, 剥离SGML/XML标签, 并且将多个空白字符转换成单空格

22.31 sum(sequence, start = 0): 接受一个序列对象, 返回序列对象的元素和start的总和, 如果指定的序列对象是空的, 就返回start指定的值

22.32 title(string): 将接受到的字符串转换成标题模式, 即每个单词的首字母大写

22.33 trim(value): 去掉字符串开始和末尾多余的空白字符

22.34 truncate(string, length = 255, killwords = False, end = “…”): 切断接受到的字符串, 截取前length个字符, 如果字符串比length长, 切断后追加end指定的字符串, 如果killwords = True可以返回, 如果killwords = False不会有任何输出, 不明白.

22.35 upper(string) 把接受到的字符串转成大写

22.36 urlize(value, trim_url_limit = None, nofollow = False): 接受一个url, 转换成一个<a>标签表示的link, 这个link的href为传入的url, innerText是url截取前trim_url_limit个字符, nofollow设置为true时, 会为这个link加入一个属性 rel=’nofollow’

22.37 wordcount(string): 计算string中的单词数

22.38 wordwrap(string, width = 79, break_long_words = True): 返回经过包装的width指定宽度的字符, 也就是说每读取width个字符就会换行. , break_long_words表明在获取到width个字符之后, 如果一个单词还没有结束, 是否截断单词, False将不会截断

22.39 xmlattr(d, autospace = True): 通过接受一个字典, 创建一个SGML/XML属性列表, 例如:

<ul {{ {‘class’ = ‘my_list’, ‘missing’: none, ‘id’: ‘list’} | xmlattr }} />

可以得到输出

<ul class=’my_list’ id=’list’ />

字典中指定的值为none的, 将不会被解析成标签属性.

内建测试:

23.1 callable(object): 测试一个对象是否是可调用对象

23.2 defined(value): 测试传入的对象是否已经定义了

23.3 divisibleby(value, num)测试传入的数值是否可以被num整除

23.4 escaped(value): 检查传入的对象是否被转码了

23.5 even(value): 如果传入的对象是even的返回True,  不懂even是什么样的对象

23.6 iterable(value): 检查对象是否是可迭代的

23.7 lower(value): 检查传入的字符串是否都是小写

23.8 none(value): 检查对象是否是空对象None

23.9 number(value): 检查对象是否是一个数字

23.10 odd(value): 检查传入的数字是否是奇数

23.11 sameas(value, other): 检查传入的对象和other指定的对象是否在内存中的同一块地址(同一个对象)

23.12 sequence(value): 检查对象是否是序列, 序列同样是可迭代对象

23.13 string(value): 检查对象是否是string

23.14 undefined(value): 检查一个对象是否未定义

23.15 upper(value): 检查一个字符串是否全部大写

全局函数:

24.1 range([start, ]stop[, step]):

{% for i in range(10) %}

       {{ i }}

{% endfor %}

24.2 lipsum(n = 5, html = True, min = 20, max = 100): 不知道用途

24.3 dict(**items)  根据传入的关键字参数构造一个字典对象.

Socket Basics

Posted on 2012年5月23日 15:23

server

from socket import *

myHost = ''
myPort = 50007

sockobj = socket(AF_INET, SOCK_STREAM)
sockobj.bind((myHost, myPort))
sockobj.listen(5)

while True:
    connection, address = sockobj.accept()
    print('Server connected by', address)
    while True:
        data = connection.recv(1024)
        if not data: break
        connection.send(b'Echo=>' + data)
    connection.close()

client

import sys
from socket import *

serverHost = 'localhost'
serverPort = 50007

message = [b'Hello network world']

if len(sys.argv) > 1:
    serverHost = sys.argv[1]
    if len(sys.argv) > 2:
        message = (x.encode() for x in sys.argv[2:])

sockobj = socket(AF_INET, SOCK_STREAM)
sockobj.connect((serverHost, serverPort))

for line in message:
    sockobj.send(line)
    data = sockobj.recv(1024)
    print('Client received:', data)

sockobj.close()

QGtkStyle was unable to detect the current GTK+ theme.

Posted on 2012年5月22日 20:35

qt-config 没有就下载

 

GUI风格 选择 Cleanlooks