博文
Windows下安装OpenSSL及其使用(2009-09-19 16:56:00)
摘要:1. 下载最新版本的Perl,安装后重启系统。
http://downloads.activestate.com/ActivePerl/Windows/5.8/ActivePerl-5.8.8.822-MSWin32-x86-280952.zip
2.下载 latest openssl并解压到C:\openssl-0.9.8k。
http://www.openssl.org/source/
参考openssl目录下的install.win32说明进行安装:
1、进入解压目录。
>cd C:\openssl-0.9.8k
2、运行Configure。
>perl Configure VC-WIN32
如不成功会有明显提示。
3、创建Makefile文件。
>ms\do_ms
推荐使用这种方式,另外两种方式 如果使用也必须保证本机有编译器才能使用。
:ms\do_masm (默认vc 自带的编译器;也也以自己下载安装)
:ms\do_nasm (需要自己下载)
4、配置VC环境变量。
>cd C:\Program Files\Microsoft Visual Studio\VC98\Bin
>vcvars32.bat
5、编译动态链接库。
>cd C:\openssl-0.9.8k
>nmake -f ms\ntdll.mak
如果编译成功,最后的输出都在out32dll目录下:包括可执行文件、两个dll(ssleay32.lib, libeay32.lib)和两个lib文件(ssleay32.dll, libeay32.dll)。
6、为VC添加头文件和静态链接库路径。
ToolsàOptionsàDirectores,在Include files中增加C:\openssl-0.9.8k \inc32目录;在Libray files中增加C:\openssl-0.9.8k\out32dll。
7、编写OpenSSL程序,可参考C:\openssl-0.9.8k\demos
(1)包含相应头文件
#include <openssl/***.h>
(2) 添加静态链接库
#pragma comment(lib, "libeay32.lib"......
按钮添加ToolTip(2009-08-19 21:25:00)
摘要:一.首先创建基于对话框的MFC工程TipBtn,添加成员变量UINT m_nBtnID和CRect m_rcBtn,拖放按钮IDC_BUTTON_ADD到对话框中。本例程动态创建含有提示的按钮。
二. 为对话框添加ToolTip支持,在CTipBtnDlg的头文件中定义CtoolTipCtrl型控件变量
m_tooltip。
// TipBtnDlg.h
class CTipBtnDlg : public Cdialog
{
// ……
// Implementation
public:
UINT m_nBtnID = WM_USER + 101; // 按钮ID
CRect m_rcBtn = CRect(10, 40, 10 + 40, 40 + 30); // 按钮的大小和位置
protected:
CToolTipCtrl m_tooltip;
// ……
}
三.在CTipBtnDlg的OnInitDialog函数中创建ToolTipCtrl并激活。
(1)BOOL CToolTipCtrl::Create( CWnd* pParentWnd, DWORD dwStyle = 0 );
参数:
pParentWnd
指定工具提示控件的父窗口,通常是一个CDialog。它不能是NULL。
dwStyle
指定工具提示控件的风格,可以是任意Windows风格的组合。
若|TTS_ALWAYSTIP则不管工具栏所属窗口是否被激活,提示都会出现。
(2)void CToolTipCtrl::Activate( BOOL bActivate );
若bActivate=TRUE,即工具提示控件被激活,当光标路过一个向提示控件注册过的工具上时就会显示提示信息;
若bActivate=FALSE,即工具提示控件未被激活,则不会显示工具提示信息,即使是光标路过一个工具上。
// TipBtnDlg.cpp
BOOL CTipBtnDlg::OnInitDialog()
{
// ……
m_tooltip.Create(this); // 创建Tool......
自定义应用层通信协议(2)(2009-07-11 22:33:00)
摘要:4.数据包TLV的设计
从应用层HTTP协议,到超文本置标语言HTML(HyperText Mark-up Language),再到可扩展置标语言XML(Extensible Markup Language),它们提供了数据的格式化存储、传输和格式化显示的规范,是网络通信的基石。然而HTTP协议以及HTML/XML置标语言的本质就是定义一堆标签(Tag)对数据进行串行化序列化,然后接收方再根据标签解析、还原数据。
自定义通信协议的关键是对数据包的合理构造(construct)和正确解析(parse),即制定编解码规则。
抽象语法标记ASN(Abstract Syntax Notation) BER的长度确定的编码方式,由3部分组成Identifier octets、Length octets和Contents octets,实际上这就是一中TLV(Type-Length-Value)模型:类型字段(Type或Tag)是关于标签和编码格式的信息;长度字段(Length)定义数值的长度; 内容字段(Value)表示实际的数值。
因此,一个编码值又称TLV三元组。编码可以是基本型或结构型,如果它表示一个简单类型的、完整的显式值,那么编码就是基本型(primitive);如果它表示的值具有嵌套结构,那么编码就是结构型 (constructed)。
TLV编码就是指对Type(Tag)、Length和Value进行编码,形成比特流数据包;解码是编码的逆过程,是从比特流缓冲区中解析还原出原始数据。
采用C++编程语言设计TLV协议类,其类视图如图5所示。
图5 CTLV类视图
目前只提供设置整形值(int型)的setValue_Int和设置字符串值(C_String型)的SetValue_Cstring两个接口。
TLV将数据封装成包的格式如表1所示。
表1 TLV包格式
TLV包
头部
包实体
m_dwTag
m_nLen
m_pValue
TLV的接口说明:
(1)值类型标签m_vtTag是内部辅助枚举变量,它根据构造TLV时传递的服务类型标签m_dwTag来确定。
(2)TLV::m_nLen在为TLV设置具体值时确定。
(3)TLV包的封装:
1)使用Tag......
自定义应用层通信协议(1)(2009-07-11 22:15:00)
摘要:1.通信协议的概念及其要素
在OSI开放互联参考模型中,对等实体之间数据单元在发送方逐层封装,在接收方的逐层解析。发送方N层实体从N+1层实体得到的数据包称为服务数据单元(Service Data Unit,SDU)。N层实体只将其视为需要本实体提供服务的数据,将服务数据单元进行封装,使其成为一个对方能够理解的数据单元(Protocol Data Unit,PDU),封装过程实际上是为SDU增加对等实体间约定的控制信息(Protocol Control Information,PCI)的过程。
为了保证网络的各个功能的相对独立性,以及便于实现和维护,通常将协议划分为多个子协议,并且让这些协议保持一种层次结构,子协议的集合通常称为协议簇。
网络协议的分层有利于将复杂的问题分解成多个简单的问题,从而分而治之。各层的协议由各层的实体实现,通信双方对等层中完成相同协议功能的实体称为对等实体。对等实体按协议进行通信,所以协议反映的是对等层的对等实体之间的一种横向关系,严格地说,协议是对等实体共同遵守的规则和约定的集合。
通信协议精确地定义了双方通信控制信息和解释信息:发送方能将特定信息(文本、图片、音频、视频)按协议封装成指定格式的数据包,最终以串行化比特流在网络上传输;接收方接收到数据包后,根据协议将比特流解析为本地化数据,从而获取对方发送过来的原始信息。
通信协议包括三个要素:
(1)语法:规定了信息的结构和格式;
(2)语义:表明信息要表达的内容;
(3)同步:规则涉及双方的交互关系和事件顺序。
整个计算机网络的实现体现为协议的实现,TCP/IP协议是Internet互联网的核心协议。
2.通信协议开发步骤
(1) 协议的开发主要包括协议设计、协议形式描述、协议实现和协议一致性测试。协议的开发过程与步骤如图1所示。
图1 协议开发过程与步骤
(2) 协议设计过程中的分组发送接收模型如图2所示。
图2协议设计过程中的分组发送接收模型
(3)协议的一致性测试
协议的一致性测试是指测试协议能否按照预想的控制策略实现正确的通信,主要体现在数据包通过信道从信源传送到信宿后,信宿能够根据协议正确的解析出原始信息。
协议的一致性测试如图3所示。
图3 协议一致性测试环境
根据测试环境的可以分为......
Winsock五种I/O模型的性能分析(2009-07-11 21:45:00)
摘要:五种I/O模型的性能分析
重叠I/O模型的另外几个优点在于,微软针对重叠I/O模型提供了一些特有的扩展函数。当使用重叠I/O模型时,可以选择使用不同的完成通知方式。
采用事件对象通知的重叠I/O模型是不可伸缩的,因为针对发出WSAWaitForMultipleEvents调用的每个线程,该I/O模型一次最多都只能支持6 4个套接字。假如想让这个模型同时管理不止64个套接字,必须创建额外的工作者线程,以便等待更多的事件对象。因为操作系统同时能够处理的事件对象是有限的,所以基于事件对象的I/O模型不具备伸缩性。
使用完成例程通知的重叠I/O模型,因为以下几个原因,也不是开发高性能服务器的最佳选择。首先,许多扩展功能不允许使用APC(Asyncroneus Procedure Call,异步过程调用)完成通知。其次,由于APC在系统内部特有的处理机制,应用程序线程可能无限等待而得不到完成通知。当一个线程处于“可警告状态”时,所有挂起的APC按照先进先出的顺序(FIFO)接受处理。现在考虑这样一种情况,服务器已经建立起了一个连接,并且调用含有完成例程指针的WSARecv投递了一个重叠I/O请求。当有数据到达时(即I/O完成时),完成例程执行并且再次调用WSARecv抛出另外一个重叠I/O请求。一个APC抛出的I/O操作需要一定的时间才能完成,所以这期间可能另外一个完成例程等待执行(比如本次WSARecv还没接收完时,又有一个新的客户接入并发来数据),因为还有更多的数据需要读取(上一个客户发来的数据尚未读完)。只要(投递WSARecv的)那个套接字上还有“未决”(未接收完)的数据,就会导致调用线程长久阻塞。
基于完成端口通知的重叠I/O模型是Windows NT系统提供的一个真正支持高伸缩性的I/O模型。在上一章中,探讨了Winsock几种常见的I/O模型,并且说明了当应对大规模客户连接时,完成端口是最佳的选择,因为它提供了最好的伸缩性。
对不同Winsock I/O模型的性能测试结果如图1所示。其中服务器采用Pentium 4 1.7 GHz Xeon的CPU,768M内存;客户端有3台PC,配置分别是Pentium 2 233MHz ,128 MB 内存,Pentium 2 350 MHz ,128 MB内存,Itanium 733 MHz ,1 GB内存。服务......
Winsock服务器设计的四个关键问题(2009-07-11 20:07:00)
摘要:6.2.1 接受连接的方法
Winsock扩展函数AcceptEx是唯一能够使用重叠I/O接受客户连接的函数。下面主要深入探讨使用该函数接收连接的问题。
前面已经讨论过,当客户连接进来时,服务器需要创建一个套接字来负责维护与一个客户端的会话。使用AcceptEx函数之前必须创建一些套接字,并且这些套接字必须是未绑定、未连接的,即使它们可能在调用TransmitFile, TransmitPackets, 或DisconnectEx后可以重用。
响应服务器必须总是具有足够的AcceptEx在站岗,以便在有客户连接请求时调用。但是,并没有具体的数量能够保证服务器能够立即响应连接。我们知道在调用listen将监听套接字置于监听状态后,TCP/IP堆栈会自动接受到来的连接,直到达到listen的backlog参数设定的限制。对于Windows NT服务器而言,支持的backlog的最大值为200。如果服务器投递了15个AcceptEx调用,然后突然有50个客户请求连接服务器,它们的连接请求都不会遭到拒绝。服务器投递的AcceptEx I/O会满足前面的15个连接,剩下的35个连接都被系统默认连接了。检查一下backlog的值发现,系统还有能力默认接受165个连接。之后,如果服务器投递AcceptEx调用,它们会立即成功返回,因为系统会将默认接收的连接放入“等待连接队列”中。
服务器的特性是决定要投递多少个AcceptEx操作的重要因素。例如,希望处理大量短时间即时连接的客户要比处理少量长时间连接的客户投递更多的AcceptEx I/O。一个好的策略是允许AcceptEx的调用数量在最小值和最大值之间变化。具体做法是,应用程序跟踪未决的AcceptEx I/O的数量,当一个或多个I/O完成使这个未决I/O数量变得比最小值还小时,就再投递额外的AcceptEx I/O。
在Windows 2000和以后的Windows操作系统版本中,Winsock提供了一种机制,用来确定应用程序是否投递了足够的AcceptEx调用。创建监听套接字时,使用WSAEventSelect函数为监听套接字关联一个事件对象,注册FD_ACCEPT事件。如果投递的AcceptEx操作用完,但是仍有客户请求接入(系统根据backlog值决定是否接受这些连接),事件对象就是受信,说明应该投递额外的A......
TCP三次握手/四次挥手(2009-06-15 18:50:00)
摘要:
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接,如图1所示。
(1)第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。
(2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。
(3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
图1 TCP三次握手建立连接
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。
(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。
TCP采用四次挥手关闭连接如图2所示。
 ......
TCP/IP数据报格式(2009-06-15 18:38:00)
摘要:IP数据报格式
图1.IP数据报格式
版本字段长度为4,用来表明建立数据报的IP版本,目前的IP版本是IPv4,IPv6正在发展中。IPv4的字段为0100 。
首部长度(报头长度)指的是首部占32 bit字的数目,包括任何选项。由于它是一个4比特字段,因此首部最长为60个字节。15x32/8=60字节.IP首部始终是32 bit的整数倍.IP数据报报头的最小长度为20个字(不含填充字段和IP选项字段的IP报头是最常见的IP报头,为20个字节)
服务类型TOS(Type Of Service)总长度字段是指整个I P数据报的长度,以字节为单位.由于该字段长16比特,所以I P数据报最长可达6 5 5 3 5字节(64KB).总长度字段是I P首部中必要的内容。数据长度=总长-报头长度。
标识符长16比特。
标志位长度为3比特,用于分段控制:第0位为预留位,第1位表示可否分段。当该位的值为0时,表示数据报不可分段,值为1时,表示数据报可被分段。第2位为段是否结束位,当该位的值为0时,表示该段是原数据报的最后一段,值为1时,表示后面不有更多的分段。
当网络设备要发送的数据报长度比所在网络的最大传输单元(MTU,Max Transfer United)大,并且标志位的第1位设置为不能分段(0)时,网络设备会向发送方返回一个因特网控制消息协议ICMP错误消息,并丢弃该数据报。除了最后一个分段外,其余分段的第2位均设置为1。
段偏移13比特长度,用于指定分段在原始数据报中的位置,以8个字节为单位.
生存时间TTL长度为8比特,用于指定数据报允许保留在网络上的时间。
协议字段长度为8比特,用于指定数据报数据区中携带的消息是由哪种高级协议建立的。ICMP为1,TCP为6,UDP为17。 协议号分配RFC790.
报头校验和16比特,仅用于IP报头校验和。
源IP地址及目的IP地址。
选项,填充字段用于确保将选项字段填充为最少32个比特位,以保证IP报头以32位结束。
分段:分段是将一个大的IP数据报分解成几个较小的数据报段的过程。当IP模块需要通过一个具有较小MTU的网络传送较大的数据包时,就必须将其分段。
//定义ip报头数据结构
typedef s......
基于完成端口的Winsock程序设计(2009-06-15 12:53:00)
摘要:关于完成端口的概念及内部机制,参考译文《深度探索I/O完成端口》。
完成端口对象取代了WSAAsyncSelect中的消息驱动和WSAEventSelect中的事件对象,当然完成端口模型的内部机制要比WSAAsyncSelect和WSAEventSelect模型复杂得多。
IOCP内部机制如下图所示:
在Winsock中编写完成端口程序,首先要调用CreateIoCompletionPort函数创建完成端口。其原型如下:
WINBASEAPI HANDLE WINAPI
CreateIoCompletionPort(
HANDLE FileHandle,
HANDLE ExistingCompletionPort,
DWORD CompletionKey,
DWORD NumberOfConcurrentThreads );
第一次调用此函数创建一个完成端口时,通常只关注NumberOfConcurrentThreads,它定义了在完成端口上同时允许执行的线程数量。一般设为0,表示系统内安装了多少个处理器,便允许同时运行多少个线程为完成端口提供服务。每个处理器各自负责一个线程的运行,避免了过于频繁的线程上下文切换。
hCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0)
这个类比重叠I/O事件通知模型中(WSA)CreateEvent。
然后再调用GetSystemInfo(&SystemInfo);取得系统安装的处理器的个数SystemInfo.dwNumberOfProcessors,根据CPU数创建线程池,在完成端口上,为已完成的I/O请求提供服务。一般线程池的规模,即线程数 = CPU数 * 2 + 2。
下面的代码片段演示了线程池的创建。
// 创建线程池,规模为CPU数的两倍
for(int i = 0......
深度探索I/O完成端口(2009-06-15 09:00:00)
摘要:
引言
要想编写一个高性能的服务器应用程序,必须实现一个高效的线程模型。让太少或者太多的服务器线程来处理客户的请求,都可能导致性能问题。例如,如果一个服务器创建单个线程来处理所有的请求,那么客户端可能长期等待而得不到响应,因为服务器同一时刻只能忙于处理一个请求。当然单个线程也能并发处理多个请求,当I/O操作被启动时,它可以从一个请求切换到另一个请求,但是这种结构相当复杂,并且不能充分利用多处理器的优势。在另一个极端,服务器可以创建一个大规模的线程池,这样几乎每一个客户请求都可以由一个专门的线程来处理。这种情形通常会导致线程频繁切换:大量线程被唤醒,执行CPU处理,阻塞等待I/O,然后在请求完成之后又一次阻塞以等待新的请求。如果没有别的情况,太多的线程将导致过多的上下文切换,因为调度程序不得不将处理器时间在多个活动线程之间分割。
服务器的目标是使线程避免不必要的阻塞,尽量减少上下文切换。同时,还要使用多线程来发挥最大限度的并行。理想的情况是在每一个处理器上运行一个线程来处理一个客户请求,当处理器上的活动线程完成一个请求时,如果还有其他的请求正在等待,则不阻塞。为了使这一优化处理可以有效的进行,应用程序必须有一种可行的方法,使得一个正在处理客户请求的线程在I/O上阻塞时(例如它在处理过程中需要读取一个文件时)另外一个等待线程被激活。
Windows NT 3.5引进了一系列API使得这个目标的实现变得相对容易。这些API主要聚焦在一个叫完成端口的对象上。在本文中,首先我将讲解完成端口的使用,然后再深入其内部,向你展示Windows NT中完成端口的实现机制。
使用I/O完成端口
应用程序将IoCompletion执行体对象当作与多个文件句柄相关的I/O完成的核心。一旦一个文件与一个完成端口相关联,任何在此文件上异步I/O操作的完成都会导致一个完成通知包(completion notification packet)加入到完成端口队列。一个线程只需简单的等待一个完成通知包被排队到此完成端口上,就可以等待在多个文件上的所有正在进行之中的I/O操作的完成事件。Windows API中的WaitForMultipleObjects 提供了类似的功能,但完成端口的优点在于在系统的协助下发挥高效的并发性。这里的并发性可以理解为应用程序主动处理客户请求的线......