关于HTTP服务器的访问流程及基本的工作原理,我已经在上一篇博文中简单叙述过。详见:http://www.cnblogs.com/suvllian/articles/5366341.html
这次主要说一下如何实现一个简单的HTTP服务器程序。
源代码地址:http://www.oschina.net/code/snippet_2685955_55369
主要函数如下:
bool InitSocket(); DWORD WINAPI AcceptThread(LPVOID lpParam); DWORD WINAPI ClientThread(LPVOID lpParam); bool IoComplete(char* szRequest); //数据包的校验函数 bool AddClientList(SOCKET s,sockaddr_in addr); bool AddThreadList(HANDLE hThread,DWORD ThreadID); bool ParseRequest(char* szRequest, char* szResponse, BOOL &bKeepAlive);
主函数如下:
if (!InitSocket()) { printf("InitSocket Error\n"); return; } GetCurrentDirectory(512,HtmlDir); strcat(HtmlDir,"\\HTML\\"); strcat(HtmlDir,FileName); //启动一个接受线程 HANDLE hAcceptThread = CreateThread(NULL,0,AcceptThread,NULL,0,NULL); //在这里我们使用事件模型来实现我们的Web服务器 //创建一个事件 WaitForSingleObject(hAcceptThread,INFINITE);
其中有几个函数:
1、GetCurrentDirectory:获取当前进程的当前目录
DWORD WINAPI GetCurrentDirectory(
  __in   DWORD nBufferLength,
  __out  LPTSTR lpBuffer
);
DWORD WINAPI GetModuleFileName(
    _In_opt_  HMODULE hModule,
    _Out_     LPTSTR lpFilename,
    _In_      DWORD nSize
);
返回值:Long,如执行成功,返回复制到lpFileName的实际字符数量;零表示失败。使用GetLastError可以打印错误信息。
参数说明:hModule Long:一个模块的句柄,用于标记这个文件资源。可以是一个DLL模块,或者是一个应用程序的实例句柄。如果该参数为NULL,该函数返回该应用程序全路径。
lpFileName String:指定一个字串缓冲区,要在其中容纳文件的用NULL字符中止的路径名,hModule模块就是从这个文件装载进来的。
nSize Long:装载到缓冲区lpFileName的最大字符数量。
头文件:windows.h
BOOL CreateSampleService() { TCHAR szPath[MAX_PATH]; if( !GetModuleFileName( NULL, szPath, MAX_PATH ) ) { printf("GetModuleFileName failed (%d)\n", GetLastError()); return FALSE; } return TRUE; }
3、CreatTherad函数:在主线程的基础上创建一个新线程
  CreateThread将在主线程的基础上创建一个新线程,大致做如下步骤:
  1、在内核对象中分配一个线程标识/句柄,可供管理,由CreateThread返回。
  2、把线程退出码置为STILL_ACTIVE,把线程挂起计数置1。
  3、分配context结构。
  4、分配两页的物理存储以准备栈,保护页设置为PAGE_READWRITE,第2页设为PAGE_GUARD。
  5、lpStartAddr和lpvThread值被放在栈顶,使它们成为传送给StartOfThread的参数。
  6、把context结构的栈指针指向栈顶(第5步)指令指针指向startOfThread函数。
语法:hThread = CreateThread(&security_attributes, dwStackSize, ThreadProc,pParam, dwFlags, &idThread) ;
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, //安全设置 DWORD dwStackSize, //堆栈大小 LPTHREAD_START_ROUTINE lpStartAddress, //入口函数 LPVOID lpParameter, // 函数参数 DWORD dwCreationFlags, // 启动选项 LPDWORD lpThreadId //输出线程ID );
   参数说明:lpThreadAttributes:指向SECURITY_ATTRIBUTES型态的结构的指针。在Windows 98中忽略该参数。在Windows NT中,它被设为NULL。 
dwStackSize:是用于新线程的初始堆栈大小,默认值为0。如果为0,那么默认将使用与调用该函数的线程相同的栈空间大小。在任何情况下,Windows根据需要动态延长堆栈的大小。
lpStartAddress:是指向线程函数的指针。函数名称没有限制,但是必须以下列形式声明:DWORD WINAPI ThreadProc (PVOID pParam) ;
lpParameter:向线程函数传递的参数,是一个指向结构的指针,这样主线程和从属线程就可以共享数据。不需传递参数时,为NULL。
dwCreationFlags:通常为0,表示线程马上执行。当建立的线程不马上执行时为旗标CREATE_SUSPENDED。线程将暂停直到呼叫ResumeThread来恢复线程的执行为止。
lpThreadId:保存新线程的ID。
返回值:函数成功,返回线程句柄;函数失败返回FALSE。若不想返回线程ID,设置lpThreadId值为NULL。
4、WSAStratup函数:WSA(Windows异步套接字)的启动命令。
int WSAStartup(WORD wVersionRequested,LPWSADATA lpWSAData); 
使用Socket的程序在使用Socket之前必须调用WSAStartup函数。
第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;
第二个参数返回请求的Socket的版本信息。
当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。
  返回值:为0时,成功。
  否则返回下列的错误代码之一。
  WSASYSNOTREADY 指出网络通信依赖的网络子系统还没有准备好。
