十一 192012
 

最近做一个小东西,涉及到Flex中的WebService传输。完全使用Flex框架太过臃肿,想试一下能不能直接解析AMF。

AMF是Action Message Format协议的简称,AMF协议是Adobe公司自己的协议,主要用于数据交互和远程过程调用,在功能上相当于WebService,但是AMF与WebService中的XML不同的是AMF是二进制数据,而XML是文本数据,AMF的传输效率比XML高。AMF使用HTTP方式传输,目前主要是用于ActionScript中,即实现Flex和Server之间的通信。
AMF目前有两种版本,AMF0和AMF3,他们在数据类型的定义上有细微不同。关于AMF的官方文档参见这里

Type
Byte code
Notes

Number
0×00

Boolean
0×01

String
0×02

Object
0×03

MovieClip
0×04
Not available in Remoting

Null
0×05

Undefined
0×06

Reference
0×07

MixedArray
0×08

EndOfObject
0×09
See Object

Array
0x0a

Date
0x0b

LongString
0x0c

Unsupported
0x0d

Recordset
0x0e
Remoting, server-to-client only

XML
0x0f

TypedObject (Class instance)
0×10

AMF3 data
0×11
Sent by Flash player 9+

对应的枚举就是

public enum DataType
{
   Number = 0,
   Boolean = 1,
   String = 2,
   UntypedObject = 3,
   MovieClip = 4,
   Null = 5,
   Undefined = 6,
   ReferencedObject = 7,
   MixedArray = 8,
   End = 9,
   Array = 10,//0x0A
   Date = 11,//0x0B
   LongString = 12,//0x0C
   TypeAsObject = 13,//0x0D
   Recordset = 14,//0x0E
   Xml = 15,//0x0F
   TypedObject = 16,//0×10
   AMF3data=17//0×11
}

以上表列出了每种数据类型的表示方法,这样看并不容易理解,下面我就主要讲解一下常用的一些格式:
0.Number这里指的是double类型,数据用8字节表示,比如十六进制00 40 10 00 00 00 00 00 00就表示的是一个double数4.0,在C#中可以使用如下代码读取该数据:

byte[] d=new byte[]{0,0,0,0,0,0,0×10,0×40};//这里的顺序是和amf文件中的顺序正好相反,不要忘记了
double num=BitConverter.ToDouble(d,0);

1.Boolean对应的是.net中的bool类型,数据使用1字节表示,和C语言差不多,使用00表示false,使用01表示true。比如十六进制01 01就表示true。
2.String相当于.net中的string类型,String所占用的空间有1个类型标识字节和2个表示字符串UTF8长度的字节加上字符串UTF8格式的内容组成。比如十六进制03 00 08 73 68 61 6E 67 67 75 61表示的就是字符串,该字符串长8字节,字符串内容为73 68 61 6E 67 67 75 61,对应的就是“shanggua”。在C#中要读取字符串则使用:

byte[] buffer=new byte[]{0×73,0×68,0×61,0x6E,0×67,0×67,0×75,0×61};//03 00 08 73 68 61 6E 67 67 75 61
string str=System.Text.Encoding.UTF8.GetString(buffer);

3.Object在.net中对应的就是Hashtable,内容由UTF8字符串作为Key,其他AMF类型作为Value,该对象由3个字节:00 00 09来表示结束。C#中读取该对象使用如下方法:

private Hashtable ReadUntypedObject()
      {
         Hashtable hash = new Hashtable();
         string key = ReadShortString();
         for (byte type = ReadByte(); type != 9; type = ReadByte())
         {
            hash.Add(key, ReadData(type));
            key = ReadShortString();
         }
         return hash;
      }

4.Null就是空对象,该对象只占用一个字节,那就是Null对象标识0×05。
5. Undefined 也是只占用一个字节0×06。
6.MixedArray相当于Hashtable,与3不同的是该对象定义了Hashtable的大小。读取该对象的C#代码是:

private Hashtable ReadDictionary()
      {
         int size = ReadInt32();
         Hashtable hash = new Hashtable(size);
         string key = ReadShortString();
         for (byte type = ReadByte(); type != 9; type = ReadByte())
         {
            object value = ReadData(type);
            hash.Add(key, value);
            key = ReadShortString();
         }
         return hash;
      }

