Windows完成端口編程

Windows完成端口編程
目錄
一 基本概念
二 OVERLAPPED數據結構
三 完成端口的內部機制
建立完成端口
完成端口線程的工做原理
線程間數據傳遞
線程的安全退出php

一 基本概念
       設備---windows操做系統上容許通訊的任何東西,好比文件、目錄、串行口、並行口、郵件槽、命名管道、無名管道、套接字、控制檯、邏輯磁盤、物理 磁盤等。絕大多數與設備打交道的函數都是CreateFile/ReadFile/WriteFile等。因此咱們不能看到**File函數就只想到文件 設備。html

       與設備通訊有兩種方式,同步方式和異步方式。同步方式下,當調用ReadFile函數時,函數會等待系統執行完所要求的工做,而後才返回;異步方式下,ReadFile這類函數會直接返回,系統本身去完成對設備的操做,而後以某種方式通知完成操做。ios

       重疊I/O----顧名思義,當你調用了某個函數(好比ReadFile)就馬上返回作本身的其餘動做的時候,同時系統也在對I/0設備進行你要求的操 做,在這段時間內你的程序和系統的內部動做是重疊的,所以有更好的性能。因此,重疊I/O是用於異步方式下使用I/O設備的。算法

重疊I/O須要使用的一個很是重要的數據結構OVERLAPPED。編程

       完成端口---是一種WINDOWS內核對象。完成端口用於異步方式的重疊I/0狀況下,固然重疊I/O不必定非使用完成端口不可,還有設備內核對象、事 件對象、告警I/0等。可是完成端口內部提供了線程池的管理,能夠避免反覆建立線程的開銷,同時能夠根據CPU的個數靈活的決定線程個數,並且可讓減小 線程調度的次數從而提升性能。windows

二 OVERLAPPED數據結構
typedef struct _OVERLAPPED 
{
    ULONG_PTR Internal;//被系統內部賦值,用來表示系統狀態
    ULONG_PTR InternalHigh;// 被系統內部賦值,傳輸的字節數 
    union {
        struct
             {
                DWORD Offset;//和OffsetHigh合成一個64位的整數,用來表示從文件頭部的多少字節開始
                DWORD OffsetHigh;//操做,若是不是對文件I/O來操做,則必須設定爲0
             };
        PVOID Pointer;
    };
    HANDLE  hEvent;//若是不使用,就務必設爲0,不然請賦一個有效的Event句柄
} OVERLAPPED, *LPOVERLAPPED;api


下面是異步方式使用ReadFile的一個例子
OVERLAPPED Overlapped;
Overlapped.Offset=345;
Overlapped.OffsetHigh=0;
Overlapped.hEvent=0;
//假定其餘參數都已經被初始化
ReadFile(hFile,buffer,sizeof(buffer),&dwNumBytesRead,&Overlapped);
這樣就完成了異步方式讀文件的操做,而後ReadFile函數返回,由操做系統作本身的事情吧數組

下面介紹幾個與OVERLAPPED結構相關的函數
等待重疊I/0操做完成的函數
BOOL GetOverlappedResult (
ANDLE hFile,
LPOVERLAPPED lpOverlapped,//接受返回的重疊I/0結構
LPDWORD lpcbTransfer,//成功傳輸了多少字節數
BOOL fWait //TRUE只有當操做完成才返回,FALSE直接返回,若是操做沒有完成,經過調//用GetLastError ( )函數會返回ERROR_IO_INCOMPLETE
);緩存

宏HasOverlappedIoCompleted能夠幫助咱們測試重疊I/0操做是否完成,該宏對OVERLAPPED結構的Internal成員進行了測試,查看是否等於STATUS_PENDING值。安全


三 完成端口的內部機制
建立完成端口

       完成端口是一個內核對象,使用時他老是要和至少一個有效的設備句柄進行關聯,完成端口是一個複雜的內核對象,建立它的函數是:
HANDLE CreateIoCompletionPort(
    IN HANDLE FileHandle,
    IN HANDLE ExistingCompletionPort,
    IN ULONG_PTR CompletionKey,
    IN DWORD NumberOfConcurrentThreads );

一般建立工做分兩步:

第一步,建立一個新的完成端口內核對象,可使用下面的函數:

HANDLE CreateNewCompletionPort(DWORD dwNumberOfThreads)
{
 return CreateIoCompletionPort(INVALID_HANDLE_VALUE,NULL,NULL,dwNumberOfThreads);
};

第二步,將剛建立的完成端口和一個有效的設備句柄關聯起來,可使用下面的函數:

 bool AssicoateDeviceWithCompletionPort(HANDLE hCompPort,HANDLE hDevice,DWORD dwCompKey)
{
          HANDLE h=CreateIoCompletionPort(hDevice,hCompPort,dwCompKey,0);
          return h==hCompPort;
};

說明
1)  CreateIoCompletionPort函數也能夠一次性的既建立完成端口對象,又關聯到一個有效的設備句柄
2)  CompletionKey是一個能夠本身定義的參數,咱們能夠把一個結構的地址賦給它,而後在合適的時候取出來使用,最好要保證結構裏面的內存不是分配在棧上,除非你有十分的把握內存會保留到你要使用的那一刻。
3)  NumberOfConcurrentThreads一般用來指定要容許同時運行的的線程的最大個數。一般咱們指定爲0,這樣系統會根據CPU的個數來自動肯定。
建立和關聯的動做完成後,系統會將完成端口關聯的設備句柄、完成鍵做爲一條紀錄加入到這個完成端口的設備列表中。若是你有多個完成端口,就會有多個對應的設備列表。若是設備句柄被關閉,則表中自動刪除該紀錄。

完成端口線程的工做原理
       完成端口能夠幫助咱們管理線程池,可是線程池中的線程須要咱們使用_beginthreadex來建立,憑什麼通知完成端口管理咱們的新線程呢?答案在函數GetQueuedCompletionStatus。該函數原型:
BOOL GetQueuedCompletionStatus(
    IN  HANDLE CompletionPort,
    OUT LPDWORD lpNumberOfBytesTransferred,
    OUT PULONG_PTR lpCompletionKey,
    OUT LPOVERLAPPED *lpOverlapped,
    IN  DWORD dwMilliseconds
);

