Look into it ~

present
随笔 - 32, 文章 - 0, 评论 - 3, 引用 - 0
数据加载中……

2008年8月15日

android软件

    只有注册用户登录后才能阅读该文。阅读全文

posted @ 2008-11-20 16:24 LukeW 阅读(33) | 评论 (0)编辑 收藏

linux设备模型

Linux 2.6内核的一个重要特色是提供了统一的内核设备模型。随着技术的不断进步,系统的拓扑结构越来越复杂,对智能电源管理、热插拔以及plug and play的支持要求也越来越高,2.4内核已经难以满足这些需求。为适应这种形势的需要,2.6内核开发了全新的设备模型。
1. Sysfs文件系统
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。其顶层目录主要有:
Block目录:包含所有的块设备
Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构
Bus目录:包含系统中所有的总线类型
Drivers目录:包括内核中所有已注册的设备驱动程序
Class目录:系统中的设备类型(如网卡设备,声卡设备等)
2. 内核对象机制关键数据结构
2.1 kobject内核对象
Kobject 是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux 2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。
Kobject结构定义为:
struct kobject {
char * k_name;    // 指向设备名称的指针
char name[KOBJ_NAME_LEN];   // 设备名称
struct kref kref;    // 对象引用计数
struct list_head entry;   // 挂接到所在kset中去的单元
struct kobject * parent; // 指向父对象的指针
struct kset * kset;    // 所属kset的指针
struct kobj_type * ktype;   // 指向其对象类型描述符的指针
struct dentry * dentry; // sysfs文件系统中与该对象对应的文件节点路径指针
};

其中的kref域表示该对象引用的计数,内核通过kref实现对象引用计数管理,内核提供两个函数kobject_get()、kobject_put()分别用于增加和减少引用计数,当引用计数为0时,所有该对象使用的资源将被释放。
Ktype 域是一个指向kobj_type结构的指针,表示该对象的类型。Kobj_type数据结构包含三个域:一个release方法用于释放kobject占 用的资源;一个sysfs_ops指针指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两个函数store()和 show()。当用户态读取属性时,show()函数被调用,该函数编码指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态 传入的属性值。
2.2 kset内核对象集合
Kobject通常通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合,在内核中用kset数据结构表示,定义为:
struct kset {
struct subsystem * subsys;   // 所在的subsystem的指针
struct kobj_type * ktype;   // 指向该kset对象类型描述符的指针
struct list_head list;      // 用于连接该kset中所有kobject的链表头
struct kobject kobj;    // 嵌入的kobject
struct kset_hotplug_ops * hotplug_ops; // 指向热插拔操作表的指针
};

包 含在kset中的所有kobject被组织成一个双向循环链表,list域正是该链表的头。Ktype域指向一个kobj_type结构,被该 kset中的所有kobject共享,表示这些对象的类型。Kset数据结构还内嵌了一个kobject对象(由kobj域表示),所有属于这个kset 的kobject对象的parent域均指向这个内嵌的对象。此外,kset还依赖于kobj维护引用计数:kset的引用计数实际上就是内嵌的 kobject对象的引用计数。
2.3 subsystem内核对象子系统
Subsystem是一系列kset的集合,描述系统中某一 类设备子系统,如block_subsys表示所有的块设备,对应于sysfs文件系统中的block目录。类似的,devices_subsys对应于 sysfs中的devices目录,描述系统中所有的设备。Subsystem由struct subsystem数据结构描述,定义为:
struct subsystem {
struct kset kset;       // 内嵌的kset对象
struct rw_semaphore rwsem; // 互斥访问信号量
};

每 个kset必须属于某个subsystem,通过设置kset结构中的subsys域指向指定的subsystem可以将一个kset加入到该 subsystem。所有挂接到同一subsystem的kset共享同一个rwsem信号量,用于同步访问kset中的链表。

3. 内核对象机制主要相关函数
针对内核对象不同层次的数据结构,linux 2.6内核定义了一系列操作函数,定义于lib/kobject.c文件中。
3.1 kobject相关函数
void kobject_init(struct kobject * kobj);// kobject初始化函数。设置kobject引用计数为1,entry域指向自身,其所属kset引用计数加1

int kobject_set_name(struct kobject *kobj, const char *format, );// 设置指定kobject的名称。

void kobject_cleanup(struct kobject * kobj);
void kobject_release(struct kref *kref);// kobject清除函数。当其引用计数为0时,释放对象占用的资源。

struct kobject *kobject_get(struct kobject *kobj);// 将kobj 对象的引用计数加1,同时返回该对象的指针。

void kobject_put(struct kobject * kobj);// 将kobj对象的引用计数减1,如果引用计数降为0,则调用kobject_release()释放该kobject对象。

int kobject_add(struct kobject * kobj);// 将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录各级kobject的引用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对象的hotplug函数。

int kobject_register(struct kobject * kobj);// kobject注册函数。通过调用kobject_init()初始化kobj,再调用kobject_add()完成该内核对象的注册。

void kobject_del(struct kobject * kobj);// 从Linux设备层次(hierarchy)中删除kobj对象。

void kobject_unregister(struct kobject * kobj);// kobject注销函数。与kobject_register()相反,它首先调用kobject_del从设备层次中删除该对象,再调用kobject_put()减少该对象的引用计数,如果引用计数降为0,则释放该kobject对象。

3.2 kset相关函数
与kobject 相似,kset_init()完成指定kset的初始化,kset_get()和kset_put()分别增加和减少kset对象的引用计数。 Kset_add()和kset_del()函数分别实现将指定keset对象加入设备层次和从其中删除;kset_register()函数完成 kset的注册而kset_unregister()函数则完成kset的注销。
3.3 subsystem相关函数
subsystem有一组完成类似的函数,分别是:
void subsystem_init(struct subsystem *subsys);
int subsystem_register(struct subsystem *subsys);
void subsystem_unregister(struct subsystem *subsys);
struct subsystem *subsys_get(struct subsystem *subsys)
void subsys_put(struct subsystem *subsys);

4. 设备模型组件
在上述内核对象机制的基础上,Linux的设备模型建立在几个关键组件的基础上,下面我们详细阐述这些组件。
4.1 devices
系统中的任一设备在设备模型中都由一个device对象描述,其对应的数据结构struct device定义为:
struct device {
struct list_head g_list;
struct list_head node;
    
struct list_head bus_list;
    
struct list_head driver_list;
    
struct list_head children;
    
struct device *parent;
    
struct kobject kobj;
    
char bus_id[BUS_ID_SIZE];
    
struct bus_type *bus;
    
struct device_driver *driver;
    
void *driver_data;
    
/* Several fields omitted */
};

g_list 将该device对象挂接到全局设备链表中,所有的device对象都包含在devices_subsys中,并组织成层次结构。Node域将该对象挂接 到其兄弟对象的链表中,而bus_list则用于将连接到相同总线上的设备组织成链表,driver_list则将同一驱动程序管理的所有设备组织为链 表。此外,children域指向该device对象子对象链表头,parent域则指向父对象。Device对象还内嵌一个kobject对象,用于引 用计数管理并通过它实现设备层次结构。Driver域指向管理该设备的驱动程序对象,而driver_data则是提供给驱动程序的数据。Bus域描述设 备所连接的总线类型。
内核提供了相应的函数用于操作device对象。其中Device_register()函数将一个新的device对象插 入设备模型,并自动在/sys/devices下创建一个对应的目录。Device_unregister()完成相反的操作,注销设备对象。 Get_device()和put_device()分别增加与减少设备对象的引用计数。通常device结构不单独使用,而是包含在更大的结构中作为一 个子结构使用,比如描述PCI设备的struct pci_dev,其中的dev域就是一个device对象。
4.2 drivers
系统中的每个驱动程序由一个device_driver对象描述,对应的数据结构定义为:
struct device_driver {
    
char *name;   // 设备驱动程序的名称
    struct bus_type *bus; // 该驱动所管理的设备挂接的总线类型
    struct kobject kobj;    // 内嵌kobject对象
    struct list_head devices;  // 该驱动所管理的设备链表头
    int (*probe)(struct device *dev); // 指向设备探测函数,用于探测设备是否可以被该驱动程序管理
int (*remove)(struct device *dev); // 用于删除设备的函数
/*
 some fields omitted*/
};

与device 结构类似,device_driver对象依靠内嵌的kobject对象实现引用计数管理和层次结构组织。内核提供类似的函数用于操作 device_driver对象,如get_driver()增加引用计数,driver_register()用于向设备模型插入新的driver对 象,同时在sysfs文件系统中创建对应的目录。Device_driver()结构还包括几个函数,用于处理热拔插、即插即用和电源管理事件。
4.3   buses
系统中总线由struct bus_type描述,定义为:
struct bus_type {
char   * name; // 总线类型的名称
struct subsystem subsys; // 与该总线相关的subsystem
struct kset drivers; // 所有与该总线相关的驱动程序集合
struct kset devices; // 所有挂接在该总线上的设备集合
struct bus_attribute * bus_attrs; // 总线属性
struct device_attribute * dev_attrs; // 设备属性
struct driver_attribute * drv_attrs;   // 驱动程序属性
int (*match)(struct device * dev, struct device_driver * drv);
int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
int (*suspend)(struct device * dev, u32 state);
int (*resume)(struct device * dev);
};

每 个bus_type对象都内嵌一个subsystem对象,bus_subsys对象管理系统中所有总线类型的subsystem对象。每个 bus_type对象都对应/sys/bus目录下的一个子目录,如PCI总线类型对应于/sys/bus/pci。在每个这样的目录下都存在两个子目 录:devices和drivers(分别对应于bus_type结构中的devices和drivers域)。其中devices子目录描述连接在该总 线上的所有设备,而drivers目录则描述与该总线关联的所有驱动程序。与device_driver对象类似,bus_type结构还包含几个函数 (match()、hotplug()等)处理相应的热插拔、即插即拔和电源管理事件。
4.4 classes
系统中的设备类由 struct class描述,表示某一类设备。所有的class对象都属于class_subsys子系统,对应于sysfs文件系统中的/sys/class目录。 每个class对象包括一个class_device链表,每个class_device对象表示一个逻辑设备,并通过struct class_device中的dev域(一个指向struct device的指针)关联一个物理设备。这样,一个逻辑设备总是对应于一个物理设备,但是一个物理设备却可能对应于多个逻辑设备。此外,class结构中 还包括用于处理热插拔、即插即拔和电源管理事件的函数,这与device对象和driver对象相似。

posted @ 2008-11-12 23:14 LukeW 阅读(182) | 评论 (0)编辑 收藏

位运算

C中的位运算
能够运用到任何整形的数据类型上(包括char, int), 无论有没有short, long, unsigned这样的限定词.


位运算的应用
// 交换指针变量x,y所指向的存储位置处存放的值
// 优势是不需要第三个位置来临时存储另一个值
// 但是这个方法并没有明显的性能优势,只是一个智力上的消遣
void inplace_swap(int *x, int *y)
{
 
*= *^ *y;
 
*= *^ *y;
 
*= *^ *y;
}

位运算常见用法:
实现掩码运算



-----------------------------------
Java中的位运算


posted @ 2008-11-12 13:53 LukeW 阅读(130) | 评论 (0)编辑 收藏

大端小端 -- 各系统及机器的信息表示

因为现行的计算机都是以八位一个字节为存储单位,那么一个16位的整数,也就是C语言中的short,在内存中可能有两种存储顺序big-

endian和litte-endian.考虑一个short整数0x3132(0x32是低位,0x31是高位),把它赋值给一个short变量,那么它在内存中的存储可

能有如下两种情况:
大端字节(Big-endian):

short变量地址
       0x1000                  0x1001
___________________________________
|                 |
|         0x31    |       0x32
|________________ | ________________
高位字节在低位字节的前面,也就是高位在内存地址低的一端.可以这样记住(大端->高位->在前->正常的逻辑顺序)
 
小端字节(little-endian):

short变量地址
       0x1000                  0x1001
_____________________________________
|                 |
|         0x32    |       0x31
|________________ | __________________
低位字节在高位字节的前面,也就是低位在内存地址低的一端.可以这样记住(小端->低位->在前->与正常逻辑顺序相反)
 
可以做个实验
在windows上下如下程序
#include <stdio.h>
#include 
<assert.h>
 
void main( void )
{
        
short test;
        FILE
* fp;
        
        test 
= 0x3132;  //(31ASIIC码的’1’,32ASIIC码的’2’)
        if ((fp = fopen ("c:""test.txt""wb")) == NULL)
              assert(
0);
        fwrite(
&test, sizeof(short), 1, fp);
        fclose(fp);
}

    然后在C盘下打开test.txt文件,可以看见内容是21,而test等于0x3132,可以明显的看出来x86的字节顺序是低位在前.如果我们
把这段同样的代码放到(big-endian)的机器上执行,那么打出来的文件就是12.这在本机中使用是没有问题的.但当你把这个文件从一
个big- endian机器复制到一个little-endian机器上时就出现问题了.

    如上述例子,我们在big-endian的机器上创建了这个test文件,把其复制到little-endian的机器上再用fread读到一个 short里
面,我们得到的就不再是0x3132而是0x3231了,这样读到的数据就是错误的,所以在两个字节顺序不一样的机器上传输数据时需要特别
小心字节顺序,理解了字节顺序在可以帮助我们写出移植行更高的代码.

正因为有字节顺序的差别,所以在网络传输的时候定义了所有字节顺序相关的数据都使用big-endian,BSD的代码中定义了四个宏来处
理:
#define ntohs(n)     //网络字节顺序到主机字节顺序 n代表net, h代表host, s代表short
#define htons(n)     //主机字节顺序到网络字节顺序 n代表net, h代表host, s代表short
#define ntohl(n)      //网络字节顺序到主机字节顺序 n代表net, h代表host, s代表 long
#define htonl(n)      //主机字节顺序到网络字节顺序 n代表net, h代表host, s代表 long