7.Array对应的就是.net中的ArrayList对象,该对象首先使用32位整数定义了ArralyList的长度,然后是密集的跟着ArrayList中的对象,读取该对象使用如下函数:

private ArrayList ReadArray()
      {
         int size = ReadInt32();
         ArrayList arr = new ArrayList(size);
         for (int i = 0; i < size; ++i)
         {
            arr.Add(ReadData(ReadByte()));
         }
         return arr;
      }

8.Date对应.net中的DateTime数据类型,Date在类型标识符0x0B后使用double来表示从1970/1/1到表示的时间所经过的毫秒数,然后再跟一个ushort的16位无符号整数表示时区。读取Date类型的C#代码为:  

private DateTime ReadDate()
     {
        double ms = ReadDouble();
        DateTime BaseDate = new DateTime(1970, 1, 1);
        DateTime date = BaseDate.AddMilliseconds(ms);       
        ReadUInt16(); //get’s the timezone       
        return date;
     }

9.LongString对应的也是string类型,不过和2对应的String不同的是这里使用32位整数来表示字符串的UTF8长度,而String使用的是16位。
10.XML是使用类型标识符0x0F后直接跟LongString类型的字符串表示。
这里大部分代码我都是摘自AMF.net 一个开源的.net AMF序列化和反序列化的库,大家若有兴趣可以到http://sourceforge.net/project/showfiles.php?group_id=159742 去下载。另外http://osflash.org/documentation/amf/astypes 这个英文网站也对AMF数据类型作了比较详细的介绍。

AMF文件总体来说分为4部分:前言(Preamble)、AMF头、AMF主体和主体的响应。
前言的前2字节用于说明AMF的版本,目前AMF有2个版本AMF0和AMF3.如使用AMF0则是:00 00
第3和第4字节用16位整数表示AMF头的数量。
每一个AMF头是由以下四部分组成:

引用

UTF string 表示Header的名字
Boolean 表示该Header是否是必须的
Int32表示Header的长度,但是好像很多情况下该值为FF FF FF FF,似乎这个字段没有意义。
Variable变量是某种AMF数据类型。

在Header表示完后,接下来是一个16位的整数用来表示AMF主体的数量,在这个数量之后才是AMF主体。
AMF主体主要由以下四部分组成:

引用

UTF String – Response表示请求的类和方法或响应的结果。
UTF String – Target是一个标识,其作用就是为了实现请求和响应的对应,通过Target找到该响应对应的请求。一般使用自增整数。
Int32- 表示主体的长度,该字段一般没有什么用
Variable变量表示主体的数据。

主体响应是客户端向服务器发送一个AMF请求以后服务器做出的和请求的主体格式相同的AMF响应,但是主体响应中的内容有所不同:
Response: 被设置为字符串‘null’.
Target: 是请求的Target值再加上“/onStatus”, “onResult”, 或者 “/onDebugEvents”组成. “/onStatus” 是为运行时错误而准备的我们一般不关心这个. “/onResult” 表示该请求被正确调用. “/onDebugEvents” 是在调试时使用的,这里也不用关心. 如果请求的Target是‘/1’, 那么被成功调用以后的主体响应应该是: ‘/1/onResult’ 。
Data:就是响应后返回的AMF对象。
说了这么多估计还是感觉比较抽象,下面给出个实例:
AMF 16进制内容

00000000h: 00 00 00 00 00 01 00 1B 7A 68 2E 66 6C 65 65 74 ; ……..zh.fleet
00000010h: 53 65 72 76 69 63 65 2E 67 65 74 46 6C 65 65 74 ; Service.getFleet
00000020h: 52 6F 77 00 03 2F 37 39 00 00 00 13 0A 00 00 00 ; Row../79……..
00000030h: 03 02 00 01 35 02 00 03 38 34 35 02 00 01 35      ; ….5…845…5