這個函數試圖從指定的完成端口的I/0完成隊列中抽取紀錄。只有當重疊I/O動做完成的時候,完成隊列中才有紀錄。凡是調用這個函數的線程將被放入到完成端口的等待線程隊列中,所以完成端口就能夠在本身的線程池中幫助咱們維護這個線程。
完 成端口的I/0完成隊列中存放了當重疊I/0完成的結果---- 一條紀錄,該紀錄擁有四個字段,前三項就對應GetQueuedCompletionStatus函數的二、三、4參數,最後一個字段是錯誤信息 dwError。咱們也能夠經過調用PostQueudCompletionStatus模擬完成了一個重疊I/0操做。
當I/0完成隊列中出現 了紀錄,完成端口將會檢查等待線程隊列,該隊列中的線程都是經過調用GetQueuedCompletionStatus函數使本身加入隊列的。等待線程 隊列很簡單,只是保存了這些線程的ID。完成端口會按照後進先出的原則將一個線程隊列的ID放入到釋放線程列表中,同時該線程將從等待 GetQueuedCompletionStatus函數返回的睡眠狀態中變爲可調度狀態等待CPU的調度。
基本上狀況就是如此,因此咱們的線程要想成爲完成端口管理的線程,就必需要調用
GetQueuedCompletionStatus函數。出於性能的優化,實際上完成端口還維護了一個暫停線程列表,具體細節能夠參考《Windows高級編程指南》,咱們如今知道的知識,已經足夠了。

線程間數據傳遞

       線程間傳遞數據最經常使用的辦法是在_beginthreadex函數中將參數傳遞給線程函數,或者使用全局變量。可是完成端口還有本身的傳遞數據的方法,答案就在於CompletionKey和OVERLAPPED參數。
CompletionKey 被保存在完成端口的設備表中,是和設備句柄一一對應的,咱們能夠將與設備句柄相關的數據保存到CompletionKey中,或者將 CompletionKey表示爲結構指針,這樣就能夠傳遞更加豐富的內容。這些內容只能在一開始關聯完成端口和設備句柄的時候作,所以不能在之後動態改 變。
OVERLAPPED參數是在每次調用ReadFile這樣的支持重疊I/0的函數時傳遞給完成端口的。咱們能夠看到,若是咱們不是對文件設 備作操做,該結構的成員變量就對咱們幾乎毫無做用。咱們須要附加信息,能夠建立本身的結構,而後將OVERLAPPED結構變量做爲咱們結構變量的第一個 成員,而後傳遞第一個成員變量的地址給ReadFile函數。由於類型匹配,固然能夠經過編譯。當GetQueuedCompletionStatus函 數返回時,咱們能夠獲取到第一個成員變量的地址,而後一個簡單的強制轉換,咱們就能夠把它看成完整的自定義結構的指針使用,這樣就能夠傳遞不少附加的數據 了。太好了!只有一點要注意,若是跨線程傳遞,請注意將數據分配到堆上,而且接收端應該將數據用完後釋放。咱們一般須要將ReadFile這樣的異步函數 的所須要的緩衝區放到咱們自定義的結構中,這樣當GetQueuedCompletionStatus被返回時,咱們的自定義結構的緩衝區變量中就存放了 I/0操做的數據。
CompletionKey和OVERLAPPED參數,均可以經過GetQueuedCompletionStatus函數得到。
線程的安全退出
      不少線程爲了避免止一次的執行異步數據處理,須要使用以下語句

while (true)
{

     ....
     GetQueuedCompletionStatus(...);       
}

那麼如何退出呢,答案就在於上面曾提到的PostQueudCompletionStatus函數,咱們能夠用它發送一個自定義的包含了OVERLAPPED成員變量的結構地址,裏面包含一個狀態變量,當狀態變量爲退出標誌時,線程就執行清除動做而後退出。

8.2.5完成端口模型
建立完成端口對象 CreateCompletionPort(HANDLE FileHandle, HANDLE ExistingCompletionPort, DWORD CompletionKey, DWORD NumberOfConcurrentThreads);
CompletionKey:則指定要與某個特定套接字句柄關聯在一塊兒的「單句柄數據」
1.工做者線程與完成端口
StartWinsock();
//step 1
//create an IO completion port.

CompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,0);

//step 2;
//Determine how many processors are on the system

GetSystemInfo(&SystemInfo);

//Step3
//create worker threads based on the number of processors available on the system .For this simple case, we create one worker thread for each processor.
for(i=0; i < SystemInfo.dwNumberOfProcessors; i++)
{
   HANDLE ThreadHandle;
   //create a server worker thread and pass the completion port to the thread, Note:the ServerWorkerThread procedure is not defined in this listing.
   ThreadHandle = CreateThread(NULL, 0, ServerWorkerThread, CompletionPort,0, &ThreadID);

   //close the thread handle
   CloseHandle(ThreadHandle);
}

//step 4
//create a listening socket
Listen = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);

InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(5150);
bind(Listen, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr));

//prepare socket for listening
listen(Listen, 5);

while (TRUE)
{
 //step 5
 //Accept connections and assign to completion port.
 Accept = WSAAccept(Listen, NULL, NULL, NULL, 0);

 //step 6
 //create per-handle data information structure to associate with the socket
 PerHandleData = (LPPER_HANDLE_DATA)GlobalAlloc(GPTR, sizeof(PER_HANDLE_DATA));

 printf("Socket number %d connected/n",Accept);
 PerHandleData->Socket = Accept;

 //step 7
 //Associate the accepted socket with the completion port.
 CreateIoCompletionPort((HANDLE)Accept,CompletionPort, (DWORD)PerHandleData,0);

 //step 8
 //start processing IO on the accepted socket. post oen or more WSASend() or WSARecv() calls on the socket using overlapped IO
 WSARecv(...)''
}

2.完成端口和重疊IO
須要由咱們的應用程序負責在之後的某個時間,經過一個OVERLAPPED結構,來接收調用的結果。
BOOL GetQueuedCompletionStatus(HANDLE CompletionPort, LPDWORD lpNumberOfBytesTransferred, LPDWORD lpCompletionKey, LPOVERLAPPED* lpOverlapped, DWORD dwMilliseconds);

3.單句柄數據和單IO操做數據
typedef struct 
{
 OVERLAPPED Overlapped;
 WSABUF     DataBuf;
 CHAR       Buffer[DATA_BUFSIZE];
 bool       OperationType;
} PER_IO_OPERATION_DATA;