举例说明下这其中一个宏的实现:
 #define sw16(x) "
    ((
short)( "
        (((short)(x) & (short)0x00ffU<< 8| "
        (((short)(x) & (short)0xff00U>> 8) ))

这里实现的是一个交换两个字节顺序.其他几个宏类似.

我们改写一下上面的程序
#include <stdio.h>
#include 
<assert.h>

#define sw16(x) "
    ((
short)( "
        (((short)(x) & (short)0x00ffU<< 8| "
        (((short)(x) & (short)0xff00U>> 8) ))

// 因为x86下面是低位在前,需要交换一下变成网络字节顺序
#define htons(x) sw16(x)
 
void main( void )
{
        
short test;
        FILE
* fp;
        
        test 
= htons(0x3132); //(31ASIIC码的’1’,32ASIIC码的’2’)
        if ((fp = fopen ("c:""test.txt""wb")) == NULL)
              assert(
0);
        fwrite(
&test, sizeof(short), 1, fp);
        fclose(fp);
}

 
    如果在高字节在前的机器上,由于与网络字节顺序一致,所以我们什么都不干就可以了,只需要把#define htons(x) sw16(x)宏替

换为 #define htons(x) (x).
    一开始我在理解这个问题时,总在想为什么其他数据不用交换字节顺序?比如说我们write一块buffer到文件,最后终于想明白了,

因为都是unsigned char类型一个字节一个字节的写进去,这个顺序是固定的,不存在字节顺序的问题.

【用函数判断系统是Big Endian还是Little Endian】


bool IsBig_Endian()
//如果字节序为big-endian,返回true;
//反之为   little-endian,返回false
{
    unsigned 
short test = 0x1122;
    
if(*( (unsigned char*&test ) == 0x11)
       
return TRUE;
else
    
return FALSE;

}
//IsBig_Endian()

【打印程序对象的字节表示】
// 可在不同平台与硬件架构的机器中测试运行这段代码,理解大端表示和小端表示的不同.
// 这段代码使用强制类型转换规避类型系统
#incluede <stdio.h>

// 假设每个字节都是非负整数
typedef unsigned char *byte_pointer;

void show_bytes(byte_pointer start, int len)
{
 
for(int i = 0; i < len; i++)
  printf(
" %.2x", start[i]);
 printf(
"\n");
}

void show_int(int x)
{
 show_bytes((byte_pointer) 
&x, sizeof(int));
}

void show_float(float x)
{
 show_bytes((byte_pointer) 
&x, sizeof(float));
}

// 在使用相同编码(如ASCII编码)的系统中,字符串字节表示得到的结果一般是相同的.所以文本数据比二进制数据具有更强的平台无关性
void show_string(char *x)
{
 show_bytes((byte_pointer) x, strlen(x));
}

void show_pointer(void *x)
{
 show_bytes((byte_pointer) 
&x, sizeof(void *));
}

void test_show_bytes(int val)
{
 
int ival = val;
 
float fval = (float)ival;
 
int *pval = &ival;
 
 show_int(ival); 
// 各个机器因为大端表示和小端表示的不同,从而只是字节顺序不同
 show_float(fval); // 各个机器因为大端表示和小端表示的不同,从而只是字节顺序不同
 show_pointer(pval); // 指针值是与机器相关的(linux,sun使用4字节地址, 而alpha使用八字节地址)
}

---------------------------------------------
对于如数值12345在int型和float型时的编码表示

posted @ 2008-11-12 11:58 LukeW 阅读(652) | 评论 (0)编辑 收藏

点子

    只有注册用户登录后才能阅读该文。阅读全文

posted @ 2008-11-06 18:17 LukeW 阅读(41) | 评论 (0)编辑 收藏

j2me 联网技术分析总结

基本点:

Generic Connections

In the CLDC Generic Connection framework, all connections are created using the open static method from the Connector class. If successful, this method returns an object that implements one of the generic connection interfaces. Figure 1 shows how these interfaces form an is-a hierarchy. The Connection interface is the base interface such that StreamConnectionNotifier is a Connection and InputConnection is a Connection too.

fig1.gif
Figure 1: Connection interface hierarchy
  • The Connection interface is the most basic connection type. It can only be opened and closed.
  • The InputConnection interface represents a device from which data can be read. Its openInputStream method returns an input stream for the connection.
  • The OuputConnection interface represents a device to which data can be written. Its openOutputStream method returns an output stream for the connection.
  • The StreamConnection interface combines the input and output connections.
  • The ContentConnection is a subinterface of StreamConnection. It provides access to some of the basic meta data information provided by HTTP connections.
  • The StreamConnectionNotified waits for a connection to be established. It returns a StreamConnection on which a communication link has ben established.
  • The DatagramConnection represents a datagram endpoint.

The open method of the Connector class has the following syntax, where the String parameter has the format "protocol:address;parameters".

Connector.open(String);

Here are a few examples:

HTTP Connection

Connector.open("http://java.sun.com/developer");

Datagram Connection

Connector.open("datagram://address:port#");

Communicate with a Port

Connector.open("comm:0;baudrate=9600');

Open Files

Connector.open("file:/myFile.txt");


The HttpConnection Interface:

The HTTP protocol is a request-response application protocol in which the parameters of the request must be set before the request is sent. The connection could be in one of the three following states:
  • Setup: No connection yet
  • Connected: Connection has been made, the request has been sent, and some response is expected
  • Closed: Connection is closed

In the setup state the following methods can be invoked:

  • setRequestMethod
  • setRequestProperty

For example, suppose you have this connection:

HttpConnection c = (HttpConnection)
Connector.open(
"http://java.sun.com/developer");

Then, you can set the request method to be of type POST as follows:

c.setRequestMethod(HttpConnection.POST);

And likewise, you can set some of the HTTP properties. For example, you can set the User-Agent as follows:

c.setRequestProperty("User-Agent","Profile/MIDP-1.0 Configuration/CLDC-1.0");

If there is a method that requires data to be sent or received from the server, there is a state transition from Setup to Connected. Examples of methods that cause the transition include:

openInputStream
openOutputStream
openDataInputStream
openDataOutputStream
getLength
getType
getDate
getExpiration

And while the connection is open, some of these methods that may be invoked:

getURL
getProtocol
getHost
getPort


------------------------------------------------------------
要注意的问题:
开发中遇到个很头疼的问题, 与服务器通信write()数据时报java.io.IOException: Couldn't write to socket.
但是服务器抓不到任何包. 一开始怀疑是连建立连接出的问题, 实际上服务器抓不到包也有可能是流在没有close的时候就已经报错了.
如:
conn.open("url");
out = conn.openDataOutputStream();//此时将进行与服务器的三次握手;
                                  //但是如果在out.close()之前出现异常服务器是抓不到任何包的
out.write(byte[] bb);

关于这个的解释应该是流的缓冲机制.
所以正确的写法应该是捕捉到异常之后在catch块中把流close掉.
服务器端开发人员一般会说收不到包所以连接有问题,会把责任推给客户端,抓住这个证据在跟服务器端的同事扯皮时将处于有利的位置,嘎嘎.
还有就是要多做小实验, 注意代码要规范严格.

发现的几个问题:

1. java.io.IOException: Couldn't write to socket

2. java.io.IOException: Couldn't read from socket

CMNET联网方案:

CMWAP联网方案:

移动资费页的处理:

一个通用的HTTP连接封装:

posted @ 2008-11-04 16:22 LukeW 阅读(373) | 评论 (0)编辑 收藏

VIM设置

vim简介
 
Vim(Vi Improved) 是一个类似于vi 的文本编辑器,在Vi的基础上增加了很多新的特性和功能。Vim以其强大的功能和可定制能力

,成为Linux/Unix环境下开源的最重要的编辑器之一(另一个是 Emacs),被众多开发者所喜爱。笔者此时所用的是最新的7.1版本


与大部分其它编辑器不同,进入 Vim 后,缺省状态下键入的字符并不会插入到所编辑的文件之中。Vim 的模式(mode,可以简单地

理解为“状态”)概念非常重要。需要知道,Vim 有以下几个模式:
1)   正常(normal)模式,缺省的编辑模式;下面如果不加特殊说明,提到的命令都直接在正常模式下输入;任何其它模式中都

可以通过键盘上的 Esc 键回到正常模式。
2)   命令(command)模式,用于执行较长、较复杂的命令;在正常模式下输入“:”(一般命令)、“/”(正向搜索)或“?”

(反向搜索)即可进入该模式;命令模式下的命令要输入回车键(Enter)才算完成。
3)   插入(insert)模式,输入文本时使用;在正常模式下键入“i”(insert)或“a”(append)即可进入插入模式(也有另

外一些命令,如“c”,也可以进入插入模式,但这些命令有其它的作用)。
4)   可视(visual)模式,用于选定文本块;可以在正常模式下输入“v”(小写)来按字符选定,输入“V”(大写)来按行选

定,或输入“Ctrl-V”来按方块选定。
5)   选择(select)模式,与普通的 Windows 编辑器较为接近的选择文本块的方式;在以可视模式和选择模式之一选定文本块之

后,可以使用“Ctrl-G”切换到另一模式——该模式很少在 Linux 上使用,本文中就不再介绍了。
------------------------------------------
首先 vim ~/.vimrc 打开编辑文件

[转]
   1、VI或VIM的配置文件的路径

    发现/usr/share/vim/vimrc和/etc/vim/vimrc指向是同一个文件,即vimrc,为vi和vim的配置文件,修改这个文件即可。这个路径在不同的LINUX版本中可能会不同。

    2、配置颜色

    配轩VI和VIM的颜色显示,使它能够高亮度显示一些特别的单词,这对编写程序很有用。后来打开文件发现里面其实已经有一行了,只是用引号注释掉了,只需 将syntax on 所在行前面的引号去掉即可。或者另外独立添加一行:syntax on 也行,另外编辑/etc/profile 增加一行alias vi="vim"就行了。

    3、设置鼠标

    使用VI编辑文本时,如果想修文件中改离光标较远的位置,这时候想用鼠标定位,可默认情况下,鼠标是不可用的。如果你想使用鼠标,只需另起一行,写上:set mouse=a 即可

    4、设置自动缩进

    默认情况下,VI和VIM都没有缩进的,每换一行,光标均定位在顶格,如果你想自动对齐,请将 set autoindent所在行前面的引号去掉,或者另外添加一行:set autoindent也可。这与配置颜色类似。这样的设置的结果是按回车后新行与上一行自动对齐。

    5、设置tab的缩进量

    如果用python编写程序,那么行缩进量是一个极其重要的概念,同一个块的缩进量必须相同。你可能喜欢在行前加空格来表示缩进,但每次必须敲多次空格 键,如果你喜欢用按TAB键来表示缩进,你可能觉得写的文本或程序不太好看,因为默认情况下,VI和VIM的TAB缩进量比较大(至少六,七个字符)。设 置TAB键缩进量的方法:set shiftwidth=3 你也可以选一 个你自己喜欢的缩进量,比如2,或4.

----------------------------------------------

首先从视觉方面:

第一个要做的是缩进,修改你的配置文件_vimrc,在最后加入set cindent,这样就设置了c风格的缩进,在这里缩进的大小是shiftwidth的值。

第二个要做的是语法高亮,这个是必须的,在中_vimrc加入syntax enable

第三个要做的字体的设置,设置一个舒服的字体可以让你编程的时候舒服好多,用editplus的时候我就用的Consolas,在中我还是用的这种字体,在_vimrc中加入

if has(”gui_running”)
set guifont=Consolas:h9
endif

表示运行界面的时候就用这种字体。

第四,设置配色方案,可以到点击这儿下载,然后放到$"vimfiles"colors这个目录下,然后在中加入如下配置

if has(”gui_running”)
set guifont=Consolas:h9
” set color schema
colorscheme
colorscheme_name
endif

colorscheme_name为你需要设置的配色方案的名称。

接下来是在运行程序中用到的:

第一,使用ctag

中已经带了Ctags这个程序。尽管ctags也可以支持其它编辑器,但是它正式支持的只有。Ctags可以帮助程序员很容易地浏览源代码。用下面的命令可以在源代码的根目录下创建“tags”文件:

[/home/brimmer/src]$ ctags -R

“-R”表示递归创建,也就包括源代码根目录下的所有子目录下的源程序。“tags”文件中包括这些对象的列表:

l        用#define定义的宏

l        枚举型变量的值

l        函数的定义、原型和声明

l        名字空间(namespace)

l        类型定义(typedefs)

l        变量(包括定义和声明)

l        类(class)、结构(struct)、枚举类型(enum)和联合(union)

l        类、结构和联合中成员变量或函数

用这个“tags”文件来定位上面这些做了标记的对象,下面介绍一下定位这些对象的方法:

1)        用命令行。在运行的时候加上“-t”参数,例如:

[/home/brimmer/src]$   -t  foo_bar

这个命令将打开定义“foo_bar”(变量或函数或其它)的文件,并把光标定位到这一行。

2)        在编辑器内用“:ta”命令,例如:

:ta foo_bar

3)        最方便的方法是把光标移到变量名或函数名上,然后按下“Ctrl-]”。用“Ctrl-o”退回原来的地方。

注意:运行的时候,必须在“tags”文件所在的目录下运行。否则,运行的时候还要用“:set tags=”命令设定“tags”文件的路径,这样才能找到“tags”文件。

你还可以选择使用taglist这个插件,这个插件可以在右侧显示函数,变量等的列表

第二,改正程序中的错误

编辑器的环境下用“:make”(make工具的使用已经在我昨天的文章中 提到的云风的几篇文章中详细介绍到)就可以编译程序,当然其前提是在当前目录下有Makefile文件。运行完“:make”之后,如果程序中有错误,就 会显示出来。这时候,光标会自动指向第一个出现错误的地方,而且你还可以看到错误的提示。然后,你就可以改正错误,而不用手工找到出错的那一行。记住下面 几个有用的命令:

l        “:cl”列出错误

l        “:cn”让光标指向下一个错误

l        “:cp”让光标指向上一个错误

l        “:cnew”从头开始

你甚至可以让识别出其它编译器而不是gcc的错误提示。这对一些开发嵌入式系统的程序员这很有用,因为他们很可能用的不是gcc而是其它编译器。通过设置“errorformat”的值,可以让识别出编译器的出错提示。因为不同的编译器的出错提示是不同的,所以如果用的不是gcc就要重新设置。

errorformat”的值是一个字符串,它的格式和C语言的scanf的字符串格式相识。

gcc的“errorformat”的值为:%f:%l:"%m。其中“%f”表示文件名,“%l”表示行号,“%m”表示出错信息。

用“:h errorformat”查看详细的帮助信息。

“:h quickfix”、“:h make”、“:h makeprg”、“:h errorfile”查看其它的信息。

第三,使用快捷键

下面的这些快捷键对程序员很有帮助:
在函数中移动光标

[[  转到上一个位于第一列的“{”

]]  转到下一个位于第一列的“{”

{   转到上一个空行

}   转到下一个空行

gd  转到当前光标所指的局部变量的定义

*   转到当前光标所指的单词下一次出现的地方

#   转到当前光标所指的单词上一次出现的地方
括号的匹配

%   用来进行小括号、中括号和大括号的匹配。这要看当前光标指向的是什么符号了。

----------------------------------------------

一个ubuntu 下的vimrc配置例子:

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 一般设定
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 设定默认解码
set fenc
=utf-8
set fencs
=utf-8,usc-bom,euc-jp,gb18030,gbk,gb2312,cp936

"设定搜索是的高亮显示
set hlsearch

" 不要使用vi的键盘模式,而是vim自己的
set nocompatible

" history文件中需要记录的行数
set history=100

" 在处理未保存或只读文件的时候,弹出确认
set confirm

" 与windows共享剪贴板
set clipboard+=unnamed

" 侦测文件类型
filetype on

" 载入文件类型插件
filetype plugin on

" 为特定文件类型载入相关缩进文件
filetype indent on

" 保存全局变量
set viminfo+=!

" 带有如下符号的单词不要被换行分割
set iskeyword
+=_,$,@,%,#,-

" 语法高亮
syntax on

" 高亮字符,让其不受100列限制
:highlight OverLength ctermbg
=red ctermfg=white guibg=red guifg=white
":match OverLength '"%101v.*'

" 状态行颜色
highlight StatusLine guifg=SlateBlue guibg=Yellow
highlight StatusLineNC guifg=Gray guibg=White

"高亮当前行
set cursorline

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 文件设置
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 不要备份文件(根据自己需要取舍)
set nobackup

" 不要生成swap文件,当buffer被丢弃的时候隐藏它
setlocal noswapfile
set bufhidden=hide

" 字符间插入的像素行数目
set linespace
=0

" 增强模式中的命令行自动完成操作
set wildmenu

" 在状态行上显示光标所在位置的行号和列号
set ruler
set rulerformat
=%20(%2*%<%f%=" %m%r" %3l" %c" %p%%%)

" 命令行(在状态行下)的高度,默认为1,这里是2
set cmdheight=2

" 使回格键(backspace)正常处理indent, eol, start等
set backspace
=2

" 允许backspace和光标键跨越行边界
set whichwrap+=<,>,h,l

" 可以在buffer的任何地方使用鼠标(类似office中在工作区双击鼠标定位)
set mouse
=a
set selection
=exclusive
set selectmode
=mouse,key

" 启动的时候不显示那个援助索马里儿童的提示
set shortmess=atI

" 通过使用: commands命令,告诉我们文件的哪一行被改变过
set report
=0

" 不让vim发出讨厌的滴滴声
set noerrorbells

" 在被分割的窗口间显示空白,便于阅读
set fillchars
=vert:" ,stl:" ,stlnc:"

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 搜索和匹配
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 高亮显示匹配的括号
set showmatch

" 匹配括号高亮的时间(单位是十分之一秒)
set matchtime
=5

" 在搜索的时候不忽略大小写
set noignorecase

" 不要高亮被搜索的句子(phrases)
"set nohlsearch

" 在搜索时,输入的词句的逐字符高亮(类似firefox的搜索)
set incsearch

" 输入:set list命令是应该显示些啥?
set listchars=tab:
"|" ,trail:.,extends:>,precedes:<,eol:$

" 光标移动到buffer的顶部和底部时保持3行距离
set scrolloff
=3

" 不要闪烁
set novisualbell

" 我的状态行显示的内容(包括文件类型和解码)
set statusline
=%F%m%r%h%w" [FORMAT=%{&ff}]" [TYPE=%Y]" [POS=%l,%v][%p%%]" %{strftime(""%d/%m/%y" -" %H:%M"")}

" 总是显示状态行
set laststatus=2

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 文本格式和排版
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 自动格式化
set formatoptions=tcrqn

" 继承前一行的缩进方式,特别适用于多行注释
set autoindent

" 为C程序提供自动缩进
set smartindent

" 使用C样式的缩进
set cindent

" 制表符为4
set tabstop=4

" 统一缩进为4
set softtabstop
=4
set shiftwidth
=4

" 不要用空格代替制表符
set noexpandtab

" 不要换行
"set nowrap

"设置每行80个字符自动换行
set textwidth
=80

" 在行和段开始处使用制表符
set smarttab

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" CTags的设定
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 按照名称排序
let Tlist_Sort_Type = 
"name"

" 在右侧显示窗口
let Tlist_Use_Right_Window 
= 1

" 压缩方式
let Tlist_Compart_Format = 1

" 如果只有一个buffer,kill窗口也kill掉buffer
let Tlist_Exist_OnlyWindow 
= 1

" 不要关闭其他文件的tags
let Tlist_File_Fold_Auto_Close = 0

" 不要显示折叠树
let Tlist_Enable_Fold_Column 
= 1

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" Autocommands
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" 只在下列文件类型被侦测到的时候显示行号,普通文本文件不显示

if has("autocmd")
   autocmd FileType xml,html,c,cs,java,perl,shell,bash,cpp,python,vim,php,ruby set number
   autocmd FileType xml,html vmap 
<C-o> <ESC>'<i<!--<ESC>o<ESC>'>o-->
   autocmd FileType java,c,cpp,cs vmap 
<C-o> <ESC>'<o/*<ESC>'>o*/
   autocmd FileType html,text,php,vim,c,java,xml,bash,shell,perl,python setlocal textwidth
=80
   autocmd Filetype html,xml,xsl source $VIMRUNTIME
/plugin/closetag.vim
   autocmd BufReadPost 
*
      
" if line("'""") > 0 && line("'""") <= line("$") |
      
"   exe "normal g`""" |
      
" endif
endif 
" has("autocmd")

" C/C++的编译和运行
map <F5> :call CompileRunGcc()<CR>
func! CompileRunGcc()
exec 
"w"
exec 
"!make"
exec 
"! ./%<"
endfunc

" shell script运行
map 
<F6> :call CompileRunSH()<CR>
func
! CompileRunSH()
exec 
"w"
exec 
"!chmod a+x %"
exec 
"!./%"
endfunc

" python运行
map <F7> :call CompileRunPyhton()<CR>
func! CompileRunPyhton()
exec 
"w"
exec 
"!chmod a+%"
exec 
"!./%"
endfunc

" 能够漂亮地显示.NFO文件
set encoding
=utf-8
function! SetFileEncodings(encodings)
    let b:myfileencodingsbak
=&fileencodings
    let 
&fileencodings=a:encodings
endfunction
function! RestoreFileEncodings()
    let 
&fileencodings=b:myfileencodingsbak
    unlet b:myfileencodingsbak
endfunction

au BufReadPre 
*.nfo call SetFileEncodings('cp437')|set ambiwidth=single
au BufReadPost 
*.nfo call RestoreFileEncodings()

" 高亮显示普通txt文件(需要txt.vim脚本)
au BufRead,BufNewFile *  setfiletype txt

" 用空格键来开关折叠
set foldenable
set foldlevel
=0
set foldmethod
=indent
nnoremap 
<space> @=((foldclosed(line('.')) < 0? 'zc' : 'zo')<CR>

" minibufexpl插件的一般设置
let g:miniBufExplMapWindowNavVim = 1
let g:miniBufExplMapWindowNavArrows = 1
let g:miniBufExplMapCTabSwitchBufs = 1
let g:miniBufExplModSelTarget = 1

----------------------
一个相关帖子
http://forum.ubuntu.org.cn/viewtopic.php?f=68&t=138212&st=0&sk=t&sd=a

 

posted @ 2008-11-03 13:02 LukeW 阅读(1630) | 评论 (0)编辑 收藏

servlet过滤器

servlet过滤器

1. Servlet过滤器基础
Servlet过滤器是Servlet的一种特殊用法,主要用来完成一些通用的操作。比如编码的过滤,判断用户的登陆状态等等。Servlet过滤器的适用场合:
A.认证过滤
B.登录和审核过滤
C.图像转换过滤
D.数据压缩过滤
E.加密过滤
F.令牌过滤
G.资源访问触发事件过滤
Servlet过滤器接口的构成:
所有的Servlet过滤器类都必须实现javax.servlet.Filter接口。这个接口含有3个过滤器类必须实现的方法:
方法 说明
init(FilterConfig cfg) 这是Servlet过滤器的初始化方法,性质等同与servlet的init方法。
doFilter(ServletRequest,ServletResponse,FilterChain) 完成实际的过滤操作,当请求访问过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain参数用于访问后续过滤器
destroy() Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源。性质等同与servlet的destory()方法。
Servlet过滤器的创建步骤:
A.实现javax.servlet.Filter接口的servlet类
B.实现init方法,读取过滤器的初始化函数
C.实现doFilter方法,完成对请求或过滤的响应
D.调用FilterChain接口对象的doFilter方法,向后续的过滤器传递请求或响应
F.在web.xml中配置Filter
2.使用过滤器处理中文问题
   当用用户登陆页面输入帐号时,如果输入是中文,后台servlet再次输出这个内容时,可能就会是乱码,这是因为serlvet中默认是以ISO-8859-1格式编码的,如果后台有多个Servlet,多个参数,这样就不合适,这个问题,我们可以通过一个过滤器统一解决,使后台的输出输出都支持中文!将ISO-8859-1转码为GBK的那段代码!
3.使用过滤器认证用户:
每个过滤器也可以配置初始化参数,可以将不需要过滤的地址配置到这个Filter的配置参数中,过滤时,如果请求地址在配置参数中,则放行,这样 就避免了在程序中硬编码。每个Filter中初始化时,都可以得到配置对象,在Filter中配置二个不需要过滤的地址,一个是登陆页面,一个是执行登陆 认证的servlet;
4.Servlet监听器
类似与Swing界面应用开发,Servlet也可以创建监听器,以对Servlet容器,或Servlet中以象的事件做出反应。Servlet监听器主要有以下几种:
ServletRequestListener ,ServletRequestAttributeListener,
HttpSessionActivationListener ,HttpSessionBindingListener ,
HttpSessionAttributeListener,HttpSessionListener,
ServletContextListener等等。
这些监听器主要用来监听session,request,application这三个对象里存取数据的变化。
----------------------------------------------------------------------------------------------------------------

servlet API中最重要的一个功能就是能够为servlet和JSP页面定义过滤器。过滤器提供了某些早期服务器所支持的非标准“servlet链接”的一种功能强大且标准的替代品。
                                                                                                
     过滤器是一个程序,它先于与之相关的servlet或JSP页面运行在服务器上。过滤器可附加到一个或多个servlet或JSP页面上,并且可以检查进入这些资源的请求信息。在这之后,过滤器可以作如下的选择:

1. 以常规的方式调用资源(即,调用servlet或JSP页面)。

2.利用修改过的请求信息调用资源。

3. 调用资源,但在发送响应到客户机前对其进行修改

4. 阻止该资源调用,代之以转到其他的资源,返回一个特定的状态代码或生成替换输出。

过滤器提供了几个重要好处 :


        首先,它以一种模块化的或可重用的方式封装公共的行为。你有30个不同的serlvet或JSP页面,需要压缩它们的内容以减少下载时间吗?没问题:构造一个压缩过滤器,然后将它应用到30个资源上即可。

其次,利用它能够将高级访问决策与表现代码相分离。这对于JSP特别有价值,其中一般希望将几乎整个页面集中在表现上,而不是集中在业务逻辑上。例如,希 望阻塞来自某些站点的访问而不用修改各页面(这些页面受到访问限制)吗?没问题:建立一个访问限制过滤器并把它应用到想要限制访问的页面上即可。

     最后,过滤器使你能够对许多不同的资源进行批量性的更改。你有许多现存资源,这些资源除了公司名要更改外其他的保持不变,能办到么?没问题:构造一个串替换过滤器,只要合适就使用它。

     但要注意,过滤器只在与servlet规范2.3版兼容的服务器上有作用。如果你的Web应用需要支持旧版服务器,就不能使用过滤器。

1.   建立基本过滤器

建立一个过滤器涉及下列五个步骤:
1)建立一个实现Filter接口的类。这个类需要三个方法,分别是:doFilter、init和destroy。
       doFilter方法包含主要的过滤代码(见第2步),init方法建立设置操作,而destroy方法进行清楚。

2)在doFilter方法中放入过滤行为。doFilter方法的第一个参数为ServletRequest对象。此对象给过滤器提供了对进入的信息 (包括表单数据、cookie和HTTP请求头)的完全访问。第二个参数为ServletResponse,通常在简单的过滤器中忽略此参数。最后一个参 数为FilterChain,如下一步所述,此参数用来调用servlet或JSP页。

3)调用FilterChain对象的doFilter方法。Filter接口的doFilter方法取一个FilterChain对象作为它的一个参 数。在调用此对象的doFilter方法时,激活下一个相关的过滤器。如果没有另一个过滤器与servlet或JSP页面关联,则servlet或JSP 页面被激活。

