<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

统计

  • 随笔 - 13
  • 文章 - 0
  • 评论 - 2
  • 引用 - 0

常用链接

留言簿

随笔分类

随笔档案

搜索

  •  

最新评论

阅读排行榜

评论排行榜

2011年8月9日

修改sqlserver2008 collation

http://msdn.microsoft.com/zh-cn/library/ms179254(v=SQL.100).aspx

posted @ 2011-08-09 12:44 @Koven.Z 阅读(514) | 评论 (0)编辑 收藏

2011年4月26日

修改win2k3 CD KEY

先打开注册表,在
HKEY_LOCAL_MACHINE\Software\Microsoft\WindowsNT\CurrentVersion\WPAEvents
    随便修改一个字,并保存退出regedit。
然后到c:\windows\system32\oobe下       运行msoobe   /a   打开激活程序。

选择电话,

更换密钥为

JCGMJ-TC669-KCBG7-HB8X2-FXG7M

JK6JC-P7P7H-4HRFC-3XM7P-G33HM


然后关掉窗口不要管那个什么四个步骤


update即可
然后重启。

posted @ 2011-04-26 14:11 @Koven.Z 阅读(282) | 评论 (0)编辑 收藏

2011年3月2日

Redhat AS4上安装telnet-server

默认情况下,Redhat AS4下只安装了telnet客户端,而没有安装telnet-server.
查询是否安装:

Bash代码 
  1. rpm -q telnet  
  2. rpm -q telnet-server  

 

安装telnet-server: 
1.c盘(hda0)是我保留的安装windows xp的分区。
登录到windows xp,把redhat as4的第四张安装光盘iso解压开,找到其中的telnet-server-0.17-31.EL4.3.i386.rpm,将其解压到c盘

2.进入linux后,

Bash代码 
  1. cd /mnt  
  2. mkdir cdisk  
  3. mount /dev/hda1 /mnt/cdisk  


将分区hda1(c盘)挂载到/mnt/cdisk目录下,这样,访问cdisk目录就等于是随意访问c盘了。
挂载成功后,

Bash代码 
  1. cd /mnt/cdisk  
  2. rpm -i -h telnet-server-0.17-31.EL4.3.i386.rpm  

安装成功。


root登录: 
telnet 登陆redhat linux,用一般用户登陆,再用su切换到root。.

再看
http://xiaocao000.javaeye.com/blog/564325

posted @ 2011-03-02 14:41 @Koven.Z 阅读(356) | 评论 (0)编辑 收藏
LinuxAS4搭建FTP

环境:AS4;vsftpd2.0.1(系统自带rpm包);

1 在进行FTP服务器配置之前,首先要确认LINUX系统中是否已经安装了FTP服务器软件包,Red Had Linux AS 4提供了FTP服务器的rpm包,使用如下命令检查:
# rpm –qa |grep vsftpd
结果现实为:
vsftpd-2.0.1-5.EL4.5
如果没有安装,则需要 Red Had Linux AS 4 安装盘来安装。

2  创建相应的实体用户和用户的操作目录、创建虚拟的公共目录操作目录
#groupadd future                 #创建我的ftp用户组
#useradd –g future test            #创建实体用户test并隶属于future组
#passwd test
#mkdir –p /home/test/pub          #创建用户test的操作目录pub
#mkdir –p /var/ftp/pub               #创建虚拟的公共目录操作目录( 让匿名用户登陆后访问的也是该目录)默认是已经创建好的

3  将创建的目录权限改为770
#chmod –R 770 /home/test/pub
#chmod –R 770 /var/ftp/pub

4  将test用户的/pub目录挂载到 /ftp/pub,这样 /ftp/pub目录和test用户家目录下的pub文件夹是公共同步的。所有的对虚拟公共目录的操作都是对/ftp/pub目录的操作
#mount  --bind  /var/ftp/pub  /home/test/pub
注:
对于大量用户,需要开机启动挂载则编辑/etc/rc.d/rc.local
在文档的最后添加:
mount  --bind  /var/ftp/pub  /home/test/pub
让test用户有权限访问/var/ftp/pub
#chown –R test.future /var/ftp/pub
5匿名用户权限
打开vsftpd.conf(/etc/vsftpd/vsftpd.conf)配置文件
#vi /etc/vsftpd/vsftpd.conf
 anonymous_enable=YES        //允许匿名登陆 
 anon_upload_enable=YES       //允许匿名上传
 write_enable=YES             //匿名用户对文件系统的上传目录有写的权限
 anon_world_readable_only=NO  //开放匿名用户的浏览权限