DWORD WINAPI ServerWorkerThread(LPVOID CompletionPortID)
{
  HANDLE CompletionPort = (HANDLE).CompletionPortID;
  DWORD  ByteTransferred;
  LPOVERLAPPED Overlapped;
  LPPER_HANDLE_DATA PerHandleData;
  LPPER_IO_OPERATION_DATA PerIoData;
  DWORD SendBytes, RecvBytes;
  DWORD Flags;

  while (TRUE)
  {
   //Wait for IO to complete on any socket associated with the completion port
   GetQueuedCompletionStatus(CompletionPort,&ByteTransferred, (LPDWORD)&PerHandleData,(LPOVERLAPPED*)&PerIoData, INFINITE);

   //socket and clean up the per-handle data and per-io opertion data associated with the socket
   if (BytesTransferred ==0&&(PerIoData->OperationType == RECV_POSTED||PerHandleData->OperationType == SEND_POSTED))
   {
    //A zero bytesTransferred indicates that the socket has been closed by the peer, so you should close the socket.
    //Note: Per-handle data was used to reference the socket associated withe IO operation.
    closesocket(PerHandleData->Socket);

    GlobalFree(PerHandleData);
    GlobalFree(PerIoData);
    continue;
   }

   //service the completed IO request, You can determine which IO request has just completed by looking at the OperationType field contained in the 
   //the per-io operation data.
     if (PerIoData->OperationType==RECV_POSTED)
  {
   //Do sth with the received data in perIoData->Buffer
     }

  //post another WSASend or WSARecv operation .
  //As an example , we will post another WSARecv() IO operation

  Flags = 0;

  //Set up the per-IO operation data for the next overlapped call
  ZeroMemory(&(PerIoData->Overlapped),sizeof(OVERLAPPED));

  PerIoData->DataBuf.len = DATA_BUFSIZE;
  PerIoData->DataBuf.buf = PerIoData->Buffer;
  PerIoData->OperationType = RECV_POSTED;

     WSARecv(PerHandleData->Socket, &(PerIoData->DataBuf),1,&RecvBytes, &Flags, &(PerIoData->Overlapped),NULL);
  }
}

8.2.3WSAEventSelect
應用程序在一個或者多個套接字上,接收以事件爲基礎的網絡事件通知。網絡事件投遞至一個事件對象句柄。
事件通知
WSAVENT WSACreateEvent(void);
BOOL    WSAResetEvent(WSAEVENT hEvent);
BOOL    WSACloseEvent(WSAEVENT hEvent);
int WSAEventSelect(SOCKET S, WSAEVENT hEventObject, long lNetworkEvents);

DWORD WSAWaitForMultipleEvents(DWORD cEvents, const WSAEVENT FAR* lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable);

Index = WSAWaitForMultipleEvents();
MyEvent = EventArray[index - WSA_WAIT_EVENT_0];
int WSAEnumNetworkEvents(SOCKET s, WSAEVET hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);

SOCKET Socket[WSA_MAXIMUM_MAIT_EVENTS];
WSAEVENT Event[WSA_MAXIMUM_MAIT_EVENTS];
SOCKET  Accept, Listen;
DWORD   EventTotal = 0;
DWORD   Index;

//Set up a TCP socket for listening on port 5150;
Listen = socket(PF_INET, SOCKET_STREAM, 0);

InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(5105);

bind(Listen, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr));

NewEvent = WSACreateEvent();

WSAEventSelect(Listen, NewEvent, FD_ACCEP|FD_CLOSE);

listen(Listen, 5);

Socket[EventTotal] = Listen;
Event[EventTotal] = NewEvent;
EventTotal++;

while(TRUE)
{
    //Wait for network events on all sockets
    Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    
    WSAEnumNetworkEvents(SocketArray[Index - WSAWAIT_EVENT_0],EventArray[Index_WSA_WAIT_EVENT_0], &NetworkEvents);
    
    //check for FD_ACCEPT messages
    if(NetWorkEvents.lNetworkEvents&FD_ACCEPT)
     {
        if(NetworkEvent.iErrorCode[FD_ACCEPT_BIT]!=0)
         {
            printf("FD_ACCEPT failed with error%d/n", NetWorkEvent.iErrorCode[FD_ACCEPT_BIT]);
            break;
         }       
     
     //Accept a new connection and it to the socket and event lists
     Accept = accept(SocketArray[Index-WSA_WAIT_EVENT_0],NULL, NULL);
     
     //We can not process more than WSA_MAXIMUM_WAIT_EVENTS sockets, so close  the accepted socket
     if(EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
     {
       printf("Too many connecttions");
       closesocket(Accept);
       break;
     }
     
     NewEvent = WSACreateEvent();
    
     WSAEventSelect(Accept, NewEvent, FD_READ|FD_WRITE|FD_CLOSE);
     
     Event[EventTotal] = NewEvent;
     Socket[EventTotal] = Accept;
     EventTotal++;
     
     printf("socket &d connected/n",ACCept);
   } 

    //Process FD_READ notification
    if(NewWorkEvent.iErrorCode[FD_READ_BIT]!0)
    {
       if(NetworkEvents.iErrorCode[FD_READ_BIT]!=0)
        {
          printf("FD_READ failed with error %dn", NetworkEvent.iErrorCode[FD_READ_BIT]);
          break;        
          
        }

        //Read data from socket
        recv(Socket[Index - WSA_WAIT_EVENT_0], buffer, sizeof(buffer),0);
    }

    //Process FD_WRITE notification
    if(NetworkEvents.lNetworkEvents&FD_WRITE)
    {
       if(NetworkEvent.iErrorCode[FD_WRITE_BIT]!=0)
       {
          printf("FD_WRITE failed with error %d/n",
          NetworkEvents.iErrorCode[FD_WRITE_BIT]);
          break;
       }
    
       send(Socket[Index - WSA_WAIT_EVENT_0], buffer, sizeof(buffer),0);         
    }
   
    if(NetworkEvents.lNetworkEvents&FD_CLOSE)
    {
        if(NetworkEvent.iErrorCode[FD_CLOSE_BIT]!=0)
        {
            printf("FD_CLOSE failed with error &d/n",
            NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
            break;
        }
    }
     
    closesocket(Socket[index - WSA_WAIT_EVET_0]);
     
    //Remove socket and associated event from the socket and evnet array and decrement eventtatal
    CompressArrays(Event, socket,&EventTotal);
   }
}

8.2.4 重疊模型
s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
1.事件通知 將win32事件對象與WSAOVERLAPPED結構關聯在一塊兒。
typedef struct WSAOVERLAPPED
{
   DWORD Internal;
   DWORD InternalHigh;
   DWORD offset;
   DWORD OffsetHigh;
   WSAEVENT hEvent;
} WSAOVERLAPPED, FAR*LPWSAOVERLAPPED;
hEvent 它容許應用程序將一個事件對象句柄同一個套接字關聯起來。

BOOL WSAGetOverlappedResult(SOCKET S, LPWSAOVERLAPPED lpOverlapped, LPDWORD lpcbTransfer, BOOL fWari, LPDWORD lpdwFlags);
void main()
{
   WSABUF DataBuf;
   DWORD  EventTotal = 0;
   WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];
   WSAOVERLAPPED AcceptOverlapped;
   SOCKET        ListenSocket, AcceptSocket;

   //step 1
   //start winsock and set up a listening socket

   //step 2
   //accepte an inbound connection

   AcceptSocket = accept(ListenSocket, NULL, NULL);

   //step 3
   //set up an overlapped structure

   EventArray[EventTotal]=WSACreateEvent();
   ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));

   AcceptOverlapped.hEvent = EventArray[EventTotal];

   DataBuf.len = DATA_BUFSIZE;
   DataBuf.buf = buffer;

   EventTotal++;

   //step 4;
   //post a WSARecv request to begin receiving data on the socket.
   WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL);

   //Process overlapped receives on the socket.
   while (TRUE)
   {
    //step 5
    //wait for overlapped io call to complete 
    Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);

    //Index should be 0 because we have one event handle in eventarray.

    //step 6
    //reset the signaled event 
    WSAResetEvent(EventArray[Index - WSA_WAIT_EVENT_0]);

    //step 7
    //Determine the status of the overlapped request.
    WSAGetOverlappedResult(AcceptSocket, &AcceptOverlapped, &BytesTransferred, FALSE, &Flags);

    //First check to see whether the peer has closed the connection ,and if so , close the socket.
    if (BytesTransferred == 0)
    {
     printf("Closing socket %d/n", AcceptSocket);
     closesocket(AcceptSocket);
     WSACloseEvent(Index - WSA_WAIT_EVENT_0);
     return;
    }

    //Do something with the received data DataBuf contains the received data.
    ...

    //step 8
    //post another WSARecv() request on the socket
     
    Flags = 0;
    ZeroMemory(&AcceptOverlapped, sizeof(WSAOVERLAPPED));
    
    AcceptOverlapped.hEvent = EventArray[Index - WSA_WAIT_EVENT_0];

    DataBuf.len = DATA_BUFSIZE;
    DataBuf.buf = Buffer;

    WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, NULL);
   }
}