以上是客户端向服务器发送的一个AMF请求。我们可以按照前面说的封装方式将该amf解析如下:
00 00(AMF0版本)00 00(Header个数为0)00 01(AMF主体有1个)
00 1B(请求的方法的字符串长度为27个字节)
7A ……77(这27个直接就是调用的类和方法:“zh.fleetService.getFleetRow”)
00 03(请求的Target字符串长3字节) 2F 37 39(Target的内容:“/79”)
00 00 00 13(主体的长度为19)
0A(传入的变量是一个Array)00 00 00 03(该Array的长度为3)02 00 01 35(Array的第一个值是字符串“5”)02 00 03 38 34 35(Array的第二个值是字符串“845”)02 00 01 35(Array的第三个值是字符串“5”)
现在整个AMF对象都解析出来了,我们可以认为是客户端调用了服务器的方法:zh.fleetService.getFleetRow(“5″, “845″, “5″)
服务器返回的AMF文件的内容的解析方式相同,这里我就不再重复了。
现在我们已经对AMF文件有了一个清晰的认识了。那么接下来就是要抓包,看某些在Flex上的操作对应的发送了什么AMF文件,服务器返回了什么AMF文件。将这些AMF文件解析出来然后就可以看到调用了API了。

262012
 

原创文章,转载请注明来自Sean的技术博客

经过之前的需求分析,整个软件的架构基本可以确定下来。考虑到用户对于存储方式的需求可能再次发生变化,在实现过程中,把整个程序分成若干模块,主程序仅提供互操作的接口。一旦需求变动,只需改动部分模块或者增删部分模块,而不必影响其他部分的代码。使得各个模块间的耦合度降低。

image

整个程序的流程图如上图,需要被加载的模块信息存储在一个配置文件中,每次执行数据抓取和存储之前,先通过配置文件获得要加载的模块。这些模块包括:

  1. mod_utils:通过基本库实现日志功能,网址生成,邮件发送,单位转换和配置文件读取。
  2. mod_rawdata:定义了数据获取,处理和存储的接口。由于本次需求对抓取的来源非常的单一,所以在内部实现了获取和处理两个接口,解析得到的JSON数据,过滤出有价值的信息,并把它们处理成便于存储的格式放在内存中。同时留出存储接口供下面的模块使用。
  3. mod_text:将内存中的数据以txt文件的形式存储。
  4. mod_excel:将内存中的数据以xls文件的形式存储。
  5. mod_sqlite:将内存中的数据存储到SQLite数据库中。

配置文件包括:

  1. zone.xml:储存区域的信息,不同的区域对应的URL是不同的,mod_utils中提供一个函数,将每个Zone映射到一个URL上。
  2. config.ini:里面包括三个字段,module字段指出哪些存储方式将被使用;interval字段指定了在非单次执行模式中,两次抓取之间的时间间隔(秒);email字段指定一个或多个电子邮件地址,将在程序的log模块中记录到异常时候发送邮件通知。

全局的模块就是Log模块,它在程序启动的第一时间加载,并全程记录信息抓取、数据存储的过程。如果过程顺利,则每次仅简单的指出Success信息;如果出错,将按照调用栈反向输出错误信息,便于分析问题的出处。

目录结构如下:

捕获

252012
 

原创文章,转载请注明来自Sean的技术博客

去年的十一月份,结束了在某杀毒软件公司中国研发中心为期半年的实习后回到学校。论文开题已经结束,手头事情不太多。刚好有位国内名校的教授需要做一个从Web站点抓取每小时滚动的数据,并保存成Excel表格形式的文件这样一个程序。凑巧找到了我,于是就有了这么一个项目。

整个项目从需求分析,到软件架构设计,再到编码、测试以及最后的部署,都是自己一个人独立完成。也算是一个小小的锻炼,让我知道了为何对需求的理解是如此重要,因为这是客户唯一需要的东西……因为项目还在运行之中,所有有些设计项目具体内容的部分就只能隐去了。这一篇博文和后续的几篇用来讲一下项目中碰到的一些问题和解决思路。

需求概述

从某Web站点抓取数据,每小时一次。数据分两类,一类是“区域”的小时平均值(3个);第二类是“区域”中“点”的每小时取值(3个)。区域共有约15个,每个区域包含2个到10个不等的点,每个点提供3个数值。数据最终格式应为Excel表格,表格包含上述数据在一年内的所有值。

