发信人: richwxy(风吟) 
整理人: wenbobo(2002-05-17 18:00:32), 站内信件
 | 
 
 
Y()类函数 
 . 这些函数是对BSD标准函数的扩充.函数WSACancelAsyncRequest()允许用户中止一个正 
 在执行的异步请求. 
         3.阻塞处理方法 
                 WINSOCK提供了"钩子函数"负责处理Windows消息,使Windows的消息循环能 
 够继续.WIN 
 SOCK提供了两个函数(WSASetBlockingHook()和WSAUnhookBlockingHook())让应用程序设 
 置或取消自己的"钩子函数".函数WSAIsBlocking()可以检测是否阻塞,函数WSACancelBl 
 ockingCall()可以取消一个阻塞的调用. 
         4.错误处理 
                 WINSOCK提供了两个WSAGetLastError()和WSASetLastError()来获取和设 
 置最近错误号 
 . 
         5.启动和终止 
                 由于Windows Sockets的服务是以动态连接库WINSOCK.DLL形式实现的,所 
 以必须要先调 
 用WSAStartup()函数对Windows Sockets DLL进行初始化,协商WINSOCK的版本支持,并分 
 配必要的资源.在应用程序关闭套接口后,还应调用WSACleanup()终止对Windows Socket 
 s DLL的使用,并释放资源,以备下一次使用. 
         在这些函数中,实现Windows网络实时通信的关键是异步选择函数WSAAsyncSelect() 
 的使 
 用. 用法及详细说明参见第5.3.7. 
 3.3 Windows Sockets与UNIX套接口编程实例 
         下面是一个简单的基于连接的点对点实时通信程序.它由两部分组成,服务器在主机 
 UNI 
 X下直接运行, 客户机在Windows下运行. 
 3.3.1 SERVER介绍 
         由于SERVER是在UNIX下运行的,它对套接口的使用都是BSD的标准函数,程序也比较 
 简单 
 , 只有一段程序,下面简要解释一下. 
         首先,建立自己的套接口.在互连网的进程通信中,全局标识一个进程需要一个被称 
 为"半 
 相关"的三元组(协议,本地主机地址,本地端口号)来描述,而一个完整的进程通信实例则 
 需要一个被称为"相关"的五元组(协议, 本地主机地址,本地端口号,远端主机地址,远端 
 端口号)来描述. 
         s=socket(AF_INET, SOCK_STREAM, 0) 
         该函数建立指定地址格式,数据类型和协议下的套接口,地址格式为AF_INET(唯一支 
 持的 
 格式),数据类型SOCK_STREAM表示建立流式套接口,参数三为0,即协议缺省. 
         bind(s, (struct sockaddr *)&server, sizeof(server)) 
         该函数将建立服务器本地的半相关,其中,server是sockaddr_in结构,其成员描述了 
 本地 
 端口号和本地主机地址,经过bind()将服务器进程在网上标识出来. 
         然后,建立连接.先是调用listen()函数表示开始侦听.再通过accept()调用等待接 
 收连 
 接. 
         listen(s,1)表示连接请求队列长度为1,即只允许有一个请求,若有多个请求,则出 
 现错 
 误,给出错误代码WSAECONNREFUSED. 
         ns = accept(s, (struct sockaddr *)&client, &namelen)) 
         accept()阻塞(缺省)等待请求队列中的请求,一旦有连接请求来,该函数就建立一个 
 和s 
 有相同属性的新的套接口.client也是一个sockaddr_in结构,连接建立时填入请求连接的 
 套接口的半相关信息. 
         接下来,就可以接收和发送数据了. 
         recv(ns,buf,1024,0) 
         send(ns,buf,pktlen,0) 
         上面两个函数分别负责接收和发送数据,recv从ns(建立连接的套接口)接收数据放 
 入bu 
 f中,send则将buf中数据发送给ns.至于第四个参数,表示该函数调用方式,可选择MSG_DO 
 NTROUTE和MSG_OOB, 0表示缺省. 
         最后,关闭套接口. 
         close(ns); 
         close(s); 
 3.3.2 CLIENT介绍 
         客户端是在Windows上运行的,使用了一些Windows Sockets的扩展函数,稍微复杂一 
 些. 
 包括了.RC和.C两个文件,其中的主窗口函数ClientProc()是程序的主要部分,下面简单解 
 释一下. 
         首先,是在WinMain()中建立好窗口后,即向主窗口函数发一条自定义的WM_USER消息 
 , 做 
 相关的准备工作.在主窗口函数中,一接收到WM_USER消息,首先调用WSAStartup()函数初 
 始化Windows Sockets DLL,并检查版本号.如下: 
         Status = WSAStartup(VersionReqd, lpmyWSAData); 
         其中,VersionReqd描述了WINSOCK的版本(这里为1.1版),lpmyWSAData指向一个WSAD 
 ATA 
 结构,该结构描述了Windows Sockets的实现细节. 
         WSAStartup()之后,进程通过主机名(运行时命令行参数传入)获取主机地址,如下: 
         hostaddr = gethostbyname(server_address); 
         hostaddr指向hostent结构,内容参见5.2.1. 
         然后,进程就不断地消息循环,等待用户通过菜单选择"启动".这时,通过调用Client 
 ()来 
 启动套接口.在Client()中,首先也是调用socket()来建立套接口.如下: 
         if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 
         { 
                 AlertUser(hWnd, "Socket Failed"); 
                 return (FALSE); 
         } 
         紧接着,调用WSAAsyncSelect()函数提名FD_CONNECT网络事件,如下: 
         if (!SetSelect(hWnd, FD_CONNECT)) 
                 return (FALSE); 
         SetSelect()主要就是调用WSAASyncSelect(),让Windows Sockets DLL在侦测到连 
 接建 
 立时,就发送一条UM_SOCK的自定义消息,使消息循环继续下去.如下: 
         BOOL SetSelect(HWND hWnd, long lEvent) 
         { 
                 if (WSAAsyncSelect(s, hWnd, UM_SOCK, lEvent) == SOCKET_ERROR) 
                 { 
                         AlertUser(hWnd, "WSAAsyncSelect Failure."); 
                         return (FALSE); 
                 } 
                 return (TRUE); 
         } 
         为建立连接,必须马上调用connect()如下,由于先调用了WSAASyncSelect(),connec 
 t() 
 便是非阻塞调用.进程发出连接请求后就不管了,当连接建立好后,WINSOCK DLL自动发一 
 条消息给主窗口函数,以使程序运行下去. 
         connect(s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr)); 
         窗口函数在收到UM_SOCK消息后,判断是由哪个网络事件引起的,第一次,必然是由连 
 接事 
 件引起的,这样,就会执行相应的程序段,同样调用SetSelect()来提名FD_WRITE事件.希望 
 在套接口可发送数据时接到消息.在收到FD_WRITE消息时,先调用send()发送数据,再调用 
 SetSelect()来提名FD_READ事件, 希望在套接口可接收数据是接到消息.在收到FD_READ 
 消息时,先调用recv()来接收数据再提名FD_WRITE事件,如此循环下去.直到发生连接关闭 
 的事件FD_CLOSE,这时就调用WSAAsyncSelect(s,hWnd,0,0)来停止异步选择.在窗口函数 
 接到WM_DESTROY消息时(即关闭窗口之前),先调用closesocket()(作用同UNIX 中的clos 
 e())来关闭套接口,再调用WSACleanup()终止Windows Sockets DLL,并释放资源. 
 3.3.3 源程序清单 
 程序1:CLIENT.RC 
 ClientMenu MENU 
 BEGIN 
         POPUP "&Server" 
         BEGIN 
                 MENUITEM "&Start...", 101 
                 MENUITEM "&Exit",  102 
         END 
 END 
 程序2:CLIENT.C 
 #define USERPORT 10001 
 #define IDM_START 101 
 #define IDM_EXIT  102 
 #define UM_SOCK WM_USER + 0X100 
 #include <alloc.h> 
 #include <mem.h> 
 #include <windows.h> 
 #include <winsock.h> 
 #define MAJOR_VERSION 1 
 #define MINOR_VERSION 2 
 #define WSA_MAKEWORD(x,y)  ((y)*256+(x)) 
 HANDLE hInst; 
 char server_address[256] = {0}; 
 char buffer[1024]; 
 char FAR * lpBuffer = &buffer[0]; 
 SOCKET s = 0; 
 struct sockaddr_in dst_addr; 
 struct hostent far *hostaddr; 
 struct hostent hostnm; 
 struct servent far *sp; 
 int count = 0; 
 BOOL InitApplication(HINSTANCE hInstance); 
 long FAR PASCAL ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lP 
 aram); 
 void AlertUser(HWND hWnd, char *message); 
 BOOL Client(HWND hWnd); 
 BOOL ReceivePacket(HWND hWnd); 
 BOOL SetSelect(HWND hWnd, long lEvent); 
 BOOL SendPacket(HWND hWnd, int len); 
 int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, 
 int nCmdShow) 
 { 
         HWND hWnd; 
         MSG msg; 
         lstrcpy((LPSTR)server_address, lpCmdLine); 
         if (!hPrevInstance) 
                 if (!InitApplication(hInstance)) 
                         return (FALSE); 
         hInst = hInstance; 
         hWnd = CreateWindow("ClientClass", "Windows ECHO Client", WS_OVERLAPPEDW 
 IND 
 OW,\ 
                 CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL 
 , NULL,\ 
                 hInstance, NULL); 
         if (!hWnd) 
                 return (FALSE); 
         ShowWindow(hWnd, nCmdShow); 
         UpdateWindow(hWnd); 
         PostMessage(hWnd, WM_USER, (WPARAM)0, (LPARAM)0); 
         while (GetMessage(&msg, NULL, NULL, NULL)) 
         { 
                 TranslateMessage(&msg); 
                 DispatchMessage(&msg); 
         } 
         return (msg.wParam); 
 } 
 BOOL InitApplication(HINSTANCE hInstance) 
 { 
     WNDCLASS WndClass; 
         char *szAppName = "ClientClass"; 
     // fill in window class information 
         WndClass.lpszClassName = (LPSTR)szAppName; 
         WndClass.hInstance     = hInstance; 
         WndClass.lpfnWndProc   = ClientProc; 
         WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW); 
         WndClass.hIcon         = LoadIcon(hInstance, NULL); 
         WndClass.lpszMenuName  = "ClientMenu"; 
         WndClass.hbrBackground = GetStockObject(WHITE_BRUSH); 
         WndClass.style         = CS_HREDRAW | CS_VREDRAW; 
         WndClass.cbClsExtra    = 0; 
         WndClass.cbWndExtra    = 0; 
     // register the class 
     if (!RegisterClass(&WndClass)) 
                 return(FALSE); 
     return(TRUE); 
 } 
 long FAR PASCAL ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lP 
 aram) 
 { 
         int length, i; 
         WSADATA wsaData; 
         int Status; 
         switch (message) 
         { 
                 case WM_USER: 
                 { 
                         WORD    wMajorVersion, wMinorVersion; 
                         LPWSADATA       lpmyWSAData; 
                         WORD            VersionReqd; 
                         int                     ret; 
                         wMajorVersion = MAJOR_VERSION; 
                         wMinorVersion = MINOR_VERSION; 
                         VersionReqd = WSA_MAKEWORD(wMajorVersion,wMinorVersion); 
   
                         lpmyWSAData = (LPWSADATA)malloc(sizeof(WSADATA)); 
                         Status = WSAStartup(VersionReqd, lpmyWSAData); 
                         if (Status != 0) 
                         { 
                                 AlertUser(hWnd, "WSAStartup() failed\n"); 
                                 PostQuitMessage(0); 
                         } 
                         hostaddr = gethostbyname(server_address); 
                         if (hostaddr == NULL) 
                         { 
                                 AlertUser(hWnd, "gethostbyname ERROR!\n"); 
                                 WSACleanup(); 
                                 PostQuitMessage(0); 
                         } 
                         _fmemcpy(&hostnm, hostaddr, sizeof(struct hostent)); 
                 } 
                         break; 
                 case WM_COMMAND: 
                         switch (wParam) 
                         { 
                                 case IDM_START: 
                                         if (!Client(hWnd)) 
                                         { 
                                                 closesocket(s); 
                                                 AlertUser(hWnd, "Start Failed"); 
                                         } 
                                         break; 
                                 case IDM_EXIT: 
 //                                      WSACleanup(); 
                                         PostQuitMessage(0); 
                                         break; 
                         } 
                         break; 
                 case UM_SOCK: 
                         switch (lParam) 
                         { 
                                 case FD_CONNECT: 
                                         if (!SetSelect(hWnd, FD_WRITE)) 
                                                 closesocket(s); 
                                         break; 
                                 case FD_READ: 
                                         if (!ReceivePacket(hWnd)) 
                                         { 
                                                 AlertUser(hWnd, "Receive Packet 
 Failed.\n"); 
                                                 closesocket(s); 
                                                 break; 
                                         } 
                                         if (!SetSelect(hWnd, FD_WRITE)) 
                                                 closesocket(s); 
                                         break; 
                                 case FD_WRITE: 
                                         for (i = 0; i < 1024; i ++) 
                                                 buffer[i] = (char)'A' + i % 26; 
                                         length = 1024; 
                                         if (!SendPacket(hWnd, length)) 
                                         { 
                                                 AlertUser(hWnd, "Packet Send Fai 
 led!\n"); 
                                                 closesocket(s); 
                                                 break; 
                                         } 
                                         if (!SetSelect(hWnd, FD_READ)) 
                                                 closesocket(s); 
                                         break; 
                                 case FD_CLOSE: 
                                         if (WSAAsyncSelect(s, hWnd, 0, 0) == SOC 
 KET_ERROR) 
                                                 AlertUser(hWnd, "WSAAsyncSelect 
 Failed.\n"); 
                                         break; 
                                 default: 
                                         if (WSAGETSELECTERROR(lParam) != 0) 
                                         { 
                                                 AlertUser(hWnd, "Socket Report F 
 ailure."); 
                                                 closesocket(s); 
                                                 break; 
                                         } 
                                         break; 
             } 
                         break; 
                 case WM_DESTROY: 
                         closesocket(s); 
                         WSACleanup(); 
                         PostQuitMessage(0); 
                         break; 
                 default: 
                         return (DefWindowProc(hWnd, message, wParam, lParam)); 
         } 
         return(NULL); 
 } 
 void AlertUser(HWND hWnd, char *message) 
 { 
         MessageBox(hWnd, (LPSTR)message, "Warning", MB_ICONEXCLAMATION); 
         return; 
 } 
 BOOL Client(HWND hWnd) 
 { 
         memset(&dst_addr,'\0', sizeof (struct sockaddr_in)); 
         _fmemcpy((char  FAR *)&dst_addr.sin_addr,(char  FAR *)hostnm.h_addr,host 
 nm. 
 h_length); 
         dst_addr.sin_family = hostnm.h_addrtype; 
         dst_addr.sin_port = htons(USERPORT); 
         if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) 
         { 
                 AlertUser(hWnd, "Socket Failed"); 
                 return (FALSE); 
         } 
         if (!SetSelect(hWnd, FD_CONNECT)) 
                 return (FALSE); 
         connect(s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr)); 
         return (TRUE); 
 } 
 BOOL ReceivePacket(HWND hWnd) 
 { 
         HDC hDc; 
         int length; 
         int i1,i2,i3; 
         char line1[255], line2[255], line3[255]; 
         count ++; 
         if ((length = recv(s, lpBuffer, 1024, 0)) == SOCKET_ERROR) 
                 return (FALSE); 
         if (length == 0) 
                 return (FALSE); 
         if (hDc = GetDC(hWnd)) 
         { 
                 i1 = wsprintf((LPSTR)line1, "TCP Echo Client No.%d", count); 
                 i2 = wsprintf((LPSTR)line2, "Receive %d bytes",length); 
                 i3 = wsprintf((LPSTR)line3, "Those are:%c, %c, %c, %c, %c, %c",b 
 uffer[0],b 
 uffer[1],buffer[2],buffer[100],buffer[1000],buffer[1023]); 
                 TextOut(hDc, 10, 2, (LPSTR)line1, i1); 
                 TextOut(hDc, 10, 22, (LPSTR)line2, i2); 
                 TextOut(hDc, 10, 42, (LPSTR)line3, i3); 
                 ReleaseDC(hWnd, hDc); 
         } 
         return (TRUE); 
 } 
 BOOL SetSelect(HWND hWnd, long lEvent) 
 { 
         if (WSAAsyncSelect(s, hWnd, UM_SOCK, lEvent) == SOCKET_ERROR) 
         { 
                 AlertUser(hWnd, "WSAAsyncSelect Failure."); 
                 return (FALSE); 
         } 
         return (TRUE); 
 } 
 BOOL SendPacket(HWND hWnd, int len) 
 { 
         int length; 
         if ((length = send(s, lpBuffer, len, 0)) == SOCKET_ERROR) 
                 return (FALSE); 
         else 
         if (length != len) 
         { 
                 AlertUser(hWnd, "Send Length NOT Match!"); 
                 return (FALSE); 
         } 
         return (TRUE); 
 } 
 程序3:SERVER.C 
 #include <sys/types.h> 
 #include <sys/mntent.h> 
 #include <netinet/in.h> 
 #include <sys/socket.h> 
 #include <arpa/inet.h> 
 #define USERPORT 10001 
 #define HOST_IP_ADDR "192.1.1.2" 
 main(int argc, char **argv) 
 { 
         char buf[1024]; 
         struct sockaddr_in client; 
         struct sockaddr_in server; 
         int s; 
         int ns; 
         int namelen; 
         int pktlen; 
   
         if ((s=socket(AF_INET, SOCK_STREAM, 0))<0) 
         { 
                 perror("Socket()"); 
                 return; 
         } 
         bzero((char *)&server,sizeof(server)); 
         server.sin_family = AF_INET; 
         server.sin_port = htons(USERPORT); 
         server.sin_addr.s_addr = INADDR_ANY; 
         if (bind(s, (struct sockaddr *)&server, sizeof(server))<0) 
         { 
                 perror("Bind()"); 
                 return; 
         } 
         if (listen(s,1)!=0) 
         { 
                 perror("Listen()"); 
                 return; 
         } 
         namelen = sizeof(client); 
         if ((ns = accept(s, (struct sockaddr *)&client, &namelen)) ==-1) 
         { 
                 perror("Accept()"); 
                 return; 
         } 
         for (;;) 
         { 
                 if ((pktlen = recv(ns,buf,1024,0))<0) 
                 { 
                         perror("Recv()"); 
                         break; 
                 } 
                 else 
                 if (pktlen == 0) 
                 { 
                         printf("Recv():return FAILED,connection is shut down!\n" 
 ); 
                         break; 
                 } 
                 else 
                         printf("Recv():return SUCCESS,packet length = %d\n",pktl 
 en); 
                 sleep(1); 
                 if (send(ns,buf,pktlen,0)<0) 
                 { 
                         perror("Send()"); 
                         break; 
                 } 
                 else 
                         printf("Send():return SUCCESS,packet length = %d\n",pktl 
 en); 
         } 
         close(ns); 
         close(s); 
         printf("Server ended successfully\n"); 
 } 
 3.4 另一个精巧的应用程序实例-wshout 
     在本节中,我们通过一个经过精心选择的例子,进一步讨论一下Windows Sockets编 
 程技术。例如如何编制客户机或服务器程序,如何应用TCP有连接服务(流式套接口)或 
 UDP无连接服务(数据报套接口),如何进行阻塞或非阻塞方式的套接口操作等等,这些 
 都是经常碰到的问题。接下来要介绍的wshout程序,可以通过灵活地设置不同选项来达 
 到上述应用情况的任意组合,从而基本覆盖了应用Windows Sockets编程所可能碰到的问 
 题,具有很好的研究参考价值。 
     由于该程序思路清晰,结构精良,所以我们不打算很详细地剖析每一个语句,而只 
 是简要介绍一下整个程序的逻辑结构,并在源程序中加入适当的注释。我们相信,任何 
 具有基本C语言和Windows编程经验的读者,都能很轻松地读懂绝大部分内容。经过仔细 
 咀嚼和推敲后,更能得到一些编写优质程序的灵感。 
     该程序在FTP公司的PCTCP支撑环境下调试通过,不过只要读者拥有任何符合Window 
 s Sockets 1.1规范的实现,也能顺利执行该程序。 
 3.4.1 源程序目录 
 1. wshout.c wshout主程序 
 2. wshout.h wshout头文件 
 3. wshout.rc wshout资源文件 
 4. ushout.c UDP客户机程序 
 5. ulisten.c UDP服务器程序 
 6. tshout.c TCP客户机程序 
 7. tlisten.c TCP服务器程序 
 8. errno.c 获取WSAE*错误描述字符串程序 
 9. resolve.c 客户机/服务器启动程序 
     在编译本程序时,笔者用的是BC3.1,只需做一个PRJ工程文件,将上述.c文件及wi 
 nsock.lib包括进来就行了。请注意winsock.h应在include目录或当前目录中,winsock 
 .lib可利用winsock.dll通过implib工具来建立。如果读者使用其他的编译器,可自行作 
 相应的调整,在此不再赘述。  | 
 
 
 |