2.完成例程
在一個重疊IO請求完成時由系統調用的函數。他們的基本設計宗旨是經過調用者的線程,爲一個已完成的I請求提供服務。除此以外,應用程序可經過完成例程,繼續進行重疊IO處理。
void CALLBACK CompletionROUTINE(DWORD dwError, DWORD cbTransferred, LPWSAOVERLAPPED lpOverlapped, DWORD dwFlags);

WSABUF DataBuf;
SOCKET AcceptSocket;

void main()
{
 WSAOVERLAPPED Overlapped;
  
   //step 1
   //start winsock and set up a listening socket

   //step 2
   //accept a new connection
 AcceptSocket = accept(ListenSocket, NULL, NULL);


   //step 3
   //To get the overlapped IO processing started, first submit an overlapped WSARecv() request.

   Flags = 0;
   ZeroMemory(&Overlapped, sizeof(WSAOVERLAPPED));

   DataBuf.len = DATA_BUFSIZE;
   DataBuf.buf = buffer;

   EventTotal++;

   //step 4;
   //post a WSARecv request to begin receiving data on the socket.
  if(WSARecv(AcceptSocket, &DataBuf, 1, &RecvBytes, &Flags, &AcceptOverlapped, WorkerRoutine)==SOCKET_ERROR)
  {
     if (WSAGetLastError()!=WSA_IO_PENDING)
  {
   printf("WSARecv() failed with error%d/n",WSAGetLastError());
   return;
     }
  }

   //since the WSAWaitForMutipleEvents()API requires waiting on one or more event objects, we will have to create a dummy event object, Aa
  //an alterative , we can use SleepEx() instead.
  EventArray[0]=WSACreateEvent();

   while (TRUE)
   {
    //step 5
    Index = WSAWaitForMultipleEvents(1,EventArray, FALSE, WSA_INFINITE, FALSE);

    //step 6
    if (Index == WAIT_IO_COMPLETION)
    {
     //An overlapped request completion routine just completed ,continue servicing more completion rountines.
    }
    else
    {
       //A bad error occurred --stop processing! if we were also processing an event object, this could be index to the event array.

    return;
    }
   }
}

void CALLBACK WorkerRoutine(DWORD Error, DWORD BytesTransferred, LPWSAOVERLAPPED Overlapped, DWORD InFlags)
{
   DWORD SendBytes, RecvBytes;
   DWORD Flags;
   if (Error !=0)||BytesTransferred ==0 
   {
    //Either a bad error occurred on the socket or the socket was closed by peer closesocket(AcceptSocket);
    return;
   }

   //At this point, an overlapped WSARecv() request completed successfully. Now we can retrieve the received data that is contained in the variable DataBuf
   //After processing the received data, we need to post another overlapped WSARecv() or WSASend() request. For simplicity , we will post another WSARecv() requst.
   Flags = 0;
   ZeroMemory(&Overlapped,sizeof(WSAOVERLAPPED));

   DataBuf.len = DATA_BUFSIZE;
   DataBuf.buf = Buffer;

   if (WSARecv(AcceptSocket, &DataBuf,1,&RecvBytes,&Flags, &Overlapped, WorkerRoutine)==SOCKET_ERROR)
   {
    if (WSAGetLastError()!=WSA_IO_PENDING)
    {
     printf("failed with error%d/n",WSAGetLastError());
     return;
    }
   }
}

8.2.2 WSAAsyncSelect
接收以windows消息爲基礎的網絡事件通知。 
int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg, long lEvent);
FD_READ, FD_WRITE, FD_ACCEPT, FD_CONNECT和FD_CLOSE.
從鎖定變成非鎖定狀態。
#define WM_SOCKET WM_USER+1
#define

int WINAPI WinMain(IN HINSTANCE hInstance, IN HINSTANCE hPrevInstance, IN LPSTR lpCmdLine, IN int nShowCmd )
{
   SOCKET Listen;
   HWND   Window;
   sockaddr  InternetAddr;

   Window = CreateWindow();

   WSAStartup(...);

   Listen = socket(...);

   InternetAddr.sin_family = AF_INET;
   InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
   InternetAddr.sin_port = htons(5150);

   bind(Listen, (PSOCKADDR)&InternetAddr,sizeof(InternetAddr));

   WSAAsyncSelect(Listen, Window, WM_SOCKET, FD_ACCEPT|FD_CLOSE);

   listen(Listen, 5);

   //Translate and dispatch window messages until the application terminates
}

BOOL CALLBACK ServerWinProc(HWND hDlg, WORD wMsg,WORD wParam, DWORD lParam)
{
    SOCKET Accept;
 switch(wMsg)
 {
 case WM_PAINT:
  break;
 case WM_SOCKET:

   //Determine whether an error occurred on the socket by using the WSAGETSLECTERRROR() macro
  if (WSAGETSELECTERROR(lParam))
  {
   closesocket(wParam);
   break;
  }

  //Determine what event occurred on the socket
  switch(WSAGETSELECTEVENT(lParam))
  {
  case FD_ACCEPT:
   Accept = accept(wParam, NULL, NULL);

   //Prepare accepted socket for read, write and close notification.
   WSAAsyncSelect(Accept, hwnd, WM_SOCKET, FD_READ|FD_WRITE|FD_CLOSE);
   break;
  case FD_READ:
   //Receive data from the socket in wparam;
   break;
  case  FD_WRITE:
   //The socket in param is ready for sending data;
   break;
  case FD_CLOSE:
   //The connection is now closed
   closesocket(wParam);
   break;
  }
  break;
 }
 return true;
}
產生FD_WRITE通知的三種條件
1.使用connect或者WASConnect,一個套接字首次創建了鏈接。
2.使用accept or WSAAccept,套接字被接受之後。
3.若send WSASend,sendto WSASendto操做失敗,返回了WSAEWOULDBLOCK錯誤,並且緩衝區的空間變得可用。