WSAVERNOTSUPPORTED 所需的Windows Sockets API的版本未由特定的Windows Sockets实现提供。
WSAEINVAL 应用程序指出的Windows Sockets版本不被该DLL支持。
5、WSACleanup函数:int WSACleanup (void)
应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
返回值:为0时,操作成功。
WORD MAKEWORD( BYTE bLow, //指定新变量的低字节序; BYTE bHigh //指定新变量的高字节序; );
返回值:一个无符号16位整形数。
7、CreateEvent函数:创建或打开一个命名的或无名的事件对象
HANDLECreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, // 安全属性 BOOL bManualReset, // 复位方式 BOOL bInitialState, // 初始状态 LPCTSTR lpName // 对象名称 );
返回值:如果函数调用成功,函数返回事件对象的句柄。
如果对于命名的对象,在函数调用前已经被创建,函数将返回存在的事件对象的句柄,而且在GetLastError函数中返回ERROR_ALREADY_EXISTS。
如果函数失败,函数返回值为NULL,如果需要获得详细的错误信息,需要调用GetLastError。
参数:lpEventAttributes:一个指向SECURITY_ATTRIBUTES结构的指针,确定返回的句柄是否可被子进程继承。如果lpEventAttributes是NULL,此句柄不能被继承,事件将获得一个默认的安全符。
8、WaitForSingleObject函数
DWORD WINAPI WaitForSingleObject(
__in HANDLE hHandle,
__in DWORD dwMilliseconds
);
参数:hHandle:一个事件的句柄。
dwMilliseconds:时间间隔。如果事件是有信号状态返回WAIT_OBJECT_0,如果时间超过dwMilliseconds值但时间事件还是无信号状态则返回WAIT_TIMEOUT。
WaitForSingleObject函数用来检测hHandle事件的信号状态,在某一线程中调用该函数时,线程暂时挂起,如果在挂起的 dwMilliseconds毫秒内,线程所等待的对象变为有信号状态,则该函数立即返回;如果超时时间已经到达dwMilliseconds毫秒,但 hHandle所指向的对象还没有变成有信号状态,函数照样返回。
参数dwMilliseconds有两个具有特殊意义的值:0和INFINITE。若为 0,则该函数立即返回;若为INFINITE,则线程一直被挂起,直到hHandle所指向的对象变为有信号状态时为止。
9、WaitForMultipleObjects函数
DWORD WaitForMultipleObjects(DWORD nCount,const HANDLE* lpHandles,BOOL bWaitAll,DWORD dwMilliseconds);
参数:nCount:句柄的数量,最大值为MAXIMUM_WAIT_OBJECTS(64)。
lpHandles:句柄数组的指针。
HANDLE 类型可以为(Event,Mutex,Process,Thread,Semaphore )数组
bWaitAll:等待的类型,如果为TRUE则等待所有信号量有效在往下执行,FALSE当有其中一个信号量有效时就向下执行
dwMilliseconds:超时时间,超时向后执行。如果为WSA_INFINITE 永不超时。如果没有信号量就会在这死等。
返回值:如果因时间终了而返回,那返回值是WAIT_TIMEOUT。
如果bWaitAll是TRUE,那么返回值是WAIT_OBJECT_0。
如果bWaitAll是FALSE,那么返回值减去WAIT_OBJECT_0,就表示哪一个handle被激发了。
如果函数失败,则返回WAIT_FAILD,这时候你可以用GetLastError()找出失败原。
10、CloseHandle函数
BOOL CloseHandle(HANDLE hObject);
返回值:Long,非零表示成功。零表示失败。会设置GetLastError。
函数作用:关闭一个内核对象。其中包括文件、文件映射、进程、线程、安全和同步对象等。在CreateThread成功之后会返回一个hThread的handle,且内核对象的计数加1,CloseHandle之后,引用计数减1,当变为0时,系统删除内核对象。
若在线程执行完之后,没有调用CloseHandle,在进程执行期间,将会造成内核对象的泄露,相当于句柄泄露,但不同于内存泄露,这势必会对系统的效率带来一定程度上的负面影响。但当进程结束退出后,系统会自动清理这些资源。
11、WSAEnumNetworkEvents()函数:检测所指定的套接口上网络事件的发生。
int WSAAPI WSAEnumNetworkEvents ( SOCKET s,WSAEVENT hEventObject, LPWSANETWORKEVENTS lpNetworkEvents, LPINT lpiCount);
参数:s:标识套接口的描述字。
hEventObject:(可选)句柄,用于标识需要复位的相应事件对象。
lpNetworkEvents:一个WSANETWORKEVENTS结构的数组,每一个元素记录了一个网络事件和相应的错误代码。
lpiCount:数组中的元素数目。在返回时,本参数表示数组中的实际元素数目;如果返回值是WSAENOBUFS,则表示为获取所有网络事件所需的元素数目。
返回值:如果操作成功则返回0。否则的话,将返回SOCKET_ERROR错误,应用程序可通过WSAGetLastError()来获取相应的错误代码。
12、ResetEvent函数:指定的事件对象设置为无信号状态。
BOOL ResetEvent(HANDLE hEvent);
返回值:函数成功,返回非0值,否则返回0值,可以调用GetLastError得到错误的详细信息。
 
                    
                 
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号