需求分析

  1. 从数据发布网站每小时抓取xxx数据,并以合适的形式保存。发布网站使用的发布方式为网页嵌入Silverlight控件展示,控件无法以HTML的分析方式得到数据,必须进行抓包,如果数据包被加密,则还需要反编译控件以破解其加密算法,从而解密数据。幸而该网站使用的是明文传输,HTTP GET形式向数据服务器发起请求,返回UTF-8编码的明文字符串。因此,抓取的重点在于研究其GET命令的形式和返回字符串的结构。
  2. 使用WireShark抓包分析,忽略TCP连接的三次握手,由第一次HTTP GET请求开始追踪每一个TCP segment of a reassembled PDU。得到了:
    • 区域均值的GET请求字符串,及相应的返回值;
    • 每个点数据的GET请求字符串及相应的返回值;
    • 返回值的格式是UTF-8文本,组织方式为JSON,除了所需的三个值之外还有其他附加数据。
  3. 存储方式:根据上面的需求,使用数据库存储无疑是性能和伸缩性最佳的,然而与客户要求的Excel文件有出入。再考虑使用Plain Text存储,通过文件系统中的树形目录结构区分区域、点、日期、时间等,因为源数据格式就是普通文本,所以这种方式的优点是可以最大限度的保留原始信息,但是显然不利于数据的检索和使用等,只能是作为备份手段使用。第三种方法是直接按需求的格式保存为Excel文件,其优点不必多言,缺点却也非常明显,性能实在是低下。因此,本系统考虑用数据库存储信息,再通过额外的附加程序将数据库生成目标xls格式,同时通过txt进行数据的备份,以防原始信息的丢失。

经过上面的分析,就可以开始设计整个程序的架构了。在实际开发过程中,并非自顶向下的进行设计的,而是先建立了多个快速的程序原型,分步实现数据的①下载②分析③有效信息提取④存储为文本。基于这些小的程序片段最终合并为完整的程序,然后重构和优化和测试。

整个程序使用Python开发,用到了xlrd, xlwt和xlutils三个第三方库。

242012
 

问题:

最近在教研室上网经常遇到一个问题,IPv6的连接经常不知在什么时候就自己断开了,基于IPv6的一切程序瞬间就不能正常工作。

查看网络设置,发现地址里面多了一条2002开头的IP地址,同时又有Native IPv6分配过来的2001的IP。这很蹊跷,因为这个2002的地址也不是一直都有的,而是时有时无。随着它的出现,IPv6的连接也是时通时断。这很令人烦恼。

晚上终于忍受不了了,到网上查查吧。果然万能的网络、万能的先驱者们已经发现了这中情况并且分析了其中缘由了。很好,顺势跟着看看

解决:

这位博主分析的很透彻,从RFC里面查了这个2002段的用处,原来是6to4隧道使用的IP。另外就是使用
netsh interface ipv6 show prefixpolicies 命令发现的ipv6使用IP段的优先级:
捕获

这个优先级是我使用 netsh interface ipv6 set prefixpolicy 2001::/32 35 1 persistent 命令修改过的了,原本的顺序里面2001::/32的优先顺序只有5,所有当有2002存在的时候就被覆盖了。

原因:

这位博主也分析了蹊跷的2002段的来源,是某个用户的广播:

原来在同一子网内有人在不断广播路由,我的电脑自动使用这个路由,加上学校提供的2001路由和地址,我的电脑有了两条路由路径和两个ipv6地址。

我把之前获取到的IP:2002:xxxx:xxxx:…中的第二和第三部分的数字换算出十进制的IP,发现是学校PPPOE拨号后分配给实验楼的某个IP,联想到有同学反应掉线的情况只发生在实验室,可以判断这个广播路由信息的电脑就在我们实验室里面,然后此时实验室恰好只有我和一位师弟在,看来就是他的电脑的问题了。

去问了一下师弟的IP,刚好就是上面查到的这个地址。

起初怀疑是他中了什么病毒之类的东西,但是心中着实怀疑现在写IPv6的这种病毒有没有什么好处可以赚。细问一下,果然跟病毒没有关系,是他开启了他校园网拨号连接的共享,也就是他是好意向大家共享他的IPv4连接,却是不料会有这种副作用。

212012
 

VPN的流量自己用不完,就想分给同学们用。又怕流量超了,起码得弄个流量的统计吧。老规矩,网上Google一下,果然已经有人做好了。本文的主要参考了这篇文章,首先对原作者表示感谢。

主机为CentOS 5.x,已经安装好了OpenVPN 2.1.1,MySQL 5.1.60,还缺少关键部件libpam-mysql。

1. 先来安装它:

wget http://prdownloads.sourceforge.net/pam-mysql/pam_mysql-0.7RC1.tar.gz
tar zxvf pam_mysql-0.7RC1.tar.gz
cd pam_mysql-0.7RC1
./configure && make && make install