8.2.3WSAEventSelect
應用程序在一個或者多個套接字上,接收以事件爲基礎的網絡事件通知。網絡事件投遞至一個事件對象句柄。
事件通知
WSAVENT WSACreateEvent(void);
BOOL    WSAResetEvent(WSAEVENT hEvent);
BOOL    WSACloseEvent(WSAEVENT hEvent);
int WSAEventSelect(SOCKET S, WSAEVENT hEventObject, long lNetworkEvents);

DWORD WSAWaitForMultipleEvents(DWORD cEvents, const WSAEVENT FAR* lphEvents, BOOL fWaitAll, DWORD dwTimeout, BOOL fAlertable);

Index = WSAWaitForMultipleEvents();
MyEvent = EventArray[index - WSA_WAIT_EVENT_0];
int WSAEnumNetworkEvents(SOCKET s, WSAEVET hEventObject, LPWSANETWORKEVENTS lpNetworkEvents);

SOCKET Socket[WSA_MAXIMUM_MAIT_EVENTS];
WSAEVENT Event[WSA_MAXIMUM_MAIT_EVENTS];
SOCKET  Accept, Listen;
DWORD   EventTotal = 0;
DWORD   Index;

//Set up a TCP socket for listening on port 5150;
Listen = socket(PF_INET, SOCKET_STREAM, 0);

InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = htonl(INADDR_ANY);
InternetAddr.sin_port = htons(5105);

bind(Listen, (PSOCKADDR)&InternetAddr, sizeof(InternetAddr));

NewEvent = WSACreateEvent();

WSAEventSelect(Listen, NewEvent, FD_ACCEP|FD_CLOSE);

listen(Listen, 5);

Socket[EventTotal] = Listen;
Event[EventTotal] = NewEvent;
EventTotal++;

while(TRUE)
{
    //Wait for network events on all sockets
    Index = WSAWaitForMultipleEvents(EventTotal, EventArray, FALSE, WSA_INFINITE, FALSE);
    
    WSAEnumNetworkEvents(SocketArray[Index - WSAWAIT_EVENT_0],EventArray[Index_WSA_WAIT_EVENT_0], &NetworkEvents);
    
    //check for FD_ACCEPT messages
    if(NetWorkEvents.lNetworkEvents&FD_ACCEPT)
     {
        if(NetworkEvent.iErrorCode[FD_ACCEPT_BIT]!=0)
         {
            printf("FD_ACCEPT failed with error%d/n", NetWorkEvent.iErrorCode[FD_ACCEPT_BIT]);
            break;
         }       
     
     //Accept a new connection and it to the socket and event lists
     Accept = accept(SocketArray[Index-WSA_WAIT_EVENT_0],NULL, NULL);
     
     //We can not process more than WSA_MAXIMUM_WAIT_EVENTS sockets, so close  the accepted socket
     if(EventTotal > WSA_MAXIMUM_WAIT_EVENTS)
     {
       printf("Too many connecttions");
       closesocket(Accept);
       break;
     }
     
     NewEvent = WSACreateEvent();
    
     WSAEventSelect(Accept, NewEvent, FD_READ|FD_WRITE|FD_CLOSE);
     
     Event[EventTotal] = NewEvent;
     Socket[EventTotal] = Accept;
     EventTotal++;
     
     printf("socket &d connected/n",ACCept);
   } 

    //Process FD_READ notification
    if(NewWorkEvent.iErrorCode[FD_READ_BIT]!0)
    {
       if(NetworkEvents.iErrorCode[FD_READ_BIT]!=0)
        {
          printf("FD_READ failed with error %dn", NetworkEvent.iErrorCode[FD_READ_BIT]);
          break;        
          
        }

        //Read data from socket
        recv(Socket[Index - WSA_WAIT_EVENT_0], buffer, sizeof(buffer),0);
    }

    //Process FD_WRITE notification
    if(NetworkEvents.lNetworkEvents&FD_WRITE)
    {
       if(NetworkEvent.iErrorCode[FD_WRITE_BIT]!=0)
       {
          printf("FD_WRITE failed with error %d/n",
          NetworkEvents.iErrorCode[FD_WRITE_BIT]);
          break;
       }
    
       send(Socket[Index - WSA_WAIT_EVENT_0], buffer, sizeof(buffer),0);         
    }
   
    if(NetworkEvents.lNetworkEvents&FD_CLOSE)
    {
        if(NetworkEvent.iErrorCode[FD_CLOSE_BIT]!=0)
        {
            printf("FD_CLOSE failed with error &d/n",
            NetworkEvents.iErrorCode[FD_CLOSE_BIT]);
            break;
        }
    }
     
    closesocket(Socket[index - WSA_WAIT_EVET_0]);
     
    //Remove socket and associated event from the socket and evnet array and decrement eventtatal
    CompressArrays(Event, socket,&EventTotal);
   }
}

第8章 WinsockIO方法
套接字模式:鎖定和非鎖定
8.1套接字模式
生產者-消費者
8.1.2非鎖定模式
SOCKET s;
unsigned long ul = 1;
int           nRet;

s = socket(AF_INET, SOCK_STREAM, 0);
nRet = ioctlsocket(s, FIOBIO, (unsigned long *)&ul);
if(nRet == SOCKET_ERROR)
{
....
}

8.2.1 select模型
利用這個函數,咱們判斷套接字上是否存在數據,或者可否向一個套接字寫入數據。
int select(int nfds,
           fd_set FAR* readfds,
           fd_set FAR* writefds,
           fd_set FAR* exceptfds,
           const struct timeval FAR* timeout);

FD_CLR(s,*set);從set中刪除套接字s。
FD_ISSET(s,*set);檢查s是否在set中。
FD_SET(s,*set);將s加入集合set。
FD_ZERO(*set);將set初始化。

SOCKET s;
fd_set fdread;
int    ret;

//create a socket and accept a connection