4)对相应的servlet和JSP页面注册过滤器。在部署描述符文件(web.xml)中使用filter和filter-mapping元素。

5)禁用激活器servlet。防止用户利用缺省servlet URL绕过过滤器设置。

1.1   建立一个实现Filter接口的类
      所有过滤器都必须实现javax.servlet.Filter。这个接口包含三个方法,分别为doFilter、init和destroy。

public void doFilter(ServletRequset request,
                     ServletResponse response,
                     FilterChain chain)
     thows ServletException, IOException

每当调用一个过滤器(即,每次请求与此过滤器相关的servlet或JSP页面)时,就执行其doFilter方法。正是这个方法包含了大部分过滤逻辑。 第一个参数为与传入请求有关的ServletRequest。对于简单的过滤器,大多数过滤逻辑是基于这个对象的。如果处理HTTP请求,并且需要访问诸 如getHeader或getCookies等在ServletRequest中无法得到的方法,就要把此对象构造成 HttpServletRequest。

第二个参数为ServletResponse。除了在两个情形下要使用它以外,通常忽略这个参数。首先,如果希望完全阻塞对相关servlet或JSP页 面的访问。可调用response.getWriter并直接发送一个响应到客户机。其次,如果希望修改相关的servlet或JSP页面的输出,可把响 应包含在一个收集所有发送到它的输出的对象中。然后,在调用serlvet或JSP页面后,过滤器可检查输出,如果合适就修改它,之后发送到客户机。

DoFilter的最后一个参数为FilterChain对象。对此对象调用doFilter以激活与servlet或JSP页面相关的下一个过滤器。如果没有另一个相关的过滤器,则对doFilter的调用激活servlet或JSP本身。

public void init(FilterConfig config)   thows ServletException

init方法只在此过滤器第一次初始化时执行,不是每次调用过滤器都执行它。对于简单的过滤器,可提供此方法的一个空体,但有两个原因需要使用init。 首先,FilterConfig对象提供对servlet环境及web.xml文件中指派的过滤器名的访问。因此,普遍的办法是利用init将 FilterConfig对象存放在一个字段中,以便doFilter方法能够访问servlet环境或过滤器名.其次,FilterConfig对象具 有一个getInitParameter方法,它能够访问部署描述符文件(web.xml)中分配的过滤器初始化参数。

public void destroy( )
     大多数过滤器简单地为此方法提供一个空体,不过,可利用它来完成诸如关闭过滤器使用的文件或数据库连接池等清除任务。

1.2   将过滤行为放入doFilter方法

     doFilter方法为大多数过滤器地关键部分。每当调用一个过滤器时,都要执行doFilter。对于大多数过滤器来说,doFilter执行的步骤是 基于传入的信息的。因此,可能要利用作为doFilter的第一个参数提供的ServletRequest。这个对象常常构造为 HttpServletRequest类型,以提供对该类的更特殊方法的访问。

1.3   调用FilterChain对象的doFilter方法
     Filter接口的doFilter方法以一个FilterChain对象作为它的第三个参数。在调用该对象的doFilter方法时,激活下一个相关的 过滤器。这个过程一般持续到链中最后一个过滤器为止。在最后一个过滤器调用其FilterChain对象的doFilter方法时,激活servlet或 页面自身。
但是,链中的任意过滤器都可以通过不调用其FilterChain的doFilter方法中断这个过程。在这样的情况下,不再调用JSP页面的serlvet,并且中断此调用过程的过滤器负责将输出提供给客户机。

1.4   对适当的servlet和JSP页面注册过滤器

     部署描述符文件的2.3版本引入了两个用于过滤器的元素,分别是:filter和filter-mapping。filter元素向系统注册一个过滤对象,filter-mapping元素指定该过滤对象所应用的URL。

1.filter元素
filter元素位于部署描述符文件(web.xml)的前部,所有filter-mapping、servlet或servlet-mapping元素之前。filter元素具有如下六个可能的子元素:

1、 icon   这是一个可选的元素,它声明IDE能够使用的一个图象文件。
2、filter-name   这是一个必需的元素,它给过滤器分配一个选定的名字。
3、display-name   这是一个可选的元素,它给出IDE使用的短名称。
4、 description   这也是一个可选的元素,它给出IDE的信息,提供文本文档。
5、 filter-class   这是一个必需的元素,它指定过滤器实现类的完全限定名。
6、 init-param   这是一个可选的元素,它定义可利用FilterConfig的getInitParameter方法读取的初始化参数。单个过滤器元素可包含多个init-param元素。

请注意,过滤是在serlvet规范2.3版中初次引入的。因此,web.xml文件必须使用DTD的2.3版本。下面介绍一个简单的例子:

  <xml version="1.0" encoding="ISO-8859-1"?>
    
DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
         "http://java.sun.com/dtd/web-app_2_3.dtd"
>

    
<web-app>
     
<filter>
        
<filter-name>MyFilterfilter-name>
        
<filter-class>myPackage.FilterClassfilter-class>
      
filter>
      

      
<filter-mapping>...filter-mapping>
     <
web-app>


2.filter-mapping元素

    filter-mapping元素位于web.xml文件中filter元素之后serlvet元素之前。它包含如下三个可能的子元素:

1、 filter-name   这个必需的元素必须与用filter元素声明时给予过滤器的名称相匹配。

2、 url-pattern   此元素声明一个以斜杠(/)开始的模式,它指定过滤器应用的URL。所有filter-mapping元素中必须提供url-pattern或 servlet-name。但不能对单个filter-mapping元素提供多个url-pattern元素项。如果希望过滤器适用于多个模式,可重复 整个filter-mapping元素。

3、 servlet-name   此元素给出一个名称,此名称必须与利用servlet元素给予servlet或JSP页面的名称相匹配。不能给单个filter-mapping元素提供 多个servlet-name元素项。如果希望过滤器适合于多个servlet名,可重复这个filter-mapping元素。
下面举一个例子:

xml version="1.0" encoding="ISO-8859-1"?>
    
DOCTYPE web-app PUBLIC
        
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

        
"http://java.sun.com/dtd/web-app_2_3.dtd">
    
<web-app>
      
<filter>
        
<filter-name>MyFilterfilter-name>
        
<filter-class>myPackage.FilterClassfilter-class>
      
filter>
      

      
<filter-mapping>
        
<filter-name>MyFilterfilter-name>
        
<url-pattern>/someDirectory/SomePage.jspurl-pattern>
      
filter-mapping>
    
web-app>


1.5   禁用激活器servlet
     在对资源应用过滤器时,可通过指定要应用过滤器的URL模式或servlet名来完成。如果提供servlet名,则此名称必须与web.xml的 servlet元素中给出的名称相匹配。如果使用应用到一个serlvet的URL模式,则此模式必须与利用web.xml的元素servlet- mapping指定的模式相匹配。但是,多数服务器使用“激活器servlet”为servlet体统一个缺省的URL:http: //host/WebAppPrefix/servlet/ServletName。需要保证用户不利用这个URL访问servlet(这样会绕过过滤器 设置)。
例如,假如利用filter和filter-mapping指示名为SomeFilter的过滤器应用到名为SomeServlet的servlet,则如下:

<filter>
      
<filter-name>SomeFilterfilter-name>
      
<filter-class>somePackage.SomeFilterClassfilter-class>
   <
filter>
    

    
<filter-mapping>
      
<filter-name>SomeFilterfilter-name>
      
<servlet-name>SomeServletservlet-name>
     <
filter-mapping>



接着,用servlet和servlet-mapping规定URL   http://host/webAppPrefix/Blah 应该调用SomeSerlvet,如下所示:

<filter>
      
<filter-name>SomeFilterfilter-name>
      
<filter-class>somePackage.SomeFilterClassfilter-class>
    
filter>
    

    
<filter-mapping>
      
<filter-name>SomeFilterfilter-name>
      
<servlet-name>/Blahservlet-name>
     <
filter-mapping>



现在,在客户机使用URL   http://host/webAppPrefix/Blah 时就会调用过滤器。过滤器不应用到
http://host/webAppPrefix/servlet/SomePackage.SomeServletClass。
尽管有关闭激活器的服务器专用方法。但是,可移植最强的方法时重新映射Web应用钟的/servlet模式,这样使所有包含此模式的请求被送到相同的 servlet中。为了重新映射此模式,首先应该建立一个简单的servlet,它打印一条错误消息,或重定向用户到顶层页。然后,使用servlet和 servlet-mapping元素发送包含/servlet模式的请求到该servlet。程序清单9-1给出了一个简短的例子。

程序清单9-1 web.xml(重定向缺省servlet URL的摘录)

 xml version="1.0" encoding="ISO-8859-1"?>
    
DOCTYPE web-app PUBLIC
         "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
         "http://java.sun.com/dtd/web-app_2_3.dtd"
>

    
<web-app>
    

    
<servlet>
      
<servlet-name>Errorservlet-name>
      
<servlet-class>somePackage.ErrorServletservlet-class>
    
servlet>
    

    
<servlet-mapping>
      
<servlet-name>Errorservlet-name>
      