2. 配置MySQL:

     先用MySQL管理员账号登陆数据库,添加用于VPN操作的账号和记录用的数据库、数据表。

mysql -uroot -p
-- 创建数据库
CREATE DATABASE openvpn;
 
-- 切换数据库
USE openvpn;
 
-- 创建用户,用户名openvpn,密码openvpn(可自行设定)
GRANT ALL ON openvpn.* TO 'openvpn'@'localhost' IDENTIFIED BY 'openvpn';
 
-- 创建用户数据表
CREATE TABLE IF NOT EXISTS `user` (
  `username` char(32) COLLATE utf8_unicode_ci NOT NULL,
  `password` char(128) COLLATE utf8_unicode_ci DEFAULT NULL,
  `active` int(10) NOT NULL DEFAULT '1',
  `creation` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `name` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
  `email` char(128) COLLATE utf8_unicode_ci DEFAULT NULL,
  `note` text COLLATE utf8_unicode_ci,
  `quota_cycle` int(10) NOT NULL DEFAULT '30',
  `quota_bytes` bigint(20) NOT NULL DEFAULT '10737418240',
  `enabled` int(10) NOT NULL DEFAULT '1',
  PRIMARY KEY (`username`),
  KEY `idx_active` (`active`),
  KEY `idx_enabled` (`enabled`)
) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
 
-- 创建日志数据表
CREATE TABLE IF NOT EXISTS `log` (
  `username` varchar(32) COLLATE utf8_unicode_ci NOT NULL,
  `start_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `end_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `trusted_ip` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `trusted_port` int(10) DEFAULT NULL,
  `protocol` varchar(16) COLLATE utf8_unicode_ci DEFAULT NULL,
  `remote_ip` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `remote_netmask` varchar(64) COLLATE utf8_unicode_ci DEFAULT NULL,
  `bytes_received` bigint(20) DEFAULT '0',
  `bytes_sent` bigint(20) DEFAULT '0',
  `status` int(10) NOT NULL DEFAULT '1',
  KEY `idx_username` (`username`),
  KEY `idx_start_time` (`start_time`),
  KEY `idx_end_time` (`end_time`)
) DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

3. 配置PAM for MySQL:

vim /etc/pam.d/openvpn

        进入输入模式,输入以下配置并保存。

Continue reading »

142012
 

原创文章,转载请注明来自Sean的技术博客

    自从买到了VPS,除了放置网站,就是玩一玩VPN了。早就听说过这种神奇的东西,用的还是不多,充满了好奇。放狗搜索了一下,发现最多的教程就是使用PPTPD搭建的,有的讲了配置,有的是用一键安装包。但是我的需求是一个能够支持IPv6的VPN,这样在校园网的环境下就可以免费的上网了,比较爽。以前一直用的是GoAgent来做代理,但是只是个普通的HTTP代理而已,还有单次返回数据500KB的限制,更是无法用任何的客户端。这些都使得这种方式并不能完全替代付费校园网IPv4的使用。

    后来发现大家表示OpenVPN是有支持IPv6的补丁包的,只要打好了补丁,就可以支持Server和Client之间的IPv6连接了。于是开始动手,以RedHat系列为例:

第一步,安装编译工具和OpenSSL。
      yum install gcc gcc-c++ kernel-devel openssl-devel

第二步,安装LZO压缩模块,后面OpenVPN用它来压缩。
      wget http://www.oberhumer.com/opensource/lzo/download/lzo-2.04.tar.gz
   tar解压,进入目录./configure && make && make install

第三步,下载OpenVPN并打上IPv6的Patch,安装之。
   wget http://openvpn.net/release/openvpn-2.1.1.tar.gz
   wget https://github.com/downloads/jjo/openvpn-ipv6/openvpn-2.1.1-ipv6-0.4.11.patch.gz
   tar解压第一个,gunzip第二个,然后 patch –p0 < openvpn-2.1.1-ipv6-0.4.11.patch
   进入openvpn-2.1.1,./configure && make && make install
Continue reading »

012011
 

极客观察:体验极差的中国互联网产品与现象

来源:http://www.geekpark.net/entity/view/97373

越来越多网民知道"用户体验"这个词,虽然他们可能并不完全了解用户体验所包含的内容,但至少,他们能告诉你,他们觉得怎样才好,怎样是不好的。