//Manage I/O on the socket
while(1)
{
   FD_ZERO(&fdread);
   
   FD_SET(s,&fdread);
   
   if((ret = select(0, &fdread, NULL, NULL, NULL)== SOCKET_ERROR)
   { //ERROR }
    
   if(ret >0)
   {
     if(FD_ISSER(s, &fdread))
     {
      //a read event has occurred on socket s.
     }
   }
   
}

第7章 Winsock基礎
7.1Winsock的初始化
int WSAStartup(WORD wVersionRequested, LPWSASATA lpWSData);
7.2錯誤檢查和控制
int WSAGetLastError(void);
7.3面向鏈接的協議
7.3.1服務器API函數
1.bind 將指定的套接字同一個已經知道的地址綁定在一塊兒。
2.listen(SOCKET s, int backlog);
3.accept和WSAAccept(SOCKET s, struct socket FAR*addr, int FAR* addrlen);
addr 返回客戶機的ip地址信息
accept返回一個新的套接字,它對應於已經接受的那個客戶機鏈接,對於該客戶機後續的全部操做,都使用這個新套接字。
原來的套接字仍然用於接受客戶機鏈接而處於監聽模式。

int CALLBACK ConditionFunc(
LPWSABUF lpCallerId,
LPWSABUF lpCallerData,
LPQQS    lpSOOS,
LPQOS    lpGOOS,
LPWSABUF lpCalleeId,
LPWSABUF lpCalleeData,
GROUP    FAR*g,
DWORD    dwCallbackData);

lpCallerId: 指定創建鏈接的協議 包含創建鏈接的那個客戶機的IP地址。
lpCallerData:包含了隨鏈接一道,由客戶機發出的任何鏈接數據。
lpSQOS,lpGQOS:對客戶機請求的任何一個服務質量參數進行指定。
lpCalleeId:結構中包含與客戶機須要與之鏈接的本地地址。
服務器將傳遞給條件的參數處理完以後,必須指出CF_ACCEPT, CF_REJECT , CF_DEFER.

 

3.1IP
網際協議:lan wan 是一個無鏈接的協議。
6.1.1TCP
Transmission Control Protocol,
6.1.2UDP
user Datagram Protocal
6.1.3定址
struct socketaddr_in
{
   short           sin_family;
   u_short         sin_port;
   struct in_addr  sin_addr;
   char            sin_zero[8];
};

端口分爲:已知端口 已註冊端口和動態端口
0~1023固定服務 
1024~49151已註冊端口供普通用戶進程使用
49152~65535動態端口
unsiged long inet_addr(const char FAR* cp);//把點式的ip地址轉換成一個32位的無符號的長整數。
1.特殊地址
INADDR_ANy 容許服務器應用監聽主機計算機上面每一個網絡接口上客戶機活動。通常狀況下,在該地址綁定套接字和本地接口時,網絡應用才利用這個地址來監聽鏈接。
INADDR_BROADCAST用於在一個IP網絡中發送廣播UDP數據報。
2.字節排序
u_long htonl               u_long ntohl
int WSAHtonl               int WSANtohl
u_short htons();           u_short ntohs
int WSAHtons();            int WSANtohs

SOCKADDR_IN InternetAddr;
INT nPortId = 5150;

InternetAddr.sin_family = AF_INET;
InternetAddr.sin_addr.s_addr = inet_addr("136.149.3.29");
InternetAddr.sin_port = htons(nPortId);
6.1.4建立套接字
s = socket(AF_INET, SOCK_STREAM,0);
s = WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WAS_FLAG_OVERLAPPED);
6.1.5名字解析
gethostbyname
WSAAsynGetHostByName
gethostbyaddr
WSAAsynGetHostByName
6.2紅外線套接字
IrSock
6.3IPX/SPX
"互聯網包交換"IPX

第二部分 WinsockAPI
第五章 網絡原理和協議
5.1協議的特徵
5.1.1 面向消息
對每一個離散寫命令來講,若是傳送協議把他們當作一條 獨立的消息在網上傳送,咱們就說該協議是面向協議的。在接收端接收數據時,返回的數據是發送端寫入的一條離散消息。這成爲:保護消息邊界。
無 保護消息邊界的協議成爲「基於流的協議」,這個術語經常使用來指代附加特性,流服務的定義是連續的數據傳輸;無論消息邊界是否存在,接收端都會盡可能地讀取有效 數據。對發送端來講 ,意味着容許將原始消息分解成小消息或把幾條消息積累在一塊兒,造成一個較大的數據包。對接收端來講,則是數據一到達網絡堆棧,網絡堆棧就開始讀取它,並將 它緩存下來等候進程處理。是否將各個對立的數據包累積在一塊兒受許多因素的影響,好比最大傳輸單元或Nagle算法。
僞流(pseudo-stream):使用的協議是基於消息的,發送的數據在各自獨立的數據包內,但在接收端能夠把這些消息緩存在一塊兒,這樣接收端即可以讀取任意大小的數據塊。
5.1.2面向鏈接和無鏈接
5.1.3可靠性與次序性 可靠性和次序性二者不能兼得。
51.4從容關閉 面向鏈接 TCP協議鏈接雙方都必須執行一次關閉以便徹底中斷鏈接。發送方FIN->接收方ACK->發送方
5.1.5廣播數據 即數據從一個工做站發出,局域網內的其餘全部工做站都能收到它。每臺機器都必須對該消息進行處理。網絡堆棧。
5.1.6多播數據 是指一個進程發送數據的能力,這些數據即將由一個或者多個接收端進行接收。多播要求對收發數據感興趣的全部主機加入一個特定的組,視頻會議。
5.1.7服務質量(Qos)實時視頻流式傳輸
5.1.8部分消息 大消息
5.1.9路由選擇的考慮
5.3winsock2協議信息
WSAEnumProtocals()
WSAStartup()
int WSACleanup()
SOCKET socket(int af, int type, int protocol);
原始套接字 容許你把其餘協議封裝在UDP數據包中,好比「互聯網控制消息協議」(ICMP)。ICMP的目的是投遞互聯網主機間的控制、錯誤和信息類型消息。
Winsock API和OSI模型
每一個傳輸協議都會提供一種傳輸數據的方法,但他們自己又是另外一個網絡協議的成員,而網絡協議位於網絡層,好比UDP和TCP就是傳輸協議但二者又都屬於因特網協議IP。
winsockAPI安裝在「會話層」和「傳輸層」之間。提供了一種爲制定傳輸層協議打開、計算和關閉會話的能力。

第四章 命名管道
是一種簡單的進程之間通訊的機制。
4.1實施細節
命名管道是圍繞Windows文件系統設計的一種技術,採用命名管道文件系統(Named Pipe File System NPFS)接口。
它依賴MSNP重定向器再網上進行命名管道數據的發送和接收。
4.1.1命名規範
//server/Pipe/[path]name 
4.1.2字節模式和消息模式
字節模式:在任何一個特定的時間段內,客戶服務器不知道有多少字節從管道讀入和寫入。
消息模式:每次在管道發送一條消息後,必須做爲一條完整的消息讀入。
4.1.3應用程序的編譯
winbase.h+kernel32.lib
4.1.4錯誤代碼
winerror.h
4.2客戶機服務器基礎
4.2.1服務器細節
1)CreateNamedPipe
2)ConnectNamedPipe監聽客戶機的鏈接
3)ReadFile WriteFile
4)DisconnectNamedPipe()
5)CloseHandle()