设置完后,重启vsftp服务(#service vsftpd restart),测试ftp是否正常登录.

6 若要增加、删除ftp用户:
添加ftp用户如test1
#useradd –g future test1
#mkdir –p /home/test1/pub
#mount --bind /var/ftp/pub /home/test1/pub
#vi /etc/rc.d/rc.local  
#在最后一行添加
mount --bind /var/ftp/pub /home/test1/pub

删除ftp用户如test1:
#userdel test1
#umount  /home/test1/pub
#cd /home
#rm –rf test1
#vi /etc/rc.d/rc.local  
#删除最后一行于test相关的内容
mount --bind /var/ftp/pub /home/test1/pub


注意事项:/ftp/pub目录和每个用户家目录下的pub文件夹是公共同步的,应防止其联动更改属组及权限。如需要更改test用户目录权限,则需执行
#umount /home/test/pub
然后使用相应的chown或chmod;

posted @ 2011-03-02 14:40 @Koven.Z 阅读(384) | 评论 (0)编辑 收藏

2010年12月16日

ADO 自定义常量 详解

     摘要: 这些常量是 ADO 预先定义的,包含在 adovbs.inc 或 adojava.inc 中,这些文件存放在 \programFiles\CommonFiles\system\ado\ 目录中。对于 ASP,既可以包含这些文件,也可以使用一个 METADATA 标记引用类型库。<!--METADATATYPE=“typelib”unid=“{0000020...  阅读全文

posted @ 2010-12-16 17:22 @Koven.Z 阅读(955) | 评论 (0)编辑 收藏

2010年11月19日

WIN32 匈牙利 表示法 前缀 说明



转自 《WINDOWS 程序设计》

posted @ 2010-11-19 10:38 @Koven.Z 阅读(409) | 评论 (0)编辑 收藏

2010年11月15日

复制构造函数(拷贝构造函数)

复制构造函数发生时间
1.类作为函数参数
2.函数返回值
3.对已经定义的对象进行赋值操作

复制构造函数做了什么
1.内置类型,直接赋值(指针除外,以下有特殊说明)
2.类类型,调用该类的复制构造函数
3.指针类型,只赋值指针中的地址,不赋值指针指向的对象,即所谓的浅拷贝,我们实际上需要的是深拷贝(这是一般要重写复制构造函数的主要原因,我认为是唯一原因)

一些技巧
1.要禁止赋值,显式的把赋值构造函数声明为private,同时不定义即可
2.复制构造函数最困难的部分在于认识到他的必要性


示例,string的拷贝构造函数
  String::String(const String &other)

    {  

   // 允许操作other的私有成员m_data

    int length = strlen(other.m_data);  

    m_data = new char[length+1];

    strcpy(m_data, other.m_data);

   }

个人建议,看看《高质量C++编程》相关部分,收获颇丰。

posted @ 2010-11-15 20:51 @Koven.Z 阅读(356) | 评论 (0)编辑 收藏
VC ADO 数据库

http://haitanghua.cn/article/html/47538.html

posted @ 2010-11-15 09:46 @Koven.Z 阅读(292) | 评论 (0)编辑 收藏

2010年11月10日

Socket I/O模型全接触

本文简单介绍了当前Windows支持的各种Socket I/O模型,如果你发现其中存在什么错误请务必赐教。

    一:select模型
    二:WSAAsyncSelect模型
    三:WSAEventSelect模型
    四:Overlapped I/O 事件通知模型
    五:Overlapped I/O 完成例程模型
    六:IOCP模型

    老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。
    这和Socket模型非常类似。下面我就以老陈接收信件为例讲解Socket I/O模型~~~

一:select模型

老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信~~~~~
在这种情况下,“下楼检查信箱”然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。
select模型和老陈的这种情况非常相似:周而复始地去检查......如果有数据......接收/发送.......

使用线程来select应该是通用的做法:


procedure TListenThread.Execute;
var
addr     : TSockAddrIn;
fd_read : TFDSet;
timeout : TTimeVal;
ASock,
MainSock : TSocket;
len, i   : Integer;
begin
MainSock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
addr.sin_family := AF_INET;
addr.sin_port := htons(5678);
addr.sin_addr.S_addr := htonl(INADDR_ANY);
bind( MainSock, @addr, sizeof(addr) );
listen( MainSock, 5 );

while (not Terminated) do
begin
    FD_ZERO( fd_read );
    FD_SET( MainSock, fd_read );
    timeout.tv_sec := 0;
    timeout.tv_usec := 500;
    if select( 0, @fd_read, nil, nil, @timeout ) > 0 then {//至少有1个等待Accept的connection}
    begin
      if FD_ISSET( MainSock, fd_read ) then
      begin
        for i:=0 to fd_read.fd_count-1 do {//注意,fd_count <= 64,也就是说select只能同时管理最多64个连接}
        begin
          len := sizeof(addr);
          ASock := accept( MainSock, addr, len );
          if ASock <> INVALID_SOCKET then
              .{//为ASock创建一个新的线程,在新的线程中再不停地select}
        end;
      end;
    end;
end; {//while (not self.Terminated)}

shutdown( MainSock, SD_BOTH );
closesocket( MainSock );
end;

 

二:WSAAsyncSelect模型

后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,你瞅准了,蓝天......不是,微软~~~~~~~~
微软提供的WSAAsyncSelect模型就是这个意思。

WSAAsyncSelect模型是Windows下最简单易用的一种Socket I/O模型。使用这种模型时,Windows会把网络事件以消息的形势通知应用程序。
首先定义一个消息标示常量:
const WM_SOCKET = WM_USER + 55;
再在主Form的private域添加一个处理此消息的函数声明:


private
procedure WMSocket(var Msg: TMessage); message WM_SOCKET;
{然后就可以使用WSAAsyncSelect了:}


Code
var
addr : TSockAddr;
sock : TSocket;

sock := socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
addr.sin_family := AF_INET;
addr.sin_port := htons(5678);
addr.sin_addr.S_addr := htonl(INADDR_ANY);
bind( m_sock, @addr, sizeof(SOCKADDR) );

WSAAsyncSelect( m_sock, Handle, WM_SOCKET, FD_ACCEPT or FD_CLOSE );

listen( m_sock, 5 );
  
应用程序可以对收到WM_SOCKET消息进行分析,判断是哪一个socket产生了网络事件以及事件类型:


procedure TfmMain.WMSocket(var Msg: TMessage);
var
sock    : TSocket;
addr    : TSockAddrIn;
addrlen : Integer;
buf     : Array [0..4095] of Char;
begin
{//Msg的WParam是产生了网络事件的socket句柄,LParam则包含了事件类型}
case WSAGetSelectEvent( Msg.LParam ) of
    FD_ACCEPT :
    begin
      addrlen := sizeof(addr);
      sock := accept( Msg.WParam, addr, addrlen );
      if sock <> INVALID_SOCKET then
         WSAAsyncSelect( sock, Handle, WM_SOCKET, FD_READ or FD_WRITE or FD_CLOSE );
    end;

    FD_CLOSE : closesocket( Msg.WParam );
    FD_READ : recv( Msg.WParam, buf[0], 4096, 0 );
    FD_WRITE : ;
end;
end;

三:WSAEventSelect模型

后来,微软的信箱非常畅销,购买微软信箱的人以百万计数......以至于盖茨每天24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出“新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了。

同样要使用线程:


procedure TListenThread.Execute;
var
hEvent : WSAEvent;
ret    : Integer;
ne     : TWSANetworkEvents;
sock   : TSocket;
adr    : TSockAddrIn;
sMsg   : String;
Index,
EventTotal : DWORD;
EventArray : Array [0..WSA_MAXIMUM_WAIT_EVENTS-1] of WSAEVENT;
begin
   socket bind
hEvent := WSACreateEvent();
WSAEventSelect( ListenSock, hEvent, FD_ACCEPT or FD_CLOSE );
   listen

while ( not Terminated ) do
begin
    Index := WSAWaitForMultipleEvents( EventTotal, @EventArray[0], FALSE, WSA_INFINITE, FALSE );
    FillChar( ne, sizeof(ne), 0 );
    WSAEnumNetworkEvents( SockArray[Index-WSA_WAIT_EVENT_0], EventArray[Index-WSA_WAIT_EVENT_0], @ne );

    if ( ne.lNetworkEvents and FD_ACCEPT ) > 0 then
    begin
      if ne.iErrorCode[FD_ACCEPT_BIT] <> 0 then
         continue;

      ret := sizeof(adr);
      sock := accept( SockArray[Index-WSA_WAIT_EVENT_0], adr, ret );
      if EventTotal > WSA_MAXIMUM_WAIT_EVENTS-1 then{//这里WSA_MAXIMUM_WAIT_EVENTS同样是64}
      begin
        closesocket( sock );
        continue;
      end;

      hEvent := WSACreateEvent();
      WSAEventSelect( sock, hEvent, FD_READ or FD_WRITE or FD_CLOSE );
      SockArray[EventTotal] := sock;
      EventArray[EventTotal] := hEvent;
      Inc( EventTotal );
    end;

    if ( ne.lNetworkEvents and FD_READ ) > 0 then
    begin
      if ne.iErrorCode[FD_READ_BIT] <> 0 then
         continue;
      FillChar( RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
      ret := recv( SockArray[Index-WSA_WAIT_EVENT_0], RecvBuf[0], PACK_SIZE_RECEIVE, 0 );
      
    end;
end;
end;

四:Overlapped I/O 事件通知模型

后来,微软通过调查发现,老陈不喜欢上下楼收发信件,因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术,只要用户告诉微软自己的家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了!老陈很高兴,因为他不必再亲自收发信件了!

Overlapped I/O 事件通知模型和WSAEventSelect模型在实现上非常相似,主要区别在“Overlapped”,Overlapped模型是让应用程序使用重叠数据结构(WSAOVERLAPPED),一次投递一个或多个Winsock I/O请求。这些提交的请求完成后,应用程序会收到通知。什么意思呢?就是说,如果你想从socket上接收数据,只需要告诉系统,由系统为你接收数据,而你需要做的只是为系统提供一个缓冲区~~~~~
Listen线程和WSAEventSelect模型一模一样,Recv/Send线程则完全不同:


Code
procedure TOverlapThread.Execute;
var
dwTemp : DWORD;
ret    : Integer;
Index : DWORD;
begin

while ( not Terminated ) do
begin
    Index := WSAWaitForMultipleEvents( FLinks.Count, @FLinks.Events[0], FALSE, RECV_TIME_OUT, FALSE );
    Dec( Index, WSA_WAIT_EVENT_0 );
    if Index > WSA_MAXIMUM_WAIT_EVENTS-1 then {//超时或者其他错误}
       continue;

    WSAResetEvent( FLinks.Events[Index] );
    WSAGetOverlappedResult( FLinks.Sockets[Index], FLinks.pOverlaps[Index], @dwTemp, FALSE, FLinks.pdwFlags[Index]^ );

    if dwTemp = 0 then {//连接已经关闭}
    begin
     
      continue;
    end else
    begin
      fmMain.ListBox1.Items.Add( FLinks.pBufs[Index]^.buf );
    end;

    {//初始化缓冲区}
    FLinks.pdwFlags[Index]^ := 0;
    FillChar( FLinks.pOverlaps[Index]^, sizeof(WSAOVERLAPPED), 0 );
    FLinks.pOverlaps[Index]^.hEvent := FLinks.Events[Index];
    FillChar( FLinks.pBufs[Index]^.buf^, BUFFER_SIZE, 0 );

    {//递一个接收数据请求}
    WSARecv( FLinks.Sockets[Index], FLinks.pBufs[Index], 1, FLinks.pdwRecvd[Index]^, FLinks.pdwFlags[Index]^, FLinks.pOverlaps[Index], nil );
end;
end;

五:Overlapped I/O 完成例程模型

老陈接收到新的信件后,一般的程序是:打开信封----掏出信纸----阅读信件----回复信件......为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信/阅读/回复了!老陈终于过上了小资生活!

Overlapped I/O 完成例程要求用户提供一个回调函数,发生新的网络事件的时候系统将执行这个函数:


procedure WorkerRoutine( const dwError, cbTransferred : DWORD; const
          lpOverlapped : LPWSAOVERLAPPED; const dwFlags : DWORD ); stdcall;

然后告诉系统用WorkerRoutine函数处理接收到的数据:
WSARecv( m_socket, @FBuf, 1, dwTemp, dwFlag, @m_overlap, WorkerRoutine );
然后......没有什么然后了,系统什么都给你做了!微软真实体贴!


Code
while ( not Terminated ) do{//这就是一个Recv/Send线程要做的事情什么都不用做啊!!!}
begin
if SleepEx( RECV_TIME_OUT, True ) = WAIT_IO_COMPLETION then {//}
begin
    ;
end else
begin
    continue;
end;
end;

 

六:IOCP模型

微软信箱似乎很完美,老陈也很满意。但是在一些大公司情况却完全不同!这些大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃!需要重新启动!微软不得不使出杀手锏......
微软给每个大公司派了一名名叫“Completion Port”的超级机器人,让这个机器人去处理那些信件!

“Windows NT小组注意到这些应用程序的性能没有预料的那么高。特别的,处理很多同时的客户请求意味着很多线程并发地运行在系统中。因为所有这些线程都是可运行的[没有被挂起和等待发生什么事],Microsoft意识到NT内核花费了太多的时间来转换运行线程的上下文[Context],线程就没有得到很多CPU时间来做它们的工作。大家可能也都感觉到并行模型的瓶颈在于它为每一个客户请求都创建了一个新线程。创建线程比起创建进程开销要小,但也远不是没有开销的。我们不妨设想一下:如果事先开好N个线程,让它们在那hold[堵塞],然后可以将所有用户的请求都投递到一个消息队列中去。然后那N个线程逐一从消息队列中去取出消息并加以处理。就可以避免针对每一个用户请求都开线程。不仅减少了线程的资源,也提高了线程的利用率。理论上很不错,你想我等泛泛之辈都能想出来的问题,Microsoft又怎会没有考虑到呢?”-----摘自nonocast的《理解I/O Completion Port》

先看一下IOCP模型的实现:


Code
{创建一个完成端口}
FCompletPort := CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0,0,0 );

{接受远程连接,并把这个连接的socket句柄绑定到刚才创建的IOCP上}
AConnect := accept( FListenSock, addr, len);
CreateIoCompletionPort( AConnect, FCompletPort, nil, 0 );

{创建CPU数*2 + 2个线程}
for i:=1 to si.dwNumberOfProcessors*2+2 do
begin
AThread := TRecvSendThread.Create( false );
AThread.CompletPort := FCompletPort;{//告诉这个线程,你要去这个IOCP去访问数据}
end;

OK,就这么简单,我们要做的就是建立一个IOCP,把远程连接的socket句柄绑定到刚才创建的IOCP上,最后创建n个线程,并告诉这n个线程到这个IOCP上去访问数据就可以了。

再看一下TRecvSendThread线程都干些什么:


Code
procedure TRecvSendThread.Execute;
var

begin
while (not self.Terminated) do
begin
    {查询IOCP状态(数据读写操作是否完成)}
    GetQueuedCompletionStatus( CompletPort, BytesTransd, CompletKey, POVERLAPPED(pPerIoDat), TIME_OUT );

    if BytesTransd <> 0 then
       .;{//数据读写操作完成}

    {//再投递一个读数据请求}
    WSARecv( CompletKey, @(pPerIoDat^.BufData), 1, BytesRecv, Flags, @(pPerIoDat^.Overlap), nil );
end;
end;

读写线程只是简单地检查IOCP是否完成了我们投递的读写操作,如果完成了则再投递一个新的读写请求。
应该注意到,我们创建的所有TRecvSendThread都在访问同一个IOCP(因为我们只创建了一个IOCP),并且我们没有使用临界区!难道不会产生冲突吗?不用考虑同步问题吗?
呵呵,这正是IOCP的奥妙所在。IOCP不是一个普通的对象,不需要考虑线程安全问题。它会自动调配访问它的线程:如果某个socket上有一个线程A正在访问,那么线程B的访问请求会被分配到另外一个socket。这一切都是由系统自动调配的,我们无需过问。

呵呵,终于写完了,好累......以上所有的源代码可以从这里看到:
http://community.csdn.net/Expert/topic/3844/3844679.xml?temp=5.836123E-02
不过代码写的很简陋,请多包涵!

转自:http://blog.csdn.net/flyinwuhan/

声明:除CSDN外的任何媒体转载必须注明作者以及“转载自CSDN”。

posted @ 2010-11-10 09:40 @Koven.Z 阅读(466) | 评论 (2)编辑 收藏

2010年11月9日

socket IO完成端口模型详解

#include "stdafx.h"

#include <WINSOCK2.h>
#include <stdio.h>

#define PORT     5150
#define MSGSIZE 1024

#pragma comment(lib, "ws2_32.lib")

typedef enum
{
   RECV_POSTED
}OPERATION_TYPE;       //枚举,表示状态

typedef struct
{
WSAOVERLAPPED   overlap;      
WSABUF          Buffer;        
   char            szMessage[MSGSIZE];
DWORD           NumberOfBytesRecvd;
DWORD           Flags;
OPERATION_TYPE OperationType;
}PER_IO_OPERATION_DATA, *LPPER_IO_OPERATION_DATA;    //定义一个结构体保存IO数据

DWORD WINAPI WorkerThread(LPVOID);

int main()
{
   WSADATA                  wsaData;
   SOCKET                   sListen, sClient;
   SOCKADDR_IN              local, client;
   DWORD                    i, dwThreadId;
   int                      iaddrSize = sizeof(SOCKADDR_IN);
   HANDLE                   CompletionPort = INVALID_HANDLE_VALUE;
   SYSTEM_INFO              systeminfo;
   LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

   // Initialize Windows Socket library
   WSAStartup(0x0202, &wsaData);

   // 初始化完成端口
   CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);

   // 有几个CPU就创建几个工作者线程
   GetSystemInfo(&systeminfo);
   for (i = 0; i < systeminfo.dwNumberOfProcessors; i++)
   {
     CreateThread(NULL, 0, WorkerThread, CompletionPort, 0, &dwThreadId);
   }

   // 创建套接字
   sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

   // 绑定套接字
   local.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
local.sin_family = AF_INET;
local.sin_port = htons(PORT);
   bind(sListen, (struct sockaddr *)&local, sizeof(SOCKADDR_IN));

   // 开始监听!
   listen(sListen, 3);

   while (TRUE)//主进程的这个循环中循环等待客户端连接,若有连接,则将该客户套接字于完成端口绑定到一起
      //然后开始异步等待接收客户传来的数据。
   {
     // 如果接到客户请求连接,则继续,否则等待。
     sClient = accept(sListen, (struct sockaddr *)&client, &iaddrSize);
//client中保存用户信息。
     printf("Accepted client:%s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));

//将这个最新到来的客户套接字和完成端口绑定到一起。
     CreateIoCompletionPort((HANDLE)sClient, CompletionPort, ( ULONG_PTR)sClient, 0);
//第三个参数表示传递的参数,这里就传递的客户套接字地址。最后一个参数为0 表示有和CPU一样的进程数。即1个CPU一个线程
   
     // 初始化结构体
     lpPerIOData = (LPPER_IO_OPERATION_DATA)HeapAlloc(
       GetProcessHeap(),
       HEAP_ZERO_MEMORY,
       sizeof(PER_IO_OPERATION_DATA));
     lpPerIOData->Buffer.len = MSGSIZE; // len=1024
     lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
     lpPerIOData->OperationType = RECV_POSTED; //操作类型
     WSARecv(sClient,         //异步接收消息,立刻返回。
       &lpPerIOData->Buffer, //获得接收的数据
       1,       //The number of WSABUF structures in the lpBuffers array.
       &lpPerIOData->NumberOfBytesRecvd, //接收到的字节数,如果错误返回0
       &lpPerIOData->Flags,       //参数,先不管
       &lpPerIOData->overlap,     //输入这个结构体咯。
       NULL);
   }


//posts an I/O completion packet to an I/O completion port.
   PostQueuedCompletionStatus(CompletionPort, 0xFFFFFFFF, 0, NULL);
CloseHandle(CompletionPort);
closesocket(sListen);
WSACleanup();
return 0;
}
//工作者线程有一个参数,是指向完成端口的句柄
DWORD WINAPI WorkerThread(LPVOID CompletionPortID)
{
   HANDLE                   CompletionPort=(HANDLE)CompletionPortID;
   DWORD                    dwBytesTransferred;
   SOCKET                   sClient;
   LPPER_IO_OPERATION_DATA lpPerIOData = NULL;

   while (TRUE)
   {
     GetQueuedCompletionStatus( //遇到可以接收数据则返回,否则等待
       CompletionPort,
       &dwBytesTransferred, //返回的字数
    (PULONG_PTR&)sClient,           //是响应的哪个客户套接字?
       (LPOVERLAPPED *)&lpPerIOData, //得到该套接字保存的IO信息
       INFINITE);               //无限等待咯。不超时的那种。
     if (dwBytesTransferred == 0xFFFFFFFF)
     {
       return 0;
     }
   
     if (lpPerIOData->OperationType == RECV_POSTED) //如果受到数据
     {
       if (dwBytesTransferred == 0)
       {
         // Connection was closed by client
         closesocket(sClient);
         HeapFree(GetProcessHeap(), 0, lpPerIOData);        //释放结构体
       }
       else
       {
         lpPerIOData->szMessage[dwBytesTransferred] = '\0';
         send(sClient, lpPerIOData->szMessage, dwBytesTransferred, 0); //将接收到的消息返回
       
         // Launch another asynchronous operation for sClient
         memset(lpPerIOData, 0, sizeof(PER_IO_OPERATION_DATA));
         lpPerIOData->Buffer.len = MSGSIZE;
         lpPerIOData->Buffer.buf = lpPerIOData->szMessage;
         lpPerIOData->OperationType = RECV_POSTED;
         WSARecv(sClient,               //循环接收
    &lpPerIOData->Buffer,
    1,
    &lpPerIOData->NumberOfBytesRecvd,
    &lpPerIOData->Flags,
    &lpPerIOData->overlap,
    NULL);
       }
     }
   }
   return 0;
}
/*
首先,说说主线程:
1.创建完成端口对象
2.创建工作者线程(这里工作者线程的数量是按照CPU的个数来决定的,这样可以达到最佳性能)
3.创建监听套接字,绑定,监听,然后程序进入循环
4.在循环中,我做了以下几件事情:
(1).接受一个客户端连接
(2).将该客户端套接字与完成端口绑定到一起(还是调用CreateIoCompletionPort,但这次的作用不同),
注意,按道理来讲,此时传递给CreateIoCompletionPort的第三个参数应该是一个完成键,
一般来讲,程序都是传递一个单句柄数据结构的地址,该单句柄数据包含了和该客户端连接有关的信息,
由于我们只关心套接字句柄,所以直接将套接字句柄作为完成键传递;
(3).触发一个WSARecv异步调用,用到了“尾随数据”,使接收数据所用的缓冲区紧跟在WSAOVERLAPPED对象之后,
此外,还有操作类型等重要信息。

在工作者线程的循环中,我们
1.调用GetQueuedCompletionStatus取得本次I/O的相关信息(例如套接字句柄、传送的字节数、单I/O数据结构的地址等等)
2.通过单I/O数据结构找到接收数据缓冲区,然后将数据原封不动的发送到客户端
3.再次触发一个WSARecv异步操作

自己的理解,有任何问题都可以留言或者于我联系


 

线程池中最佳的工作线程数应该是 CPU个数 * 2 + 2 —— WIN32多线程程序设计

posted @ 2010-11-09 21:50 @Koven.Z 阅读(2076) | 评论 (0)编辑 收藏
仅列出标题  下一页