Web1.0时代不同的是,Web2.0时代网站百花齐放,用户不只有一个选择,相反,用户有很多选择。如果一个网站的用户体验做得非常差--除非用户必须使用这个网站--否则用户将会慢慢离开。

这期极客观察就抛弃以往只称赞不批评的风格,尝试分析中国互联网上用户体验极差的一些产品、服务和现象。

一、别这么繁琐行不行?

当我们的观察员在各个微博问他的关注者,"你认为中国互联网体验极差,但有时不得不用的5个网站/软件/手机应用是?",得到的回复里,几乎每个都包含了"支付宝"。甚至,有的人连续回复了5"支付宝"

支付宝是国内使用率最高的支付服务,它的用户体验之所以差,很大程度上是因为用户完成一次支付要花费相当大的成本。我们来模拟一次使用过程:

1、用户A想在淘宝上购买价值3000元的商品,他发现自己的帐号没钱,于是打电话让用户B从支付宝里给他转账3000元。

2、用户B在苹果电脑Mac系统下用Firefox浏览器打开支付宝,准备登录,却无法输入密码,提示要安装安全控件。这时,因为安全控件只有Safari浏览器的版本,他必须更换浏览器。

安全控件

3B关掉了Firefox,打开了Safari,重新打开支付宝网站,在登录框的地方点击下载了安全控件,安全控件提示他要重启浏览器,于是他重启了Safari

4B终于登录了支付宝,他找到了"我要付款"按钮,准备给A打款。 Continue reading »

242011
 

本来只是闲暇时候弄着玩的博客,并没有考虑过有什么大的成就,写给自己看罢了。后来看着"冰河"同学折腾的有声有色,觉得还是挺好的。前几日GoDaddy又一次推出一美元域名,顺势就拿下了这个心仪已久的域名了。

博客的服务器还是没换,一直用着以前那个免费主机空间BYET Host。这个空间还挺稳定的,也或许是我上线频率低的缘故,从来没碰到过服务器Down的情况。5632MB的磁盘空间,每月200GB的带宽,50个MySql数据库每个50MB,各种域名的支持,Linux + Apache 2.2.15 + PHP 5.2.13……对于免费主机来说也算挺好的了吧?唯一不爽的是经常说我长时间不登录需要激活,很是莫名其妙,不过登录一下就正常了。

以后可能比以前勤奋一下吧,经常来写一点手头上做的工作,转载些觉得有价值的文章什么的。但是依旧没有保证啊,手头的事情往往太琐碎了。

222011
 

一、首先是备份WordPresssql数据库,也就是导出Mysq数据库里的数据。

如果您的新域名还没有生效,这时你不能急于对WordPress搬家,用临时url不能正确安装WordPress
进入phpmyadmin后点击右侧出现的数据库名称,再点击export,然后依下图操作,第一步是点击select all,然后选择sql…

最后点GO导出。完成这些操作,你的电脑上会有以下两个项目:
1
个或多个数据库备份文件和你的WordPress下的所有文件。
Continue reading »

242010
 

最近做了一个串口通信的程序,PC到PC的通信,为之后的单片机和PC通信做点准备。很简单的小东西,就是实现了一下串口的通信连接、异步收发数据。虽然简单,还是遇到了些小问题,总结一下,供以后借鉴吧。

一、整体架构:

a) 全局变量

  1. 使用HANDLE m_hComm保存打开的端口句柄;
  2. BOOL m_bConnected来标示当前端口的状态,即是否已经连接成功;
  3. CWinThread* m_pThread来保存之后要开启的异步读取辅助线程;
  4. int nPort保存连接的端口号;
  5. 为了进行异步操作,还要给读取和写入分别定义一个OVERLAPPED结构变量,OVERLAPPED m_ovRead, m_ovWrite;
  6. 最后还要定义一个消息事件HANDLE m_hPostEvent,用于线程向窗体通知数据的到达;

b)函数概述:

  • BOOL ConfigPort();     //配置端口
  • BOOL ConnectToPort(int nPort);     //连接到端口
  • BOOL DisconnectToPort();     //从端口断开
  • DWORD WriteComm(char *buff, DWORD dwCount);     //向端口写入
  • DWORD ReadComm(char *buff, DWORD dwCount);     //从端口读出
  • UINT thCommProc(LPVOID pParam);      //全局函数,是一个线程的执行体

Continue reading »