void main()
{
   HANDLE ThreadHandle;
   int i;
   DOWRD ThreadId;
   for(i=0; i<5;i++)
   {
      if((ThreadHandle = CreateThread(NULL, 0, PipeInstanceProc,
           NULL, 0, &ThreadId)) == NULL)
       {
         return;
       }
       CloseHandle(ThreadHandle)
      _getch();
   }
}

DWORD WINAPI PipeInstanceProc(LPVOID lpParameter)
{
  HANDLE PipeHandle;
  DWORD  BytesRead;
  DWORD  BytesWrite;
  CHAR   Buffer[256];

  if((PipeHandle = CreateNamedPiped("////.//PIPE//jim ",
     PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE|PIPE_READMODE_BYTE,
     NUM_PIPES,0,0,1000, NULL))==INVALID_HANDLE_VALUE)
   {return 0;}
   
  while(1)
  {
    if(ConnectNamedPipe(PipeHandle, NULL)==0)
    { break;}
  
    while(ReadFile(PipeHandle, Buffer, sizeof(Buffer),&BytesRead,NULL)>0)
    {
    }
    
    if(DisconnectNamedPipe(PipeHandle)==0)
    {return;}
    
    CloseHandle(PipeHandle);
   
  }
}
2.重疊IO
容許Win32API函數在發出IO以後以一部方式工做。
4.2.3客戶機細節
1)WaitNamedPipe
2)CreateFile
3)WriteFile&ReadFile
4)CloseHanlde
4.3OtherAPI
CallNamedPipe
TransactNamedPipe
SetNamedPipeHandleState
GetNamedPipeHandleState

進程間通訊,經過槽客戶機進程可將消息或廣播給一個或多個服務器進程。在同一臺機器的不一樣進程之間
,或跨越整個網絡的不一樣計算機進程之間,協助進行消息的傳輸,客戶服務器模式單向消息傳遞。
3.1郵槽的細節
郵槽必須依賴windows重定向器,經過一個郵槽文件系統(Mailslot File System,MSFS),來建立及標識郵槽。
消息一般是無鏈接的傳輸方式發送,但亦可強迫windows重定定向器在windows NT和windows 2000上使用面向鏈接的
的方式傳輸。
3.1.1郵槽的名字
//server/Mailslot/[path]name 
3.1.2 消息的長度
3.1.3 應用程序的編譯
windows.h/winbase.h & Kernel32.lib
3.2基本客戶機/服務器
3.2.1郵槽服務器
1) CreateMailslot()
2) ReadFile()//在默認狀況下,處於暫停狀態,直到有數據能夠讀入爲止。
3) CloseFile()
3.2.2客戶機
1) CreateFile()針對郵槽打開指向它的的一個引用句柄。
2) WriteFile()
3) CloseFile()

windows使應用程序能經過操做系統內建的文件系統服務在網絡上通訊。「網絡操做系統」。若應用程序但願訪問本地系統中的文件,須要依賴操做系統來知足
I/O請求。咱們稱之爲本地I/O。例如,在一個應用程序打開或關閉文件時, 須要由操做系統來決定如何訪問包含了制定文件內容的一個設備。找到設備後,
I/O請求會被轉發給一個本地設備驅動程序。經過網絡來訪問一個設備也是一樣。然而,IO請求必須經過網絡會被轉發給對應的遠程設備。咱們稱之爲
「IO重定向(IO redirection)」.
如何將普通的IO請求重定向到遠程設備。
通用命名規範 Universal Naming Convention UNC
多UNC提供者(Multiple UNC Provider)MUP
2.1 通用命名規範
它最大的特色是沒必要制定或引用一個已映射到遠程文件系統的本地驅動器字母。「與驅動器字母無關」。它的格式:
//[服務器]/[共享名]/[路徑]
2.2MUP
網絡提供者Network Provider,其實就是一種服務,可經過網絡硬件訪問位於一臺遠程計算機上的資源(如文件和打印機)。網絡
提供者如「Microsoft網絡用戶」。還有例如Novell公司的Novell Client v3.10 for Windows 95/98.
MUP的基本任務是決定具體由哪一個網絡提供者來知足一個UNC請求。爲做出這個決定,MUP需將請求中提到的UNC名字發給已經安裝好的每個提供者
(以並行方式)。若某個網絡提供這代表本身可以提供UNC名字牽涉到的那一種服務,MUP便會將請求中剩餘的部分發給他。若是有多個提供者都表示可以服務一個UNC
請求,MUP便會根據優先級挑選最恰當的一個提供者。
2.3網絡提供者
網絡提供者只是一種服務,經過網絡硬件來訪問位於遠程計算機上的共享資源,好比文件和打印機。這正是網絡操做系統的一種核心功能。網絡
提供者具有最主要的功能之一即是將本地磁盤標識符如(E)重定向至遠程機器上的一個磁盤目錄。
2.4重定向器簡介
重定向器由網絡提供者展現給用於接收和處理遠程IO服務請求的操做系統。要作到這一點他須要格式化服務請求消息,再將其發給遠程計算機的
重定向器服務器服務。遠程機器的重定向器服務器收到這個請求以後,會發出本地IO請求的方式,來知足這一請求。
MSNP提供了一個特殊的重定向器,可直接與網絡傳輸層和NetBIOS打交道,以便在客戶機與服務器之間創建通訊。
2.5服務器消息塊
SMB:包含三個基本組件:命令代碼、命令特有的和用戶數據。
SMB協議採用很是簡單的「客戶機請求/服務器相應」傳輸模型。MSNP重定向器建立一個SMB結合時,須要再命令代碼字段中指定一個
特定的請求。若命令要求的是發送數據,好比寫指令,數據便會隨結構一道傳送出去。隨後,SMB結構會經過一種想TCP/IP這樣的傳輸協議傳給一個遠程工做站的服務器服務。
遠程工做站的服務器服務會對收到的客戶機請求進行處理,而後將一個SMB相應數據結構傳回客戶機。
WINS:windows互聯網命名服務器。
NBTstat-n 查看本機已經註冊的NetBIOS名字列表。
windows最有特點的一個網絡提供者稱爲「microsoft網絡用戶」,之前叫作「Microsoft網絡提供者(MSNP)」
2.6 安全問題
本地的安全。訪問權限:讀、寫、執行。windows NT和windows2000是經過安全描述符和訪問令牌來實現安全的。