<url-pattern>/servlet/*url-pattern>
    
servlet-mapping>
    

    <
web-app>

-------------------------------------------------------------------------------------------
解决乱码

web.xml加配置

<!--   过滤器 -->
 <filter>
  <filter-name>Filter</filter-name>
  <filter-class>
   com.util.EncodingFilter<!-- 过滤器类 -->
  </filter-class>
  <init-param>
   <param-name>Encoding</param-name>
   <param-value>gb2312</param-value>
  </init-param>
 </filter>
 <filter-mapping>
  <filter-name>Filter</filter-name>
  <url-pattern>/*</url-pattern>
 </filter-mapping>

EncodingFilter.java

package com.hibernate.util;

import java.io.IOException;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class EncodingFilter implements Filter {
 protected FilterConfig config;

 protected String Encoding = null;

 public void init(FilterConfig config) throws ServletException {

  this.config = config;
  this.Encoding = config.getInitParameter("Encoding");

 }

 public void doFilter(ServletRequest request, ServletResponse response,
   FilterChain chain) throws IOException, ServletException {

  if (request.getCharacterEncoding() == null) {
   if (Encoding != null) {
    request.setCharacterEncoding(Encoding);
    response.setCharacterEncoding(Encoding);
   }
  }
  chain.doFilter(request,response);
 }
 public void destroy() {}
}

OK!!!

----------------------------------------------------------------------------------------------------

很简单的过滤器,就是为了记录一个url的请求时间 filter:
  1. package com.javaeye.wqf;  
  2. import javax.servlet.*;  
  3.   
  4. public class CounterFilter implements Filter {  
  5.     public void doFilter(ServletRequest request, ServletResponse response,  
  6.             FilterChain chain) throws IOException, ServletException {  
  7.         long start = System.currentTimeMillis();  
  8.         System.out.println("Filter start at "+start);  
  9.         chain.doFilter(request, response);  
  10.         long end = System.currentTimeMillis();  
  11.         System.out.println("Filter end at "+end);  
  12.     } 



  1. <filter>  
  2.     <filter-name>test</filter-name>  
  3.     <filter-class>com.javaeye.wqf.CounterFilter</filter-class>  
  4. </filter>  
  5.   
  6. <filter-mapping>  
  7.     <filter-name>test</filter-name>  
  8.     <url-pattern>/*</url-pattern>  
  9. </filter-mapping> 
一般情况下是没什么问题,但是当我下载一个稍微大的文件时,跳出确认窗口,如果选择的是cancel,
就会发现filter并没有返回,也就是说
  1. System.out.println("Filter end at "+end); 
并没有执行. 原因是:

chain.doFilter(request, response);  
执行到这里时会从这里调用剩下的filter和servlet,所以这个调用将会是一个很长的过程。
在这个调用里,将会完全通过request和resonse去操作连接,取得/发送数据,如果连接出现异常,将直接弹出Exception
你的代码里没有捕获异常,所以如果出现异常,chain.doFilter后面的就不会执行。
可以把chain.doFilter放到try finally结构中,保证后续会被执行




posted @ 2008-10-28 14:58 LukeW 阅读(337) | 评论 (0)编辑 收藏

Android应用程序剖析

Anatomy of an Android Application


There are four building blocks to an Android application:
  • Activity
  • Intent Receiver
  • Service
  • Content Provider

Not every application needs to have all four, but your application will be written with some combination of these.

Once you have decided what components you need for your application, you should list them in a file called AndroidManifest.xml. This is an XML file where you declare the components of your application and what their capabilities and requirements are. See the Android manifest file documentation for complete details.

Activity
Activities are the most common of the four Android building blocks. An activity is usually a single screen in your application. Each activity is implemented as a single class that extends the Activity base class. Your class will display a user interface composed of Views and respond to events. Most applications consist of multiple screens. For example, a text messaging application might have one screen that shows a list of contacts to send messages to, a second screen to write the message to the chosen contact, and other screens to review old messages or change settings. Each of these screens would be implemented as an activity. Moving to another screen is accomplished by a starting a new activity. In some cases an activity may return a value to the previous activity -- for example an activity that lets the user pick a photo would return the chosen photo to the caller.

When a new screen opens, the previous screen is paused and put onto a history stack. The user can navigate backward through previously opened screens in the history. Screens can also choose to be removed from the history stack when it would be inappropriate for them to remain. Android retains history stacks for each application launched from the home screen.

Intent and Intent Filters
Android uses a special class called an Intent to move from screen to screen. An intent describes what an application wants done. The two most important parts of the intent data structure are the action and the data to act upon. Typical values for action are MAIN (the front door of the activity), VIEW, PICK, EDIT, etc. The data is expressed as a URI. For example, to view contact information for a person, you would create an intent with the VIEW action and the data set to a URI representing that person.

There is a related class called an IntentFilter. While an intent is effectively a request to do something, an intent filter is a description of what intents an activity (or intent receiver, see below) is capable of handling. An activity that is able to display contact information for a person would publish an IntentFilter that said that it knows how to handle the action VIEW when applied to data representing a person. Activities publish their IntentFilters in the AndroidManifest.xml file.

Navigating from screen to screen is accomplished by resolving intents. To navigate forward, an activity calls startActivity(myIntent). The system then looks at the intent filters for all installed applications and picks the activity whose intent filters best matches myIntent. The new activity is informed of the intent, which causes it to be launched. The process of resolving intents happens at run time when startActivity is called, which offers two key benefits:

* Activities can reuse functionality from other components simply by making a request in the form of an Intent
* Activities can be replaced at any time by a new Activity with an equivalent IntentFilter


Intent Receiver
You can use an IntentReceiver when you want code in your application to execute in reaction to an external event, for example, when the phone rings, or when the data network is available, or when it's midnight. Intent receivers do not display a UI, although they may use the NotificationManager to alert the user if something interesting has happened. Intent receivers are registered in AndroidManifest.xml, but you can also register them from code using Context.registerReceiver(). Your application does not have to be running for its intent receivers to be called; the system will start your application, if necessary, when an intent receiver is triggered. Applications can also send their own intent broadcasts to others with Context.broadcastIntent().
Service

A Service is code that is long-lived and runs without a UI. A good example of this is a media player playing songs from a play list. In a media player application, there would probably be one or more activities that allow the user to choose songs and start playing them. However, the music playback itself should not be handled by an activity because the user will expect the music to keep playing even after navigating to a new screen. In this case, the media player activity could start a service using Context.startService() to to run in the background to keep the music going. The system will then keep the music playback service running until it has finished. (You can learn more about the priority given to services in the system by reading Lifecycle of an Android Application.) Note that you can connect to a service (and start it if it's not already running) with the Context.bindService() method. When connected to a service, you can communicate with it through an interface exposed by the service. For the music service, this might allow you to pause, rewind, etc.

Content Provider
Applications can store their data in files, an SQLite database, or any other mechanism that makes sense. A content provider, however, is useful if you want your application's data to be shared with other applications. A content provider is a class that implements a standard set of methods to let other applications store and retrieve the type of data that is handled by that content provider.

To get more details on content providers, see Accessing Content Providers.

posted @ 2008-10-07 16:22 LukeW 阅读(269) | 评论 (0)编辑 收藏

HTTP协议中的Tranfer-Encoding:chunked编码解析

    当不能预先确定报文体的长度时,不可能在头中包含Content-Length域来指明报文体长度,此时就需要通过Transfer-Encoding域来确定报文体长度。
    通常情况下,Transfer-Encoding域的值应当为chunked,表明采用chunked编码方式来进行报文体的传输。chunked编码是HTTP/1.1 RFC里定义的一种编码方式,因此所有的HTTP/1.1应用都应当支持此方式。
    chunked编码的基本方法是将大块数据分解成多块小数据,每块都可以自指定长度,其具体格式如下(BNF文法):
    Chunked-Body   = *chunk            //0至多个chunk
                     last-chunk         //最后一个chunk
                     trailer            //尾部
                     CRLF               //结束标记符

   chunk          = chunk-size [ chunk-extension ] CRLF  
                        chunk-data CRLF
   chunk-size     = 1*HEX
   last-chunk     = 1*("0") [ chunk-extension ] CRLF

   chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] )
   chunk-ext-name = token
   chunk-ext-val  = token | quoted-string
   chunk-data     = chunk-size(OCTET)
   trailer        = *(entity-header CRLF)     
   
    解释:
    Chunked-Body表示经过chunked编码后的报文体。报文体可以分为chunk, last-chunk,trailer和结束符四部分。chunk的数量在报文体中最少可以为0,无上限;每个chunk的长度是自指定的,即,起始的数 据必然是16进制数字的字符串,代表后面chunk-data的长度(字节数)。这个16进制的字符串第一个字符如果是“0”,则表示chunk- size为0,该chunk为last-chunk,无chunk-data部分。可选的chunk-extension由通信双方自行确定,如果接收者 不理解它的意义,可以忽略。
    trailer是附加的在尾部的额外头域,通常包含一些元数据(metadata, meta means "about information"),这些头域可以在解码后附加在现有头域之后。
    实例分析:
    下面分析用ethereal抓包使用Firefox与某网站通信的结果(从头域结束符后开始):
Address  0..........................  f
000c0                                31
000d0    66 66 63 0d 0a ...............   // ASCII码:1ffc"r"n, chunk-data数据起始地址为000d5
         很明显,“1ffc”为第一个chunk的chunk-size,转换为int为8188.由于1ffc后马上就是
         CRLF,因此没有chunk-extension.chunk-data的起始地址为000d5, 计算可知下一块chunk的起始
         地址为000d5+1ffc + 2=020d3,如下:
020d0    .. 0d 0a 31 66 66 63 0d 0a .... // ASCII码:"r"n1ffc"r"n
         前一个0d0a是上一个chunk的结束标记符,后一个0d0a则是chunk-size和chunk-data的分隔符。
         此块chunk的长度同样为8188, 依次类推,直到最后一块
100e0                          0d 0a 31
100f0    65 61 39 0d 0a......            //ASII码:"r"n"1ea9"r"n
         此块长度为0x1ea9 = 7849, 下一块起始为100f5 + 1ea9 + 2 = 11fa0,如下:
100a0    30 0d 0a 0d 0a                  //ASCII码:0"r"n"r"n
         “0”说明当前chunk为last-chunk, 第一个0d 0a为chunk结束符。第二个0d0a说明没有trailer部分,整个Chunk-body结束。
    解码流程:
    对chunked编码进行解码的目的是将分块的chunk-data整合恢复成一块作为报文体,同时记录此块体的长度。
    RFC2616中附带的解码流程如下:(伪代码)
    length := 0         //长度计数器置0
    read chunk-size, chunk-extension (if any) and CRLF      //读取chunk-size, chunk-extension
                                                          //和CRLF
    while(chunk-size > 0 )   {            //表明不是last-chunk
          read chunk-data and CRLF            //读chunk-size大小的chunk-data,skip CRLF
          append chunk-data to entity-body     //将此块chunk-data追加到entity-body后
          read chunk-size and CRLF          //读取新chunk的chunk-size 和 CRLF
    }
    read entity-header      //entity-header的格式为name:valueCRLF,如果为空即只有CRLF
    while (entity-header not empty)   //即,不是只有CRLF的空行
    {
       append entity-header to existing header fields
       read entity-header
    }
    Content-Length:=length      //将整个解码流程结束后计算得到的新报文体length
                                 //作为Content-Length域的值写入报文中
    Remove "chunked" from Transfer-Encoding  //同时从Transfer-Encoding中域值去除chunked这个标记
    length最后的值实际为所有chunk的chunk-size之和,在上面的抓包实例中,一共有八块chunk-size为0x1ffc(8188)的chunk,剩下一块为0x1ea9(7849),加起来一共73353字节。
    注:对于上面例子中前几个chunk的大小都是8188,可能是因为:"1ffc" 4字节,""r"n"2字节,加上块尾一个""r"n"2字节一共8字节,因此一个chunk整体为8196,正好可能是发送端一次TCP发送的缓存大小。

posted @ 2008-09-24 18:03 LukeW 阅读(2953) | 评论 (2)编辑 收藏

HTTP Connections

HTTP Connections
最近初涉网络编程,分析了下HTTP协议,下面为第一篇关于HTTP连接控制方面的学习日志,主要参考RFC2616,肯定有疏漏之处,还望指出。
HTTP协议是位于传输层之上的应用层协议,其网络层基础通常是TCP协议。TCP协议是面向连接和流的,因此连接的状态和控制对于HTTP协议而言相当重要。同时,HTTP是基于报文的,因此如何确定报文长度也是协议中比较重要的一点。
Persistent Connections持久连接
目的
    在使用持久连接前,HTTP协议规定为获取每个URL资源都需要使用单独的一个TCP连接,这增加了HTTP服务端的负载,引起互联网拥塞。例如内嵌图片以及其他类似数据的使用要求一个客户端在很短时间内向同一个服务端发起多个请求。
使用持久连接的优点:
减少TCP连接数量
在一个连接上实现HTTP请求和应答的流水,即允许客户端发出多个请求,而不必在接收到前一请求的应答后才发出下一请求,极大减少时间消耗
后续请求延迟减少,无需再在TCP握手上耗时
可以更加优雅地实现HTTP协议,由于持续连接的存在无需报告错误后无需关闭连接,因此客户端可使用最新的协议特性发出请求,如果接收到表示错误的应答,则换用更旧的语义。

总体描述
HTTP/1.1和之前版本的显著区别是HTTP/1.1默认使用持久连接。即,除非服务端在应答中明确指出,客户端应当假定服务端会维持一个持久连接,即使从服务端收到的应答是报告错误。
持 久连接对关闭TCP连接的行为提供信号量机制支持。这个信号量是在HTTP头中的Connection域设置,注意Client向Proxy发出请求时该 域可能被Proxy-Connection域替换。一旦close信号被表明,客户端绝不能再通过该连接发送更多的请求。

协商(Negotiation)
HTTP/1.1 服务端可以假定HTTP/1.1客户端会维持持久连接,除非请求中Connection域的值是"close".同样的,如果服务端打算在送出应答后立即 关闭连接,它应当在应答中包含同样的Connection域。(TCP连接关闭是双向的,此时TCP进入半关闭状态)
同样的,HTTP/1.1客户端可以期望连接是持久的,除非如前所述收到表示连接关闭的应答。当然,也可以主动发出一个包含Connection:close的请求以表明终止连接。
无论客户端还是服务端发出的报文包含Connection:close,则该请求均为连接上的最后一个请求(服务端发出此应答后关闭,因此不可能接收更多的请求)
报文传输长度
    为保证持久性,连接上的报文都必须有一个自定义的报文传输长度(否则必须通过连接的关闭表示报文结束,因为TCP连接是面向流的),确定的规则按优先级由高到低排列如下:
    报文传输长度指报文中出现的报文体的长度(即,不包括头长度,因为报文头的结束可通过连续两个CRLF确定)
1.任何绝不能包含报文体(如1xx,204,304)的应答消息总是以头域后的第一个空行结束,无视头中所有的entity类型域的设置,包括Content-Length域。
2.Transfer-Encoding域出现,其值为除"identify"以外的其他值,则用"chunked"传输编码方式确定传输长度,具体方式留待下篇分析。
3.Content- Length域出现,且Transfer-Encoding域未出现(出现则忽略Content-Length域)。Content-Length域的值 为十进制数的字节序,如Content-Length:1234,则1、2、3、4是分别作为一个octet传输的,因此需要atoi转换成数值。
4.如果报文使用了"multipart/byteranges"的媒体类型,且没对传输长度做前面的指明,则这种自分割的媒体类型定义了传输长度。具体参见Range头域的说明。
5.服务端关闭连接(此方法不可用于客户端发出的请求报文,因为客户端关闭连接则使得服务端无法发送应答).
    为保持和HTTP/1.0的兼容性, 包含报文体的HTTP/1.1请求必须包含合法的Content-Length头域,除非明确知道服务端是HTTP/1.1兼容的.如果请求包含消息体, 而没有Content-Length域,那么如果服务端无法确定消息长度时,它会返回400(无效请求),或者坚持获取合法Content-Length 而返回411(要求包含长度).

    所有接收实体的HTTP/1.1应用程序必须接受"chunked"传输编码, 这样允许当报文长度无法预先确定时可以运用此机制获取报文长度.
    报文不能同时包含Content-Length头域和非"identity" Transfer-Encoding.如果出现了, Content-Length域必须被忽略.
    当Content-Length域在允许报文体的报文中存在时, 其域值必须严格等于消息体中的8比特字节.HTTP/1.1 user agent 必须在接收并检测到一个错误的长度时提醒用户.
    以上方法中,最常见的还是使用Content-Length域表示报文体长度,Transfer-Encoding需要按格式解码才能还原出发送编码前的报文。

流水
    支持持久连接的客户端可以流水发送请求,服务端必须按发送的顺序发送应答。
    假定持久连接和连接后即可流水的客户端应当做好在第一次流水失败后重新尝试此连接。在这样的尝试中,在确定连接是持久的之前,客户端不能再流水。
    客户端同样必须准备好在服务端送回所有相关应答前就关闭连接时重发请求。
    不应流水non-idempotent方法

Proxy Servers
    对于代理服务端而言,正确实现Connection头域指定的属性尤为重要。
    代理服务端必须分立通告它的客户端和连接的原始服务端持久连接的属性,每个持久连接设置仅针对一个传输连接。
   
实践考量
    超时值,服务端通常会为每个连接维护一个定时器,一旦某个连接不活跃超过一定时间值,服务端会关闭此连接。考虑到一个客户端可能通过代理服务端发出更多连接,代理服务端通常会将超时值设置得更高。
    还有一些关于从异步关闭中恢复的讨论。

报文传输要求
    使用TCP流控制来解决服务端临时负载过高问题,而不是简单的依赖客户端重连而关闭连接。
    监视连接情况以获取错误状态消息
    关于使用100(继续)状态码
    100状态码用于客户端发送请求体之前测试是否可以发送该请求,对于Proxy,有以下要求:
1.如果代理服务端接收到包含Expect头域值为"100-continue"的请求, 而不明确知道下一跳服务不支持HTTP/1.1以上版本, 则它必须转发这个请求, 包括Expect头域.
2.如果代理知道下一跳服务端为HTTP/1.0或者更低版本, 则它不能转发此请求, 且必须以407应答客户端.
3.如果明确知道发出请求的客户端版本为HTTP/1.0或者更低,则代理服务端绝不能转发100应答,这条规则凌驾于转发1xx应答的一般准则.

Connection头域说明
BNF文法:
    Connection = "Connection" ":" 1#(connection-token)
    connection-token  = token
   
Connection头域中的token用于指定对于特定连接有意义的选项,因此proxy在转发前要扫描此域,从头中去除和token同名的域。例如Connection:Range,则要去掉Range域。
    HTTP/1.1定义了close这个token,发送者用此token表示在完成这个报文所属请求/应答的收发后连接将关闭。


posted @ 2008-09-24 18:01 LukeW 阅读(545) | 评论 (1)编辑 收藏

关于断点续传的调研

一些讨论:
http://topic.csdn.net/t/20061214/22/5231907.html

posted @ 2008-09-24 17:57 LukeW 阅读(215) | 评论 (0)编辑 收藏

Serializing an Image

Serializing an Image

Creating an image from an array of data is an easy task, but to create a byte-array of data from an image is a little more complicated. But it's required if you want to send a modified image to a server.

To create a byte-array of data from an image, we can use the getRGB(..) method in the image class in MIDP 2.0. From the getRGB method we get an int-array of data containing all the ARGB values of each pixel in the image. Integers in java are four bytes, so we need to split each int value into four byte values.

To mask out each of the bytes in the int we can use the 'AND &' operator and the 'shift-right >>' operator. Here's an example:

int ARGB = 0xFFFFFFFF;  // AARRGGBB 
int a = (ARGB & 0xFF000000);
int r = (ARGB & 0x00FF0000);
int g = (ARGB & 0x0000FF00);
int b = (ARGB & 0x000000FF);
// Here we move each bit to the right.
a = (a >> 24); // a = 0x000000FF
r = (r >> 16); // r = 0x000000FF
g = (g >> 8); // g = 0x000000FF
b = b;        // b = 0x000000FF

When we convert the integer to a byte, there are some problems since there are only signed bytes in Java. A byte may contain values between –128 and 127 and from our integer we'll have a value between 0 and 255.

Here are some conversion examples:

Integer val 127 = byte val 127. 
Integer val 128 = byte val –128.
Integer val 255 = byte val –1.
byte ba = (byte)(a);  // if a=0x000000FF (255), then ba = -1
byte br = (byte)(r);
byte bg = (byte)(g);
byte bb = (byte)(b);

We have to loop though each pixel in the image and get each pixel value to our byte-array. When that's done, we can send the image to a server where we can convert the byte-array back to a integer-array and then show our picture.

So, when we convert the byte back to an integer we have to check if the byte value is less than zero.

Int a, r, g, b;
If(ba<0)
     a = 256 – a;

Now our new integer value will be between 0 and 255 and we just have to use the 'shift-left <<' operator and add the values together to get our new int-array.

a = (a << 24);
r = (r << 16);
g = (g << 8);
b = (b);
  0xFF000000 (a)
+ 0x00FF0000 (r)
+ 0x0000FF00 (g)
+ 0x000000FF (b)
= 0xFFFFFFFF (argb)
int ARGB = a+r+g+b;

After the integer-array is recreated we can use the createRGBImage(..) method in the Image class to create our image.

Be aware that the byte-array is quite large as it contains all of the ARGB values for each pixel. If an image is 100*60 px, where each pixel is four bytes, the byte array will be 24kb.

ImageBytearryConvert.rar

posted @ 2008-09-11 17:09 LukeW 阅读(259) | 评论 (0)编辑 收藏

HTTP头信息

HTTP(HyperTextTransferProtocol)是超文本传输协议的缩写,它用于传送WWW方式的数据,关于HTTP 协议的详细内容请参 考RFC2616。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户 信息和内容的类似于MIME的消息结构。服务器以一个状态行作为响应,相应的内容包括消息协议的版本,成功或者错误编码加上包含服务器信息、实体元信息以 及可能的实体内容。

通常HTTP消息包括客户机向服务器的请求消息和服务器向客户机的响应消息。这两种类型的消息由一个起始行, 一个或者多个头域,一个只是头域结束的空行和可 选的消息体组成。HTTP的头域包括通用头,请求头,响应头和实体头四个部分。每个头域由一个域名,冒号(:)和域值三部分组成。域名是大小写无关的,域 值前可以添加任何数量的空格符,头域可以被扩展为多行,在每行开始处,使用至少一个空格或制表符。

通用头域
通用头 域包含请求和响应消息都支持的头域,通用头域包含Cache-Control、 Connection、Date、Pragma、Transfer-Encoding、Upgrade、Via。对通用头域的扩展要求通讯双方都支持此扩 展,如果存在不支持的通用头域,一般将会作为实体头域处理。下面简单介绍几个在UPnP消息中使用的通用头域。


  Cache-Control头域
Cache -Control指定请求和响应遵循的缓存机制。在请求消息或响应消息中设置 Cache-Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no-cache、no-store、max-age、 max-stale、min-fresh、only-if-cached,响应消息中的指令包括public、private、no-cache、no- store、no-transform、must-revalidate、proxy-revalidate、max-age。各个消息中的指令含义如 下:

Public指示响应可被任何缓存区缓存。
Private指示对于单个用户的整个或部分响应消息,不能被共享缓存处理。这允许服务器仅仅描述当用户的部分响应消息,此响应消息对于其他用户的请求无效。
no-cache指示请求或响应消息不能缓存
no-store用于防止重要的信息被无意的发布。在请求消息中发送将使得请求和响应消息都不使用缓存。
max-age指示客户机可以接收生存期不大于指定时间(以秒为单位)的响应。
min-fresh指示客户机可以接收响应时间小于当前时间加上指定时间的响应。
max-stale指示客户机可以接收超出超时期间的响应消息。如果指定max-stale消息的值,那么客户机可以接收超出超时期指定值之内的响应消息。


  Date头域
Date头域表示消息发送的时间,时间的描述格式由rfc822定义。例如,Date:Mon,31Dec200104:25:57GMT。Date描述的时间表示世界标准时,换算成本地时间,需要知道用户所在的时区。

  Pragma头域
  Pragma头域用来包含实现特定的指令,最常用的是Pragma:no-cache。在HTTP/1.1协议中,它的含义和Cache- Control:no-cache相同。

请求消息
请求消息的第一行为下面的格式:
MethodSPRequest-URISPHTTP-VersionCRLFMethod 表示对于Request-URI完成的方法,这个字段是大小写敏感的,包括OPTIONS、GET、HEAD、POST、PUT、DELETE、 TRACE。方法GET和HEAD应该被所有的通用WEB服务器支持,其他所有方法的实现是可选的。GET方法取回由Request-URI标识的信息。 HEAD方法也是取回由Request-URI标识的信息,只是可以在响应时,不返回消息体。POST方法可以请求服务器接收包含在请求中的实体信息,可 以用于提交表单,向新闻组、BBS、邮件群组和数据库发送消息。

SP表示空格。Request-URI遵循URI格式,在此字段 为星 号(*)时,说明请求并不用于某个特定的资源地址,而是用于服务器本身。HTTP- Version表示支持的HTTP版本,例如为HTTP/1.1。CRLF表示换行回车符。请求头域允许客户端向服务器传递关于请求或者关于客户机的附加 信息。请求头域可能包含下列字段Accept、Accept-Charset、Accept- Encoding、Accept-Language、Authorization、From、Host、If-Modified-Since、If- Match、If-None-Match、If-Range、If-Range、If-Unmodified-Since、Max-Forwards、 Proxy-Authorization、Range、Referer、User-Agent。对请求头域的扩展要求通讯双方都支持,如果存在不支持的请 求头域,一般将会作为实体头域处理。

  典型的请求消息:
GET http://download.microtool.de:80/somedata.exe
Host: download.microtool.de
Accept:*/*
Pragma: no-cache
Cache-Control: no-cache
Referer: http://download.microtool.de/
User-Agent:Mozilla/4.04[en](Win95;I;Nav)
Range:bytes=554554-

上例第一行表示HTTP客户端(可能是浏览器、下载程序)通过GET方法获得指定URL下的文件。棕色的部分表示请求头域的信息,绿色的部分表示通用头部分。

  Host头域
  Host头域指定请求资源的Intenet主机和端口号,必须表示请求url的原始服务器或网关的位置。HTTP/1.1请求必须包含主机头域,否则系统会以400状态码返回。

  Referer头域
Referer 头域允许客户端指定请求uri的源资源地址,这可以允许服务器生成回退链表,可用来登陆、优化cache等。他也允许废除的或错误的连接由于维护的目的被 追踪。如果请求的uri没有自己的uri地址,Referer不能被发送。如果指定的是部分uri地址,则此地址应该是一个相对地址。

  Range头域
Range头域可以请求实体的一个或者多个子范围。例如,
表示头500个字节:bytes=0-499
表示第二个500字节:bytes=500-999
表示最后500个字节:bytes=-500
表示500字节以后的范围:bytes=500-
第一个和最后一个字节:bytes=0-0,-1
同时指定几个范围:bytes=500-600,601-999

但是服务器可以忽略此请求头,如果无条件GET包含Range请求头,响应会以状态码206(PartialContent)返回而不是以200 (OK)。

  User-Agent头域
User-Agent头域的内容包含发出请求的用户信息。

响应消息
响应消息的第一行为下面的格式:
HTTP-VersionSPStatus-CodeSPReason-PhraseCRLF

HTTP -Version表示支持的HTTP版本,例如为HTTP/1.1。Status- Code是一个三个数字的结果代码。Reason-Phrase给Status-Code提供一个简单的文本描述。Status-Code主要用于机器自 动识别,Reason-Phrase主要用于帮助用户理解。Status-Code的第一个数字定义响应的类别,后两个数字没有分类的作用。第一个数字可 能取5个不同的值:

1xx:信息响应类,表示接收到请求并且继续处理
2xx:处理成功响应类,表示动作被成功接收、理解和接受
3xx:重定向响应类,为了完成指定的动作,必须接受进一步处理
4xx:客户端错误,客户请求包含语法错误或者是不能正确执行
5xx:服务端错误,服务器不能正确执行一个正确的请求

响应头域允许服务器传递不能放在状态行的附加信息,这些域主要描述服务器的信息和 Request-URI进一步的信息。响应头域包含Age、Location、Proxy-Authenticate、Public、Retry- After、Server、Vary、Warning、WWW-Authenticate。对响应头域的扩展要求通讯双方都支持,如果存在不支持的响应头 域,一般将会作为实体头域处理。

典型的响应消息:

HTTP/1.0200OK
Date:Mon,31Dec200104:25:57GMT
Server:Apache/1.3.14(Unix)
Content-type:text/html
Last-modified:Tue,17Apr200106:46:28GMT
Etag:"a030f020ac7c01:1e9f"
Content-length:39725426
Content-range:bytes554554-40279979/40279980

上例第一行表示HTTP服务端响应一个GET方法。棕色的部分表示响应头域的信息,绿色的部分表示通用头部分,红色的部分表示实体头域的信息。

  Location响应头
Location响应头用于重定向接收者到一个新URI地址。

  Server响应头
Server响应头包含处理请求的原始服务器的软件信息。此域能包含多个产品标识和注释,产品标识一般按照重要性排序。

  实体
请求消息和响应消息都可以包含实体信息,实体信息一般由实体头域和实体组成。实体头域包含关于实体的原信息,实体头包括Allow、Content- Base、Content-Encoding、Content-Language、 Content-Length、Content-Location、Content-MD5、Content-Range、Content-Type、 Etag、Expires、Last-Modified、extension-header。extension-header允许客户端定义新的实体 头,但是这些域可能无法未接受方识别。实体可以是一个经过编码的字节流,它的编码方式由Content-Encoding或Content-Type定 义,它的长度由Content-Length或Content-Range定义。

  Content-Type实体头
Content-Type实体头用于向接收方指示实体的介质类型,指定HEAD方法送到接收方的实体介质类型,或GET方法发送的请求介质类型 Content-Range实体头
Content-Range实体头用于指定整个实体中的一部分的插入位置,他也指示了整个实体的长度。在服务器向客户返回一个部分响应,它必须描述响应覆盖的范围和整个实体长度。一般格式:
Content-Range:bytes-unitSPfirst-byte-pos-last-byte-pos/entity-legth 


例如,传送头500个字节次字段的形式:Content-Range:bytes0- 499/1234如果一个http消息包含此节(例如,对范围请求的响应或对一系列范围的重叠请求),Content-Range表示传送的范围, Content-Length表示实际传送的字节数。

  Last-modified实体头

应答头 说明
Allow 服务器支持哪些请求方法(如GET、POST等)。
Content-Encoding 文 档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的 下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept- Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。
Content-Length 表 示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过 byteArrayStream.writeTo(response.getOutputStream()发送内容。
Content-Type 表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。
Date 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。
Expires 应该在什么时候认为文档已经过期,从而不再缓存它?
Last-Modified 文 档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档 才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。
Location 表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。
Refresh 表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。
注 意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的 HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。

注意Refresh的意义是“N秒之后 刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则 可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV="Refresh" ...>。

注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。
Server 服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。
Set-Cookie 设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。
WWW-Authenticate 客 户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如, response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。
注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问(例如.htaccess)。

posted @ 2008-09-10 12:05 LukeW 阅读(237) | 评论 (0)编辑 收藏

j2me获取系统信息

在J2ME开发中,我们经常需要和手机系统进行交互,获得一些和系统相关的信息,在J2ME API设计中,提供了一系列的系统属性
表1   CLDC、MIDP和JTWI属性
属性名称
属性作用
microedition.profiles
代表手机支持的MIDP版本,返回格式值为“MIDP-1.0”或“MIDP-2.0”
microedition.configuration
代表手机支持的CLDC版本,返回格式值为“CLDC-1.0”或“CLDC-2.0”
microedition.locale
代表手机所在的国家或地区,返回值格式为“en-US”
microedition.platform
代表手机的品牌和型号,Nokia手机的返回值格式为“Nokia6310i/4.42”
microedition.encoding
代表手机默认的字符集名称,返回值格式为“ISO-8859-1”
microedition.commports
代表手机可以使用的串口列表,返回值中各个串口之间使用逗号分隔
microedition.hostname
MIDP2.0定义,代表本地主机名称,需要手机支持。
microedition.jtwi.version
代表手机支持的JTWI版本,值必须是“1.0”
 表2 可选包属性
属性名称
属性作用
microedition.media.version
代表手机支持的MMAPI版本,如果不支持则返回null
microedition.pim.version
代表手机支持的PIM API版本,如果不支持则返回null
microedition.m3g.version
代表手机支持的M3G API版本,如果不支持则返回null
microedition.location.version
代表手机支持的Location API版本,如果不支持则返回null
Bluetooth.api.version
代表手机支持的BT API版本,如果不支持则返回null
microedition.io.file.
FileConnection.version
代表手机支持的FC API版本,如果不支持则返回null
microedition.global.version
代表手机支持的Mobile Internationalization API(JSR-238)版本,如果不支持则返回null
microedition.chapi.version
代表手机支持的CH(Content Handler) API(JSR211)版本,如果不支持则返回null
microedition.sip.version
代表手机支持的SIP API版本,如果不支持则返回null
 表3 MMAPI属性
属性名称
属性作用
supports.mixing
代表手机是否支持混音(同时播放多个Player),返回值为“true”或“false”
supports.audio.capture
代表手机是否支持声音捕获(录音),返回值为“true”或“false”
supports.video.capture
代表手机是否支持视频捕获(录像),返回值为“true”或“false”
supports.recording
代表手机是否支持记录(record),返回值为“true”或“false”
audio.encodings
代表手机支持的声音格式,返回值格式为“encoding=audio/wav”,多个格式之间使用至少一个空格进行间隔
video.encodings
代表手机支持的视频格式,返回值格式为“encoding=video/3gpp”,多个格式之间使用至少一个空格进行间隔
video.snapshot.encodings
代表手机使用getSnapshot方法获得的视频快照格式,返回值格式为“encoding=png”,多个格式之间使用至少一个空格进行间隔
streamable.contents
代表手机支持的流媒体格式,返回null代表不支持
表4 Wireless Messaging API属性
属性名称
属性作用
wireless.messaging.sms.smsc
代表手机发送短信时的短信服务中心号码
表5 FileConnection API
属性名称
属性作用
fileconn.dir.photos
代表手机中存储照片和其它图片的目录,例如“file:///c:/My files/ Images /”
fileconn.dir.videos
代表手机中存储视频的目录,例如“file:///c:/My files/Video clips/”
fileconn.dir.tones
代表手机中存储声音的目录,例如“file:///c:/My files/Tones/”
fileconn.dir.memorycard
代表手机中存储卡的根目录。例如“file:///d:/”
fileconn.dir.private
(Nokia S40不支持) 
代表手机中MIDlet的私有工作目录,例如“file:///c:/System/MIDlets/[1015f294]/scratch”
fileconn.dir.photos.name
代表手机中图片目录的名称,例如“Images”
fileconn.dir.videos.name
代表手机中视频目录的名称,例如“Video clips”
fileconn.dir.tones.name
代表手机中声音目录的名称,例如“Sound clips”
file.separator
代表手机中的文件分隔符,例如“/”
fileconn.dir.memorycard.name
代表手机中存储卡的名称,例如“Memory card”
 使用这些属性,可以获得在程序运行过程中需要的很多和系统相关的信息,也可以使用表2中的属性来获得手机是否支持对应的可选包等信息。
实际使用示例:String name = System.getProperty(“microedition.platform”);

注意:如果需要获得JVM或jad文件中的信息,需要使用MIDlet类中的getAppProperty方法,其属性名则需要查阅jad文件的设定,和本文所述的属性名无关。

posted @ 2008-08-28 16:43 LukeW 阅读(286) | 评论 (0)编辑 收藏

HTTP请求头信息

HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST)。如有必要,客户程序还可以选择发送其他的请求 头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说Content-Length必须出现。

下面是一些最常见的请求头:

Accept:浏览器可接受的MIME类型。
Accept-Charset:浏览器可接受的字符集。
Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。
Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。
Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。
Connection: 表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP 1.1(HTTP 1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点, Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然 后在正式写出内容之前计算它的大小。
Content-Length:表示请求消息正文的长度。
Cookie:这是最重要的请求头信息之一,参见后面《Cookie处理》一章中的讨论。
From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。
Host:初始URL中的主机和端口。
If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“Not Modified”应答。
Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。
JAVA手机网[www.cnjm.net]

Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。
UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。
有关HTTP头完整、详细的说明,请参见http://www.w3.org/Protocols/ 的HTTP规范。

java 读取方法

  Enumeration headerNames = request.getHeaderNames();
         while(headerNames.hasMoreElements()) {
             String headerName = (String)headerNames.nextElement();
             out.println("<p>"+headerName+"  "+request.getHeader(headerName)+"</p>");
         }

posted @ 2008-08-20 13:58 LukeW 阅读(162) | 评论 (0)编辑 收藏

期待eclipse RCP:Design, Coding and Packaging 第二版

eclipse_rcp_book_cover

《Eclipse Rich Client Platform – Design, Coding and Packaging》是在Eclipse 3.1发布以后,针对 Eclipse RCP 平台的一本非常重要,也非常有用的书。 最近,这本书的作者们开始了第二版的编写,将以最新的 Eclipse 3.4 为基础,并且预计在08年年底正式发布。 新书的作者透露,第二版中除了以3.4为基础,还将加入很多新东西:

非常期待着这本书。

posted @ 2008-08-20 10:13 LukeW 阅读(131) | 评论 (0)编辑 收藏

eclipse真机调试J2me程序

简单记录一下遇到的几个问题:

1. 真机调试其实是厂商SDK根据JAVA的调试API层实现的.

2. 要注意JAD文件中字段的长度不要过长, 否则会报错, 无法调试.

3. 相比较, netbeans进行真机调试要方便一些. 当然eclipse应该也没问题(懒得实验了..).

posted @ 2008-08-15 15:22 LukeW 阅读(297) | 评论 (0)编辑 收藏

J2me的List总结

List控件是使用频率非常高的显示控件之一了。但是最近发现它的一点不足。
那就是getSelectedIndex()函数,一般情况下它都能正常工作。唯有在List处于复选模式(MULTIPLE)时,使用该函数无法获得当前高亮条选中索引,而总是返回-1。

参考List控件的源代码,可以发现,List类实现了Choice接口,并且包含一个ChoiceGroup成员。它才是实现List大部分功能的大功臣。
List类的getSelectedIndex()方法,实际上就是ChoiceGroup的getSelectedIndex()方法。大家可以参考Doc中关于ChoiceGroup的getSelectedIndex()方法的如下部分。
“For ChoiceGroup objects of type MULTIPLE, this always returns -1 because no single value can in general represent the state of such a ChoiceGroup.”

就是说,List在多选模式(MULTIPLE)下,我们是无法获取当前高亮条所在项的索引值的。
当然,你可以自己数数。

也许你觉得这个问题不算严重,也许吧。但我觉得对于程序员来说,最严重的问题就是理解发生偏差。
比如我,在发现这个问题之前。凭借多年的编程经验,我很确定很确定的认为,这个getSelectedIndex()永远能够获得当前光标所指项的索引值。
……当错误来临时,我百思不得其解。最后,花了好些时间去调试才发现,那个值总等于-1。操!花了太多时间去猜测原本正确的代码。
我只想告诉大家,真正耗费时间最多,让人最恶心的错误,往往就是这样的问题。再操!

那么有什么简单的解决办法么?
我可以很负责任的告诉你,自己写一个多选的List控件吧,记住不要让getSelectedIndex()总返回-1。即使是多选,有时候也是需要这个值的。

也许你想继承List,然后重载getSelectedIndex()方法。但你无法重载List的keyPressed方法(其实List等Screen控件都用Canvas写的)。
……或许还能想到别的什么好办法。不过我的选择是自己写一个List控件代替它(如果对它感兴趣,请回复,改天我在弄出来吧)。

至于List的其它功能,就没什么重复的必要的,看看文档吧。

posted @ 2008-08-15 15:20 LukeW 阅读(295) | 评论 (0)编辑 收藏

Vector 总结

Vector是在java编程中比较常用的动态数组。一直以为它是个数组的链表,当内存不够用了,就新申请一个capacityIncrement大小的数组,连到原来的链表上。
在仔细阅读源代码后发现,Vector并没有任何链表的性质。它是一个纯粹的数组。当内存不够用时,就重新初始化一个容量较大新数组,然后使用System.arraycopy()函数将原有的数组copy到新的数组当中。

System.arraycopy()是一个由系统平台来实现的函数,这样的系统调用性能是比较高的。
即使如此,我们在写程序时,注意initialCapacity(初始容量)和capacityIncrement(增量)的设置,将会有效的减少重新定义数组并且拷贝数组的次数。

例如:
Vector v = new Vector(1010);//初始容量为10,增量10
Integer[] ints = new Integer11;
for (int i = 0; i < ints.length; i++) {
ints 
= new Integer(i);
v.addElement(ints);
}

在这里,当v添加第11个Integer的时候,v就会自动创建一个长度为20的数组(当前容量+增量),以后每次装满数组,都会重新按增量追加数组长度。
所以,设置一个合适的初始容量和增量,将会提高Vector的效率。千万别像我一样,把它当做链表。因为链表在增长空间时是不会影响到以前使用的空间和数据的。

我想指出一点,因为Vector中存储的,实际上都是Object变量,大家可以把它理解为指针。重新初始化Vector内置的数组并且拷贝,相当于对一个指针数组进行操作,并不等同于对输入类型的重新分配内存。
就是说Vector的重新分配,不论进行多少次都不涉及到Integer对象的创建,拷贝等工作。它只是重新创建并拷贝“指针”数组,也就是Object数组。
此外,java中有一个容易被忽视的基本概念。当某个对象再没有指向该对象的引用时,垃圾回收器才会自动将其自动释放。不小心使用,很容易给程序造成内存问题。
例如:

Vector v = new Vector(1010);//初始容量为10,增量10
Integer[] ints = new Integer11;
for (int i = 0; i < ints.length; i++) {
ints 
= new Integer(i);
v.addElement(ints);
}
ints 
= null;//对它的释放将造成数组元素的释放。但是由于v中还保存有元素的引用,所以这些Integer对象并不会被回收。
我强调这个问题,是因为曾经写过一段代码:
Image img = Image.createImage(”/xxx.png”);
Image img2 = img;
这张图片非常大,在使用完img之后,我释放掉它,然后重新申请另一个图片。于是,内存爆了。因为img2仍然持有xxx.png的引用,所以无法释放。
Vectro中存储的也是引用,所以在使用时应该更加注意编程规范,以免发生类似的问题。其实应该尽量避免使用多个引用。

上面说了这些题外话,正是想警告各位程序员,Vector是一个可变的Object数组,一个引用数组。所以请大家使用时要小心,别想当然的以为它是一个容器,它里面存储的可不是对象,而是引用。
而且,在释放曾经加入到vector的对象时,对象本身并不会被真正释放,得到回收。只是原有的引用无法再使用罢了。

最后还有一点技巧。
应该尽量使用索引获取对象,避免使用IndexOf()方法。把它当做堆栈来使用时更应该注意,要避免使用insertElementAt()方法。
此外,j2me中的Stack类是基于Vector实现的,使用时也要留心。

posted @ 2008-08-15 15:19 LukeW 阅读(187) | 评论 (0)编辑 收藏

汉字转拼音

     摘要: 在网上参考了一些汉字转换到拼音的资料。思路应该只有以下两种。 1,查表法。这样做需要一个庞大的映射表,在j2me环境下不大合适。不过效果好,有些还支持多音字。 2,使用GB字库的映射关系。因为GB2312及其扩展GBK的汉字编码都根据区位于拼音存在映射关系。 实际上网络上的大部分文章都是根据第二种方法来实现的。 我也是采用这种方法,因为它基本上可以利用GB2312字库,直接映射成拼音。...  阅读全文

posted @ 2008-08-15 15:15 LukeW 阅读(407) | 评论 (0)编辑 收藏

GB2312转换Unicode

之前的文章介绍了在j2me环境下GB2312转换为UTF-8的方法。
后来继续对编码及char类型进行学习,发现一些有趣的问题。
首先java环境下的char类型变量,实际上就是以unicode方式存储的。
所以以下方法有效:

输入unicode编码的byte数组,即可两两拼接成一个char。
而String类型实际上就是在char数组的基础上衍生出来的。大家可以参考cldc的源代码。
public static String read_Uni(byte[] word_unicode) {
        StringBuffer stringbuffer 
= new StringBuffer("");
        
for (int j = 0; j < word_unicode.length;) {
            
int l = word_unicode[j++];
            
int h = word_unicode[j++];
            
char c = (char) ((l & 0xff| ((h << 8& 0xff00));
            stringbuffer.append(c);
        }
        
return stringbuffer.toString();
    }


j2me环境下也是如此。
所以在第一次给出的转换类中,提供的gb2312到utf-8直接转换的快速方法。现在看来是画蛇添足了。

根据以上经验,更新转换类如下:
public class HGB2312 {
 
        
private byte[] map = new byte[15228];
 
        
public HGB2312() throws Exception {
            InputStream is 
= getClass().getResourceAsStream("/gb2u.dat");
            is.read(map);
            is.close();
        }
 
        
public String gb2utf8(byte[] gb) {
            StringBuffer sb 
= new StringBuffer();
            
int c, h, l, ind;
            
for (int i = 0; i < gb.length;) {
                
if (gb[i] >= 0) {
                    sb.append((
char) gb[i++]);
                } 
else {
                    h 
= 256 + gb[i++];
                    l 
= 256 + gb[i++];
                    h 
= h - 0xA0 - 1;
                    l 
= l - 0xA0 - 1;
                    
if (h < 9) {
                        ind 
= (h * 94 + l) << 1;
                        c 
= (byte2Int(map[ind]) << 8 | byte2Int(map[ind + 1]));
                        sb.append((
char) c);
                    } 
else if (h >= 9 && h <= 14) {
                        sb.append((
char0);
                    } 
else if (h > 14) {
                        h 
-= 6;
                        ind 
= (h * 94 + l) << 1;
                        c 
= (byte2Int(map[ind]) << 8 | byte2Int(map[ind + 1]));
                        sb.append((
char) c);
 
                    } 
else {
                        sb.append((
char0);
                    }
                }
            }
            
return sb.toString();
        }
 
        
private int byte2Int(byte b) {
            
if (b < 0) {
                
return 256 + b;
            } 
else {
                
return b;
            }
        }
    }


这个方法明显要比第一次快很多了,直接查表,然后拼接成String,不需要转换成utf-8编码。

数据文件请在http://download.csdn.net/source/263609获取

总之,java中的char类型实际上是存储了unicode编码。目前在nokia 5300上测试通过。
我觉得其它机器也应该是这样。如果哪位大侠知道这方面的资料,请赐教。

posted @ 2008-08-15 14:58 LukeW 阅读(608) | 评论 (0)编辑 收藏

MIDP2.0及MIDP数字签名

本文档是 WoTrust 根据 Forum Nokia 提供的技术文档《MIDP 2.0: Tutorial On Signed MIDlets》翻译整理的,请同时参考此英文原文文档。请用户在编写 MIDlet 和签名 MIdlet 之前阅读此文档,以便对 MIDP2.0 的安全机制有一个深刻的理解,有助于用户能用好 MIDlet 代码签名证书。

一、概述

MIDP2.0 采用了全新的安全机制,这对于需要调用一个敏感的(重要的)函数和 API 的 MIDlet 开发者来讲是必须了解的,如:网络连接 API 、消息 API 和推 (Push) 函数等,还有一些可选的 MIDP 包也有许多受限制的 API 。

虽然购买代码签名证书需要费 用,但签名 MIDlet 对开发者来讲是收益非浅的,因为许多受保护的 API 都是需要签名的,以保护开发者和用户的利益。当然,有些应用是不需要签名的,如有些不需要联网的仅用到一些图形 API 的小游戏软件。但一些重要的应用,如:连接网络、发送短消息 ( 短信和彩信 ) 或访问移动终端 ( 智能手机、 PDA 等,以下简称为手机 ) 上的 PIM( 个人信息管理 ) 数据等等都需要签名。

数字签名 MIDlet 的好处包括:

(1) 基于 MIDlet 的安全策略,某些功能是必须签名才能使用的,而有些功能虽然不签名也可以使用,但必须要求用户在使用时确认和修改其安全策略,如:写用户数据缺省是不允许没有签名的 MIDlet 操作的;

(2) 基于手机的系统安全和移动网络的安全考虑,某些手机制造商、移动运营商等可能拒绝没有签名的 MIDlet 在手机上安装和运行;

(3) 大大改善用户体验,让用户使用方便,使得用户不会遭遇调用受保护 API 时的安全警告的烦恼;

(4) 出于安全考虑,安装没有签名的 MIDlet 是会有安全警告的,而相反,安装已经签名的 MIDlet 则不会出现烦人的警告,手机会自动验证签名而顺利地安装成功;

(5) 已经签名的 MIDlet 将使得用户能改善其低安全策略设置,提高手机的安全性;

(6) 确保已经签名的 MIDlet 不会被非法篡改和非法盗用。

二、 MIDP 2.0 安全机制

MIDP 是一个开放的平台,使得任何人都可以为支持 MIDP 的设备开发各种应用软件,一般都是移动终端设备。 MIDlet 套件可以以匿名方式通过网络下载,非常方便,但这也会带来许多安全问题和隐私信息保护问题,用户会问: MIDlet 能把用户的个人信息发给不知道的服务器吗?会自动产生没有授权的呼叫或短消息而给用户带来费用吗?恶意软件会破坏手机?等等。

除了 Java 语言的安全特性外, MIDP 还增加了许多安全考虑。 MIDP 2.0 比 MIDP 1.0 增强了安全策略,把 API 分为普通 API 和敏感 API ,如:通过 HTTP 协议访问移动网络,由于会给用户产生费用, 所以被列为 敏感 API 。 MIDlet 2.0 推出了可信任 MIDlet(trusted) 和不可信任 MIDlet(untrusted) 的概念,一个不可信任 MIDlet 只能访问有限的 API ,同时还需要用户手动确认并修改其安全策略;而可信任 MIDlet 则自动继承系统中的安全策略而获得访问许可。

许可 (Permissions) 用于需要身份认证的 敏感 API 。 MIDP 2.0 要求调用 敏感 API 之前必须获得必要的许可,这些许可包的命名同 J2SE 许可,如: HTTP 连接许可同样称为: javax.microedition.io.Connector.http 。 有关许可的文档同意归类在受保护 API 中。

2.1 Protection Domains( 保护域 )

保护域是 MIDP 2.0 中一个非常重要的安全概念,一个保护域就是一个许可集和一种交互模式,这些许可既可以是自己继承的,也可能是用户设置的,前者称为允许 (allowed) ,而后者称为用户允许 (user permission) 。当一个 MIDlet 被安装后,它被分配到一个指定的保护域而获得它的许可和交互模式。

而用户允许则需要用户自己决定是否同意,用户既拒绝一个许可,也可以同意。用户允许有 3 种交互模式: blanket( 普遍适用 ) 、 session( 短期适用 ) 和 oneshot( 本次适用 ) , 普遍适用 模式就是 MIDlet 安装时获得的许可一直有效,除非用户取消这些许可;而 短期适用 模式则是指第一次调用 API 时需要用户允许,有效期到此 MIDlet 套件运行结束;而 本次适用 模式则在每次调用 API 时都要求用户允许。保护域为用户许可定义了缺省的交互模式。

一个 MIDlet 套件使用 MIDlet-Permissions 和 MIDlet-Permissions-Opt 属性来明确地定义其许可,可以是在 JAD 文件中定义,也可以在 manifest 文件中定义。其中: MIDlet-Permissions 定义了 MIDlet 套件中必须具有的许可,而 MIDlet-Permissions-Opt 则定义希望具有的许可。如:一个应用软件的基本要求是要有 http 连接才能正常工作,同时,也可以使用 https 连接 ( 服务器部署了 SSL 证书 ) 来增强安全性,但不是必须的,这样,这个应用软件的应用描述可以是这样:

MIDlet-Permissions: javax.microedition.io.Connector.http

MIDlet-Permissions-Opt: javax.microedition.io.Connector.https

请注意:一个 MIDlet 所要求的许可必须是安装时分配的保护域所具有的许可的子集。如: Nokia S60 MIDP Emulator Prototype 2.0 (SDK) 有一个叫做“ minimum ”的域,此域没有任何许可。所以,如果一个含有许多许可的已经签名的 MIDlet 如果被安装到此域,则会安装失败,因为此域不支持这些许可。同样,如果一个许可的名称有拼写错误,则一样会导致安装失败,因为域中没有此拼写错误的许可。

MIDP 2.0 为 GSM/UTMS 设备定义了 4 种保护域: manufacturer( 设备制造商 ) , operator( 移动运营商 ) , trusted third party( 可信任的第三方 ) , and untrusted( 不受信任域 ) , 除了 untrusted 域外,每个保护域都对应一组根证书,用于签名 MIDlet 的签名证书的根证书必须包含在这些根证书中,使用不同的签名证书签名的 MIDlet 将被自动归类予根证书所属的保护域,根证书与保护域的关系是:一个保护域可以有许多个根证书,而一个根证书只能对应于一个保护域。

具体来讲, manufacturer 域属于设备制造商,其根证书是设备制造商自己的根证书;而 operator 域运营商,一般使用其 SIM 卡中的根证书;而 trusted third party 域则预置了全球知名的数字证书颁发机构 (CA) 的根证书,用于验证由 CA 颁发的 MIDlet 签名证书;而 untrusted 域没有根证书,将用于没有签名的 MIDlet 和 MIDP 1.0 。

Thawte 和 VeriSign 的根证书已经预置在 trusted third party 域 中,其 Java 代码签名证书可以用于签名 MIDlet 。当然,用户也可以选择使用设备制造商和移动运营商颁发的证书,只要其根证书已经包含在手机的 4 个保护域中。据 WoTrust 了解,大多数摩托罗拉 (Motorola) 手机只支持设备制造商域,所以,只能向 Motorola 申请签名服务了。

请注意:由于 MIDP 2.0 也在不断地修改和增补,所以,可能不用的移动网络运营商有不同的保护域和许可,用户可能需要向移动运营商了解详细信息。而最简单的方法是检查目标用户所使用的手机的根证书是否有计划购买的 MIDlet 签名证书的根证书。

2.2 Untrusted MIDlet ( 不受信任的 MIDlet)

MIDP 2.0 定义了那些 API 是 untrusted 的,这些 Jar 文件的来源和完整性是不能被手机验证的。但这并不意味着这些 MIDlet 不能被安装和运行,而是运行这些 MIDlet 需要用户人工确认允许。而所有 MIDP 1.0 的 MIDlets 都被定义为 untrusted 。

untrusted 的 MIDlets 只能调用一个不需要许可保护的 API ,如:
java.util
java.lang
java.io
javax.microedition.rms
javax.microedition.midlet
javax.microedition.lcdui
javax.microedition.lcdui.game
javax.microedition.media
javax.microedition.media.control

如果 untrusted MIDlet 套件试图调用一个被保护的 API 而且没有被人工允许,则会产生一个 SecurityException 而被 MIDlet 按照安全策略处理。请注意: Nokia 的 UI API 是不被保护的,包括类: com.nokia.mid.sound 和 com.nokia.mid.ui 。

2.3 Trusted MIDlets ( 可信任的 MIDlets)

如果手机能验证 MIDlet 的身份和完整性 ( 也就是已经数字签名 ) ,则会自动分配一个合适的保护 域这种 MIDlet 套件就称为可信任的 MIDlet 。一个可信任的 MIDlet 套件所要求的许可将被准许,只要所属的保护域拥有这种许可,假如许可: javax.microedition.io.Connector.http 已经在所属保护域中是允许的,则 MIDlet 在打开一个 http 连接时是不需要用户确认的。

请不要混淆了可信任的 MIDlet 套件和可信任的保护域的不同,每个可信任的 MIDlet 套件依据安全策略被分配到一个特定的保护域。

您需要使用一个手机中已经预置的根证书的证书颁发机构颁发的代码签名证书来签名 MIDlet ,否则将不能通过身份验证。成功签名后的 JAD 文件中一定会包含有整个签名证书的证书链,属性名称为: MIDlet-Certificate-1-1 就是您的签名证书,而 MIDlet-Certificate-1-2 就是 CA 的中级根证书,而 MIDlet-Certificate-1-3 就是 CA 的顶级根证书。同时还会有一个 MIDlet-Jar-RSA-SHA1 属性就是 JAR 文件的摘要。

当一个 MIDlet 被下载或被安装时, MIDlet 应用管理器首先会检查 JAD 文件中是否包含了 MIDlet-Jar-RSA-SHA1 属 性,如果有,则启动如下验证过程:首先会读出 MIDlet-Certificate-1-1 、 MIDlet-Certificate-1-2 和 MIDlet-Certificate-1-3 属性中的证书,并与已经预置的根证书相比较,如果证书链能被根证书验证,则表明开发者身份已经被验证。接着就会使用用户证书来解密 MIDlet-Jar-RSA-SHA1 属 性的摘要,再计算出已经下载的 Jar 文件的摘要,比较两个摘要是否相等,如果相等,则表明 MIDlet 代码自签名后没有被修改。这样,既验证了身份又检查了完整性的 MIDlet 会被分配到所属根证书所对应的保护域中。但是,如果 MIDlet 中的许可属性 ( MIDlet-Permissions ) 中有一个或多个不属于所属的保护域,则仍然不允许安装。而如果 MIDlet 中的可选许可属性 ( MIDlet-Permissions-Opt ) 中有一个或多个不属于所属的保护域,会允许安装。可见,正确设置许可属性和可选许可属性非常重要。

2.4 Function Groups ( 功能分组 )

为了简化用户管理操作, MIDlet 把一些类似功能分组,这样,用户只需对功能组设置许可即可。如:许可 “Net Access”( 网络访问 ) 组来代替许可 javax.microedition.io.Connector.http ,这对于简化手机的交互操作非常有用。

MIDP 2.0 和 JTWI 定义了如下 7 个功能组:

(1) Net Access: 包括所有网络连接许可;

(2) Messaging: 包括所有与发送和接收短消息 ( 短信和彩信 等 ) 相关的许可;

(3) Auto Invocation : 包括与自动启动 MIDlet 相关的许可,如: Push Registration

(4) Local Connectivity : 包括与本地连接相关的许可,如: IrDA 或 蓝牙;

(5) Multimedia Recording : 包括与允许录音、照相、摄像等相关的许可;

(6) Read User Data : 包括读取用户数据相关的许可,如:通讯录、日程表等;

(7) Write User Data : 包括写用户数据相关的许可。

不同的手机支持不同的功能组,如: Multimedia Recording 就不会包含在没有摄录装置的手机中。当然,也有可能将来会增加更多的功能组。

功能组也同时定义了不同的域的不同交互方式,如:在不信任域, “Net Access” ( 网络访问 ) 被设置为 session( 短期适用 ) 或 denied( 拒绝 ) ,而在可信任域则可以设置为 oneshot 、 blanket 和 denied 的。

三、仿真器和手机的缺省安全设置

让我们来看看具体的使用 Thawte 或 VeriSign 代码签名证书签名后的 MIDlet 在 trusted third party 域中的所有缺省许可,如下图 1 所示,点击 NDS 3.0 的“ Config Emulators ”就可以看到仿真器在 trusted third party 域的缺省安全设置是“ Ask first time ”,即第 1 次使用是需要确认:


如下图 2 所示,您可以下拉所有功能组的许可设置,如“ Network Access ”就有 4 个选项可以修改: Ask first time 、 Ask every time 、 Always allowed 和 Not allowed :


而如下图 3 所示,在“ Real Life ”模式,也就是实际手机的运行模式,可以看出:定义的 7 个功能组都是“ Always allowed ” ( 总是允许 ) ,这就显示出 MIDlet 签名对于开发商来讲是多么的重要,将大大方便了用户的使用,再也不需要用户操作烦人的系列确认了。

posted @ 2008-08-15 14:53 LukeW 阅读(314) | 评论 (0)编辑 收藏

树形结构

树形结构(tree)是比较常用的数据结构了,MIDP中没有它的身影,不然我就不用写这篇文章了。
代码如下:
/**
 *
 * 
@author hunhun1981
 
*/
public class HTree {
 
 
private HNode root;
 
 
private HNode current;
 
 
private int currDepth;
 
 
private int maxDepth;
 
 
public HTree(Object rootValue) {
  root 
= new HNode(null, rootValue);
  current 
= root;
 }
 
 
public void goRoot() {
  current 
= root;
  currDepth 
= 0;
 }
 
 
public boolean goChild(int index) {
  
if (current.childList != null) {
   
if (current.childList.size() > 0
     
&& index < current.childList.size()) {
    current 
= (HNode) current.childList.elementAt(index);
    currDepth
++;
    
if (currDepth > maxDepth) {
     maxDepth 
= currDepth;
    }
    
return true;
   }
  }
  
return false;
 }
 
 
public void goBack() {
  
if (current.father != null) {
   current 
= current.father;
   currDepth–;
  }
 }
 
 
public Object getCurrent() {
  
return current.value;
 }
 
 
public int getCurrentDepth() {
  
return currDepth;
 }
 
 
public int getMaxDepth() {
  
return maxDepth;
 }
 
 
public Object[] getChilds() {
  
if (current.childList != null) {
   
if (current.childList.size() > 0) {
    Object[] ret 
= new Object[current.childList.size()];
    
for (int i = 0; i < ret.length; i++) {
     ret[i] 
= ((HNode) current.childList.elementAt(i)).value;
    }
    
return ret;
   }
  }
  
return null;
 }
 
 
public Object getChild(int index) {
  
if (current.childList != null) {
   
if (current.childList.size() > 0
     
&& index < current.childList.size()) {
    
return ((HNode) current.childList.elementAt(index)).value;
   }
  }
  
return null;
 }
 
 
public void addChild(Object obj) {
  
if (current.childList == null) {
   current.childList 
= new Vector();
  }
  current.childList.addElement(
new HNode(current, obj));
 }
 
 
public void addChilds(Object[] objs) {
  
if (current.childList == null) {
   current.childList 
= new Vector();
  }
  
for (int i = 0; i < objs.length; i++) {
   current.childList.addElement(
new HNode(current, objs[i]));
  }
 }
 
 
public int hasChild() {
  
if (current.childList == null || current.childList.size() <= 0) {
   
return 0;
  } 
else {
   
return current.childList.size();
  }
 }
 
 
private class HNode {
 
  
public Vector childList;
 
  
public HNode father;
 
  
public Object value;
 
  
public HNode(HNode father, Object value) {
   
this.value = value;
   
this.father = father;
   
this.childList = null;
  }
 }
}


这个类实现简单,没有包含复杂的功能,仅仅用来做树形数据的存储还是不错的。比如游戏中用来管理场景,管理资源;应用中用来作分类数据的表现等等。完全足以胜任。
使用方法如下:
HTree tree = new HTree(”root”);//会自动创建一个默认的根节点
tree.addChild(”天才”);//在根节点添加新的节点
tree.addChild(”白痴”);
tree.goChild(
0);//进入到当前节点的第一个节点(天才)。
tree.addChild(”天才1号”);//在当前节点(天才)添加新的节点
tree.addChild(”天才2号”);
tree.goBack();
//返回当前节点(天才)的父节点(根)
tree.goChild(1);//进入到当前节点的第二个节点(白痴)。
tree.addChild(”白痴1号”);//在当前节点(白痴)添加新的节点
tree.addChild(”白痴2号”);
tree.goRoot();
//完成创建后将当前节点设置为根节点。

上面的代码创建了一棵完整的树,当然,您可以使用任何对象代替这里存储的String对象。
还有一些方法,一看函数名大概都能明白,就不再唠叨了。
遍历的方法于上面创建树的方法相似,总之,要注意当前节点的位置,以免下次使用时处在错误的位置。
有兴趣的朋友可以扩展一下遍历方法。不过我觉得没必要。因为J2ME环境下更需要的是树形结构,而不是强大的tree对象。

总之,我比较倾向于简单实现,希望它不太让人觉得简陋就好。从实用出发,它还是能够满足大部分受限平台的需求的。

posted @ 2008-08-15 14:51 LukeW 阅读(195) | 评论 (0)编辑 收藏

URLEncoding

URLEncoding是用于解决链接字符串中包含中文字符的一种转换编码。各种编程环境下几乎带有它的库函数。

不过,J2ME除外。

好在JAVA的源代码中带有这个类,我们把它拷贝到J2ME环境下编译到我们的应用当中就可以了。

该文件位于JDK的目录下src.zip文件中,名叫URLEncoder.java。

但是,这个文件还需要做很多修改才能使用在J2ME环境中。

先警告大家,有几个真机(其中一个就是索爱的,好像是k500c),不管输入什么样的Encodeing都会出错,甚至是“UTF-8”。所以我一怒之下 去除了Encodeing参数。(这可是在实际应用中得出的结论,不去掉的话可以在大部分情况下正常使用,但是,现实总是有点缺陷)

修改后的代码如下,大家请放心使用。如果有兴趣,可以比较两个代码,看看我改动了什么地方。


import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
public class HURLEncoder {
 
 
private static boolean[] dontNeedEncoding;
 
 
static {
  dontNeedEncoding 
= new boolean[256];
 
  
for (int i = 0; i < 256; i++) {
   
boolean b = ((i >= ‘0′) && (i <= ‘9′))
     
|| ((i >= ‘A’) && (i <= ‘Z’)) || ((i >= ‘a’) && (i <= ‘z’));
 
   dontNeedEncoding[i] 
= b;
  }
 
  dontNeedEncoding[’ ‘] 
= true;
  dontNeedEncoding[’
-'] = true;
  dontNeedEncoding[’_'] = true;
  dontNeedEncoding[’.'] = true;
  dontNeedEncoding[’*'] = true;
 }
 
 
public static String encode(String s) {
 
  
boolean wroteUnencodedChar = false;
 
  StringBuffer writer 
= new StringBuffer();
 
  StringBuffer out 
= new StringBuffer(s.length());
 
  
for (int i = 0; i < s.length(); i++) {
   
char c = s.charAt(i);
 
   
if ((c < 256&& dontNeedEncoding[c]) {
    
if (c == ‘ ‘) {
     c 
= ‘+’;
    }
 
    out.append((
char) c);
    wroteUnencodedChar 
= true;
   } 
else {
    
try {
     
if (wroteUnencodedChar) {
      writer 
= new StringBuffer();
      wroteUnencodedChar 
= false;
     }
 
     writer.append(c);
 
     
if (c >= 0xD800 && c <= 0xDBFF) {
      
if ((i + 1< s.length()) {
       
int d = (int) (s.charAt(i + 1));
 
       
if (d >= 0xDC00 && d <= 0xDFFF) {
        writer.append(d);
        i
++;
       }
      }
     }
 
    } 
catch (Exception e) {
     writer 
= new StringBuffer();
     
continue;
    }
 
    String str 
= writer.toString();
 
    ByteArrayOutputStream baos 
= new ByteArrayOutputStream();
    DataOutputStream dos 
= new DataOutputStream(baos);
    
try {
     dos.writeUTF(str);
     dos.flush();
    } 
catch (Exception e) {
     e.printStackTrace();
    }
 
    
byte[] temp = baos.toByteArray();
    
byte[] ba = new byte[temp.length - 2];
    
for (int ix = 0; ix < ba.length; ix++) {
     ba[ix] 
= temp[ix + 2];
    }
 
    
for (int j = 0; j < ba.length; j++) {
     out.append(’
%');
 
     
char ch = forDigit((ba[j] >> 4& 0xF16);
     out.append(ch);
 
     ch 
= forDigit(ba[j] & 0xF16);
     out.append(ch);
    }
 
    writer 
= new StringBuffer();
    
try {
     dos.close();
     baos.close();
    } 
catch (Exception e) {
     e.printStackTrace();
    }
   }
  }
 
  
return out.toString();
 }
 
 
private static char forDigit(int digit, int radix) {
  
if ((digit >= radix) || (digit < 0)) {
   
return ‘0′;
  }
  
if (digit < 10) {
   
return (char) (’0′ + digit);
  }
  
return (char) (’A’ + digit - 10);
 }


posted @ 2008-08-15 14:49 LukeW 阅读(1503) | 评论 (0)编辑 收藏

GB2312转换为UTF-8

摩托罗拉的部分手机(a1200,e60等),不支持gb2312编码。曾经给我造成了不少麻烦。现在,大家可以分享解决这个问题的一些经验。

关于gb2312,unicode,utf-8的一些资料,大家请自行搜索。一下列举几个比较好的资源网址。
http://baike.baidu.com/view/25492.htm
http://www.utf.com.cn/article/s45
http://www.utf.com.cn/article/s74
http://www.haiyan.com/steelk/navigator/ref/gb2312/gbindex.htm

要点:
1,gb2312于unicode或者utf-8之间并不存在直接的映射关系。所以我们只能通过查表法来进行转换。
2,utf-8是unicode用于网络传输的一种形式,它与unicode之间是可以通过运算来进行转换的。
3,j2me环境使用的都是utf-8编码,但是请注意,j2me中的utf-8编码比较特殊,在整个编码前面对了两个字节,用于存放字符串的长度。

过程:
1,制作映射表gb2312-unicode,应为汉字的unicode比utf-8要小,这样做出的表也会小一些,而且对于unicode的可扩展性也强一些。
2,先将gb2312编码串通过查表,转换为unicode。
3,然后通过运算,将unicode转换为utf-8,以便在j2me环境下使用。

我修改了Herong Yang大侠的一个映射表生成函数,原文请参考http://www.herongyang.com/gb2312/gb2312_unicode.html
它的作用是生成一个二进制的gb2312到unicode的查找表,它按照gb2312的分区,分块特性,将其对应的unicode按顺序存入指定的位置。
这样我们只需要根据gb2312的编码,计算出索引就可以获取编码对应的unicode了。
由于是修改的代码,没脸贴出来,大家有需求可以直接参考Herong Yang的文章,然后根据自己需求修改并生成自己的映射表。

这里我把自己这个转换表文件以及访问代码公开。
http://download.csdn.net/source/263609
转帖请注明。这是个傻瓜化的代码,在java中给它gb2312的byte数组,它就给你构造出字符串。
用在不支持gb2312的手机上非常方便。这个转换表的大小是15228byte,对j2me来说还是可以接受的。

如果有朋友需要沟通,可以发邮件到hunhun1981@hotmail.com

import java.io.InputStream;
 
public class HGB2312 {
 
    
private byte[] map = new byte[15228];
 
    
private byte[] buffer;
    
private int index;
 
    
public HGB2312() throws Exception {
        InputStream is 
= getClass().getResourceAsStream("/gb2u.dat");
        is.read(map);
        is.close();
    }
 
    
public String gb2utf8(byte[] gb) throws Exception {
        buffer 
= new byte[gb.length + gb.length / 2 + 3];
        index 
= 0;
        
int c, h, l, ind;
        
for (int i = 0; i < gb.length;) {
            
if (gb[i] >= 0) {
                fillBuffer(gb[i
++]);
            } 
else {
                h 
= 256 + gb[i++];
                l 
= 256 + gb[i++];
                h 
= h - 0xA0 - 1;
                l 
= l - 0xA0 - 1;
                
if (h < 9) {
                    ind 
= (h * 94 + l) << 1;
                    c 
= (byte2Int(map[ind]) << 8 | byte2Int(map[ind + 1]));
                    fillBuffer(c);
                } 
else if (h >= 9 && h <= 14) {
                    fillBuffer(
0);
                } 
else if (h > 14) {
                    h 
-= 6;
                    ind 
= (h * 94 + l) << 1;
                    c 
= (byte2Int(map[ind]) << 8 | byte2Int(map[ind + 1]));
                    fillBuffer(c);
                } 
else {
                    fillBuffer(
0);
                }
            }
        }
        
// ind = index - 2;
        
// h = (byte) ((ind >> 8) & 0x7F);
        
// l = (byte) (ind & 0xFF);
        
// buffer[0] = h;
        
// buffer[1] = l;
 
        
return new String(buffer, 0, index, "UTF-8");
    }
 
    
private void fillBuffer(int value) {
        
if (value <= 0x0000007F) {
            buffer[index
++= (byte) value;
        } 
else if (value >= 0x00000080 && value <= 0x000007FF) {
            
byte b1 = (byte) (0x60 | (value >> 6));
            
byte b2 = (byte) (0x80 | (value & 0x3F));
            buffer[index
++= b1;
            buffer[index
++= b2;
        } 
else if (value >= 0x00000800 && value <= 0x0000FFFF) {
            
byte b1 = (byte) (0xE0 | (value >> 12));
            
byte b2 = (byte) (0x80 | ((value >> 6& 0x3F));
            
byte b3 = (byte) (0x80 | (value & 0x3F));
            buffer[index
++= b1;
            buffer[index
++= b2;
            buffer[index
++= b3;
        } 
else if (value >= 0x00010000 && value <= 0x001FFFFF) {
            
byte b1 = (byte) (0x1E | (value >> 18));
            
byte b2 = (byte) (0x80 | ((value >> 12& 0x3F));
            
byte b3 = (byte) (0x80 | ((value >> 6& 0x3F));
            
byte b4 = (byte) (0x80 | (value & 0x3F));
            buffer[index
++= b1;
            buffer[index
++= b2;
            buffer[index
++= b3;
            buffer[index
++= b4;
        } 
else if (value >= 0x00200000 && value <= 0x03FFFFFF) {
            
byte b1 = (byte) (0x3E | (value >> 24));
            
byte b2 = (byte) (0x80 | ((value >> 18& 0x3F));
            
byte b3 = (byte) (0x80 | ((value >> 12& 0x3F));
            
byte b4 = (byte) (0x80 | ((value >> 6& 0x3F));
            
byte b5 = (byte) (0x80 | (value & 0x3F));
            buffer[index
++= b1;
            buffer[index
++= b2;
            buffer[index
++= b3;
            buffer[index
++= b4;
            buffer[index
++= b5;
        } 
else if (value >= 0x04000000 && value <= 0x7FFFFFFF) {
            
byte b1 = (byte) (0x7E | (value >> 30));
            
byte b2 = (byte) (0x80 | ((value >> 24& 0x3F));
            
byte b3 = (byte) (0x80 | ((value >> 18& 0x3F));
            
byte b4 = (byte) (0x80 | ((value >> 12& 0x3F));
            
byte b5 = (byte) (0x80 | ((value >> 6& 0x3F));
            
byte b6 = (byte) (0x80 | (value & 0x3F));
            buffer[index
++= b1;
            buffer[index
++= b2;
            buffer[index
++= b3;
            buffer[index
++= b4;
            buffer[index
++= b5;
            buffer[index
++= b6;
        }
    }
 
    
private int byte2Int(byte b) {
        
if (b < 0) {
            
return 256 + b;
        } 
else {
            
return b;
        }
    }
}

posted @ 2008-08-15 14:47 LukeW 阅读(620) | 评论 (0)编辑 收藏

修改png图的调色板

今天在硬盘上挖出这个存放了几年的代码。又回忆起3年前的那个j2me手机游戏程序员……

这个算法是参考一位高人的文章,直接读取并修改png格式图片的调色板,然后生成新的调色板替代原来的。
这样可以实现游戏中常见的变色效果,可以解决游戏容量有限,不能存放太多精灵图片的问题。

具体过程其实并不复杂,大家可以先搜索资料,先看看png图片的格式定义。这个算法正是找到调色板区,根据原有格式修改之后,生成新的crc校验码,然后替换原来的调色板。这样就可以用一个png图片,创建多个变色副本。
public class PalettedImage {
 
    
public Image getPalettedImage(byte[] data, int[] originalColors,
            
int[] palettedColors) {
        
byte[] tempData = new byte[data.length];
        System.arraycopy(data, 
0, tempData, 0, data.length);
        Image img 
= null;
        
int[] parameter = { 000 };
        analyze(tempData, parameter);
        
for (int i = 0; i < originalColors.length; i++) {
            replaceColor(tempData, parameter, originalColors[i],
                    palettedColors[i]);
        }
        fillData(tempData, parameter);
        
try {
            img 
= Image.createImage(tempData, 0, data.length);
        } 
catch (Exception e) {
            System.out.println(
"getPalettedImage  &&  " + e.toString());
        }
        
return img;
    }
 
    
private void analyze(byte[] data, int[] para) {
        
int offset = 8;
        
int chunkLen = 0;
        
while (data[offset + 4!= 0x50 || data[offset + 5!= 0x4c
                
|| data[offset + 6!= 0x54 || data[offset + 7!= 0x45) {
            chunkLen 
= readInt(data, offset);
            offset 
+= (4 + 4 + chunkLen + 4);
        }
        chunkLen 
= readInt(data, offset);
        para[
2= chunkLen / 3;
        para[
0= offset + 8;
        para[
1= offset + 8 + chunkLen;
    }
 
    
private int readInt(byte[] data, int offset) {
        
return ((data[offset] & 0xFF<< 24)
                
| ((data[offset + 1& 0xFF<< 16)
                
| ((data[offset + 2& 0xFF<< 8| (data[offset + 3& 0xFF);
    }
 
    
private void replaceColor(byte[] data, int[] para, int oldColor,
            
int newColor) {
        
byte rr = (byte) ((oldColor >> 16& 0xff);
        
byte gg = (byte) ((oldColor >> 8& 0xff);
        
byte bb = (byte) (oldColor & 0xff);
        
for (int i = 0, offset = para[0], temp = 0; i < para[2]; i++, offset += 3) {
            
if (rr == data[offset] && gg == data[offset + 1]
                    
&& bb == data[offset + 2]) {
                data[offset] 
= (byte) ((newColor >> 16& 0xff);
                data[offset 
+ 1= (byte) ((newColor >> 8& 0xff);
                data[offset 
+ 2= (byte) (newColor & 0xff);
                
break;
            }
        }
    }
 
    
private void fillData(byte[] data, int[] para) {
        
int checksum = update_crc(data, para[0- 4, para[2* 3 + 4);
        data[para[
1]] = (byte) ((checksum >> 24& 0xff);
        data[para[
1+ 1= (byte) ((checksum >> 16& 0xff);
        data[para[
1+ 2= (byte) ((checksum >> 8& 0xff);
        data[para[
1+ 3= (byte) ((checksum) & 0xff);
    }
 
    
private int update_crc(byte[] buf, int off, int len) {
        
int c = 0xffffffff;
        
int n, k;
        
int xx;
        
int[] crc_table = new int[256];
        
for (n = 0; n < 256; n++) {
            xx 
= n;
            
for (k = 0; k < 8; k++) {
                
if ((xx & 1== 1) {
                    xx 
= 0xedb88320 ^ (xx >>> 1);
                } 
else {
                    xx 
= xx >>> 1;
                }
            }
            crc_table[n] 
= xx;
        }
 
        
for (n = off; n < len + off; n++) {
            c 
= crc_table[(c ^ buf[n]) & 0xff^ (c >>> 8);
        }
        
return (c ^ 0xffffffff);
    }
 
}


接口就是getPalettedImage()函数,只需要输入原始图片的byte数组,以及需要替换颜色的颜色值还有目标颜色值就行了。因为可以同时替换多个颜色,所以输入参数是代表颜色的整形的数组。总之,要保证原始颜色与目标颜色一一对应就好了。方法简单实用。

欢迎大家使用并留下宝贵的意见。当然,也可以修改一下这个函数,做一些特殊的效果。这里就不多说了。
不过这个代码用处已经不大,因为现在的手机基本上都支持midp2.0所以可以使用更方便的方法替换颜色。

总之,再次感谢这位已经被我忘掉名字的大侠,关键代码是他写的,我只是修改整理而已。

posted @ 2008-08-15 14:34 LukeW 阅读(609) | 评论 (0)编辑 收藏

Autostarting MIDlets in JP-7 phones using PushRegistry

In Sony Ericsson Java Platform 7 (JP-7) phones, such as the K610 or W850, a new push functionality is introduced. PushRegistry Alarm and PushRegistry SMS are supported throughout the Java Platforms, with JP-4 adding PushRegistry CBS and now JP-7 includes PushRegistry auto start.

Note: the auto start functionality is not supported by the first released K610 and K800 JP-7 phones. Please use the phone's upgrade service to ensure that you are using the latest firmware version.

The auto start functionality can be set static by including it in the .jad file or it can be set dynamically in the code. Two example MIDlets are included as an example of this.

Download the source code and examples here>>

To enable the auto start functionality in the .jad file include the line:
//MIDlet-Push-<n>: <ConnectionURL>, <MIDletClassName>, <AllowedSender>
MIDlet-Push-1: autostart://:, AutoStartStatic, *


To make the MIDlet auto start from the source code the following methods can be used to register and un-register the MIDlet.
//Registers the pushRegistry
public void Register(){
        
// List of registered push connections.
        String connections[];
        
// Check to see if the connection has been registered.
        
// This is a dynamic connection allocated on first
        
// time execution of this MIDlet.
        connections = PushRegistry.listConnections(false);
        
if (connections.length == 0) {
                
try {
                        
//Register so the MIDlet will wake up when phone is started.
                        PushRegistry.registerConnection("autostart://:""AutoStartDyn""*");
                        sDisplayString 
= "MIDlet is registered";
                } 
catch (Exception ex) {
                        System.out.println(
"Exception: " + ex);
                        sDisplayString 
= "Fail: " + ex;
                }
        } 
else {
                sDisplayString 
= "Already registered";
        }
        displayForm.deleteAll();
        displayForm.append(sDisplayString);
}
 
//Unregisters the pushRegistry
public void Unregister(){
        
if (PushRegistry.unregisterConnection("autostart://:")){
                System.out.println(
"The pushRegistry is unregistered");
                sDisplayString 
= "MIDlet is unregistered.";
        }
else{
                System.out.println(
"There is no pushRegistry to unregister");
                sDisplayString 
= "No MIDlet to unregister or failed to unregister";
        }
        displayForm.deleteAll();
        displayForm.append(sDisplayString);
}



To find out if the MIDlet is started via pushRegistry or manually you can inspect the connections registered by pushRegistry. Below is a small example of how to handle pushRegistry autostart or manual startup.
    private void handlePushActivation() {
        String[] connections 
= PushRegistry.listConnections(true);
        
if (connections != null && connections.length > 0) {
            
for (int i = 0; i < connections.length; i++) {
                
if (connections[i].startsWith("autostart")) {
                    alert(
"Application autostarted");
                }
            }
        } 
else {
            alert(
"User started the application");
        }
    }

posted @ 2008-08-15 10:57 LukeW 阅读(346) | 评论 (0)编辑 收藏

Using simultaneous sounds

The code sample below describes how to play two sounds at the same time. This feature is supported by the Sony Ericsson JP-5 platform and onwards.

Only one wav file can be played simultaniously but several midi files might be played at the same time. The number of existing players is only limited by available memory, and you have the possibility to play more than one player at the same time. The example below shows how to do this using one midi file and one wav file.

The code is straight-forward - just load the resource, create the player and start playing the file.

Here's the sample:
InputStream is = getClass().getResourceAsStream(file);
InputStream is1 
= getClass().getResourceAsStream(file1);

player 
= Manager.createPlayer(is, " audio/midi");
player.setLoopCount(
-1);
player.prefetch();
player.realize();

player1 
= Manager.createPlayer(is1, "audio/x-wav");
player1.setLoopCount(
1);
player1.prefetch();
player1.realize();

player.start();
player1.start();

example code

posted @ 2008-08-15 10:34 LukeW 阅读(117) | 评论 (0)编辑 收藏

Serializing an Image

Creating an image from an array of data is an easy task, but to create a byte-array of data from an image is a little more complicated. But it's required if you want to send a modified image to a server.

To create a byte-array of data from an image, we can use the getRGB(..) method in the image class in MIDP 2.0. From the getRGB method we get an int-array of data containing all the ARGB values of each pixel in the image. Integers in java are four bytes, so we need to split each int value into four byte values.

To mask out each of the bytes in the int we can use the 'AND &' operator and the 'shift-right >>' operator. Here's an example:
int ARGB = 0xFFFFFFFF;  // AARRGGBB 
int a = (ARGB & 0xFF000000); 
int r = (ARGB & 0x00FF0000); 
int g = (ARGB & 0x0000FF00); 
int b = (ARGB & 0x000000FF);
 
// Here we move each bit to the right.
= (a >> 24); // a = 0x000000FF 
= (r >> 16); // r = 0x000000FF 
= (g >> 8); // g = 0x000000FF
= b;        // b = 0x000000FF


When we convert the integer to a byte, there are some problems since there are only signed bytes in Java. A byte may contain values between -128 and 127 and from our integer we'll have a value between 0 and 255.

Here are some conversion examples:

Integer val 
127 = byte val 127.
Integer val 
128 = byte val -128.
Integer val 
255 = byte val -1.

byte ba = (byte)(a);  // if a=0x000000FF (255), then ba = -1
byte br = (byte)(r); 
byte bg = (byte)(g); 
byte bb = (byte)(b);


We have to loop though each pixel in the image and get each pixel value to our byte-array. When that's done, we can send the image to a server where we can convert the byte-array back to a integer-array and then show our picture.

So, when we convert the byte back to an integer we have to check if the byte value is less than zero.
int a, r, g, b;
if(ba<0)
     a 
= 256 - a;


Now our new integer value will be between 0 and 255 and we just have to use the 'shift-left <<' operator and add the values together to get our new int-array.
= (a << 24);
= (r << 16);
= (g << 8);
= (b);
 
  
0xFF000000 (a)
+ 0x00FF0000 (r)
+ 0x0000FF00 (g)
+ 0x000000FF (b)
= 0xFFFFFFFF (argb)
 
int ARGB = a+r+g+b;

After the integer-array is recreated we can use the createRGBImage(..) method in the Image class to create our image.

Be aware that the byte-array is quite large as it contains all of the ARGB values for each pixel. If an image is 100*60 px, where each pixel is four bytes, the byte array will be 24kb.
example code

posted @ 2008-08-15 10:29 LukeW 阅读(146) | 评论 (0)编辑 收藏

Fade in and out images in MIDP 2.0

This tip describes how to change the alpha value of an image to make it appear blended. There's also an example MIDlet with source code.

In MIDP 2.0 there's a new method in the Image class, getRGB(...) that will get all Alpha, Red, Green, Blue (ARGB) values from the image to an int array. We can use this method, and the resulting array, to change the alpha value of the image.

Integers in J2ME are four bytes, each pixel in the image is described by the ARGB values where each color is one byte 0 to 255. If the alpha value is 0, the corresponding pixel will be transparent, if the alpha is 255, the pixel will be opaque.

To get a specific color from the int array, it's possible to use the AND '&' operator and to accomplish the blending effect we need to get the colors, without the alpha value, from the int array.
FF = 11111111 = 255
0xFFFFFFFF - Alpha = 255, Red =255 Green = 255, Blue = 255
(
0xFFFFFFFF & 0x00FFFFFF= 0x00FFFFFF


By doing this we'll only get the RGB colors from the int array, alpha equals zero.

Now when we just have the RGB colors and alpha equals zero, we can just add our new alpha value.

If we have the alpha value 255 and want to add this to our color, we need to use the shift left operator.

To change:
(00000000 00000000 00000000 11111111) to 
(
11111111 00000000 00000000 00000000)
use the shift left 
'<<' operator.
(
0xFF << 24= 0xFF000000.


With this knowledge we now can change the alpha value or do masking for specific colors.

  • Use the getRGB(...) method in the image class to get all the ARGB values from the image to a int array.
  • Use the blend function below to change the alpha value for each value in the array.
  • Use the createRGBImage(...) method in the image class to create a new image from the edited int array.

There are examples on how to use the getRGB and createRGBImage methods in the MIDlet below.
public static void blend(int[] raw, int alphaValue){
    
int len = raw.length;
    
// Start loop through all the pixels in the image.
    for(int i=0; i<len; i++){
        
int a = 0;
        
int color = (raw[i] & 0x00FFFFFF); // get the color of the pixel.
        a = alphaValue;     // set the alpha value we want to use 0-255.
 
        a 
= (a<<24);    // left shift the alpha value 24 bits.
        
// if color = 00000000 11111111 11111111 00000000 (0xFFFF00 = Yellow)
        
// and alpha= 01111111 00000000 00000000 00000000
        
// then c+a = 01111111 11111111 11111111 00000000
        
// and the pixel will be blended.
        color += a;
        raw[i] 
= color;
    }
}

example code

posted @ 2008-08-15 10:18 LukeW 阅读(192) | 评论 (0)编辑 收藏

Fast stream reading in Java

To increase the performance of your Java™ application when reading from an InputStream, there are a few key areas to look into. If possible, don't make any reallocations of memory. Allocate the input buffer once. Let the Java™ VM do the bulk of the reading, i.e. read data in big chunks. Some coding examples show a loop that reads data a small amount at a time. Using a too small buffer is inefficient. A much better technique is to use a relatively large buffer.

If you know the maximum content length, the most optimal read would be a single line, like this:
len = instream.read( buf, 0, MAX_BUFFER_SIZE );

In this case we assume that the buffer has already been allocated with a size set to MAX_BUFFER_SIZE.

If the content is of varying size, we have to make a tradeoff between performance and memory usage. If you keep your buffer size just above the average content length, then the number of reallocations of the data buffer and the number of reads will be kept at a minimum. There is no defined size of what a large buffer is but a good rule of thumb might be to keep the buffer size at most about 0,5 to 1 MB.

posted @ 2008-08-15 10:11 LukeW 阅读(140) | 评论 (0)编辑 收藏