第一部分傳統網絡API
NetBIOS:和winsock相似,也是一種與協議無關的網絡API。他提供了異步調用,同時兼容OS/二、DOS等操做系統。
重定向器:提供了與傳輸無關的文件輸入輸出方式。
郵槽:是一種簡單的接口,可在windows機器之間實現廣播和單向數據通訊。
命名管道:可創建一種雙向信道。提供了對windows安全通訊的支持。

第一章 NetBIOS(Newwork Basic Input/Output System)
OSI(開放系統互連)網絡模型
應用層     爲用戶提供相應的界面,以便使用提供的聯網功能
表示層     完成數據的格式化
會話層     控制兩個主機間的通訊鏈路(開放、操做和關閉)
傳輸層     提供數據傳輸服務(可靠與不可靠)
網絡層     在兩個主機之間提供一套定址/尋址機制,同時負責數據包的路由選擇
數據鏈路層 控制連個主機間的物理通訊鏈路:同時還要負責對數據整形,以便在物理媒體上傳輸
物理層     物理媒體負責以一系列電子信號的形式傳出數據
NetBIOS主要在會話和傳輸層發揮做用。


1.1.1 LANA(LAN adapter)編號 每張物理網卡被分配的獨一無二的值,一般在0-9之間。每一個LANA編號對應於網卡和傳輸協議的惟一組合。
1.1.2 NetBIOS名字 對一個進程來講,他會註冊本身但願與其通訊的每一個LANA編號。一個NetBIOS名字長度爲16個字符。 在Win32環境中,針對每一個可用的LANA編號,每一個進程都會
爲其維持一張BetBIOS名字表。
1.1.3 NetBIOS特性:同時提供面向連接和無連接服務。
1.2 NetBIOS編程基礎
UCHAR Netbios(PNCB pNCB);//NCB 網絡控制塊
Nb30.h , Netapi32.lib
1.3 常規BetBIOS例程

CDragListBox
In addition to proving the functionality of a windows list box, the CDragListBox class allows
the user to move list box items.


修改socket屬性
int TimeOut=6000; //設置發送超時6秒
if(::setsockopt(sock,SOL_SOCKET,SO_SNDTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
   ::closesocket (sock);
   continue;
}


TimeOut=6000;//設置接收超時6秒
if(::setsockopt(sock,SOL_SOCKET,SO_RCVTIMEO,(char *)&TimeOut,sizeof(TimeOut))==SOCKET_ERROR)
{
  ::closesocket (sock);
  continue;
}

//設置非阻塞方式鏈接
unsigned long ul = 1;
ret = ioctlsocket(sock, FIONBIO, (unsigned long*)&ul);
if(ret==SOCKET_ERROR)
{
   ::closesocket (sock);
   continue;
}

重疊模型就是讓應用程序使用重疊數據結構(WSAOVERLAPPED),一次投遞一個或者多個winsock i/0請求。針對這些請求,在他們完成以後,應用程序會收到通知,因而就能夠用本身的代碼來處理這些數據了。
有兩個方法來管理重疊i/o請求的完成狀況(就是說接到重疊操做完成的通知): 時間通知 和 完成例程。
基 於事件通知:就是把windows事件和WSAOVERLAPPED結構關聯在一塊兒。使用WSASend, WSASendto,WSARecvFrom。能夠把重疊結構與這些函數綁定在一塊兒,提交請求,其餘的事情就交給重疊結構去操心了。這樣咱們就能夠坐享其 成了,等到重疊結構完成以後,天然會有與之對應的事件來通知咱們。咱們根據重疊操做的結果取得咱們須要的數據。
1。WSAOVERLAPPED
WSAEVENT event;
WSAOVERLAPPED AcceptOverlapped;
event = WSACreateEvent();
ZeroMemory(&Acceptoverlapped, sizeof(WSAOVERLAPPED));
AccepOverlapped.hEvent = event;
2. WSARecv系列函數

SOCKET S;
WSABUF DataBuf;
char buffer[5095];
ZeroMemorry(buffer,5095);
DataBuf.len = 5095;

DWORD dwBufferCount = 1, dwRecvBytes = 0, Flags = 0;
WSAOVERLAPPED AcceptOVerlapped;
WSAEVENT event;
event = WSACreateEvent();
ZeroMemory(&Acceptoverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = event;

WSARecv(s, &DataBuf, dwBufferCount,&dwRecvBytes, &Flags, &AcceptOverlapped,NULL);

3.WSAWaitForMultipleEvents
等待事件的觸發。

4.WSAGetOverlappedResult
查詢重疊操做的結果。


[1] 定義變量
#define DATA_BUFSIZE 4096

SOCKET ListenSocket;//監聽套節字
Socket AcceptSocket;//與客戶端通訊的套節字
WSAOVERLAPPED AcceptOverlapped;//重疊結構
WSAEVENT EventArray[WSA_MAXIMUM_WAIT_EVENTS];//用來通知重疊操做完成的事件句柄數組
WSABUF DataBuf[WSA_MAXIMUM_WAIT_EVENTS];
DWORD dwEventTotal = 0;//事件總數
DWORD dwRecvBytes = 0;//接收的字符長度
Flags= 0 ;
[2] 建立套節字並在指定的端口上監聽鏈接請求。
WSADATA wsaData;
WSAStartup();

ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

SOCKADDR_IN ServerAddr;
serverAddr.sin.family = AF_INET;
serverAddr.sin.addr.S_un.S_addr = htonl(INNADDR_ANY);
serverAddr.sin_port = htons(11111);

bind(ListenSocket, &serveraddr, sizeof());

listen(Listensocket,5);
[3] 接收一個入站的鏈接請求。
SOCKADDR_IN clientAddr;
int addr_length = sizeof(clientAddr);
AcceptSocket= accept(ListenSocket, &clientsocket, &addr_length);
LPCTSTR lpIP = inet_ntoa(clientAddr.sin_addr);//client ip
UINT nPort = clientAddr.sin_port;//client port

[4]創建並初始化重疊結構

EventArray[dwEventTotal] = WSACreateEvent;
ZeroMemory(&Acceptoverlapped, sizeof(WSAOVERLAPPED));
AcceptOverlapped.hEvent = EventArray[dwEventTotal];//關聯事件

char buffer[DATA_BUFSIZE];
ZeroMemory(buffer, DATA_BUFSIZE);

DataBuffer.len = DATA_BUFFER;//初始化一個WSABUF結構
DataBuffer.buf = buffer;

dwEventTotal++;
[5] 以WSAOVERLAPPED爲參數,在套節字上投遞WSARecv請求。

 

http://blog.csdn.net/tjb_1216/article/details/4628957

相關文章
相關標籤/搜索