注册 登录  
 加关注
查看详情
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

一路奔跑

奔跑着一路向前...

 
 
 

日志

 
 

Windows Sockets 规范与应用(第三章)  

2009-09-25 18:13:24|  分类: Sockets |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
第三章   Windows   Sockets   1.1应用实例  
          在本章中,作者的实际工作为背景,给出了一个使用Windows   Sockets   1.1编程的具体例子。并对这个例子作了详细的分析。这个例子在Windows   3.1、Windows   Sockets   1.1和BSD   OS   for   PC   2.0(BSD   UNIX微机版)环境下调试通过。  
  3.1   套接口网络编程原理  
  套接口有三种类型:流式套接口,数据报套接口及原始套接口.  
          流式套接口定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输.数据报套接口定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错.原始套接口允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议实现的测试等.  
          无连接服务器一般都是面向事务处理的,一个请求一个应答就完成了客户程序与服务程序之间的相互作用。若使用无连接的套接口编程,程序的流程可以用图3-1表示。  
          面向连接服务器处理的请求往往比较复杂,不是一来一去的请求应答所能解决的,而且往往是并发服务器。使用面向连接的套接口编程,可以通过图3-1来表示:其时序。  
  套接口工作过程如下:服务器首先启动,通过调用socket()建立一个套接口,然后调用bind()将该套接口和本地网络地址联系在一起,再调用listen()使套接口做好侦听的准备,并规定它的请求队列的长度,之后就调用accept()来接收连接.客户在建立套接口后就可调用connect()和服务器建立连接.连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据.最后,待数据传送结束后,双方调用close()关闭套接口.  
  3.2   Windows   Sockets编程原理  
  由于Windows的基于消息的特点,WINSOCK和BSD套接口相比,有如下一些新的扩充:  
  1.异步选择机制  
  异步选择函数WSAAsyncSelect()允许应用程序提名一个或多个感兴趣的网络事件,如FD_READ,FD_WRITE,FD_CONNECT,FD_ACCEPT等等代表的网络事件.当被提名的网络事件发生时,Windows应用程序的窗口函数将收到一个消息.这样就可以实现事件驱动了.  
  2.异步请求函数  
  异步请求函数允许应用程序用异步方式获得请求的信息,如WSAAsyncGetXByY()类函数.   这些函数是对BSD标准函数的扩充.函数WSACancelAsyncRequest()允许用户中止一个正在执行的异步请求.  
  3.阻塞处理方法  
  WINSOCK提供了"钩子函数"负责处理Windows消息,使Windows的消息循环能够继续.WINSOCK提供了两个函数(WSASetBlockingHook()和WSAUnhookBlockingHook())让应用程序设置或取消自己的"钩子函数".函数WSAIsBlocking()可以检测是否阻塞,函数WSACancelBlockingCall()可以取消一个阻塞的调用.  
  4.错误处理  
  WINSOCK提供了两个WSAGetLastError()和WSASetLastError()来获取和设置最近错误号.  
  5.启动和终止  
  由于Windows   Sockets的服务是以动态连接库WINSOCK.DLL形式实现的,所以必须要先调用WSAStartup()函数对Windows   Sockets   DLL进行初始化,协商WINSOCK的版本支持,并分配必要的资源.在应用程序关闭套接口后,还应调用WSACleanup()终止对Windows   Sockets   DLL的使用,并释放资源,以备下一次使用.  
  在这些函数中,实现Windows网络实时通信的关键是异步选择函数WSAAsyncSelect()的使用.   用法及详细说明参见第5.3.7.  
  3.3   Windows   Sockets与UNIX套接口编程实例  
  下面是一个简单的基于连接的点对点实时通信程序.它由两部分组成,服务器在主机UNIX下直接运行,   客户机在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(建立连接的套接口)接收数据放入buf中,send则将buf中数据发送给ns.至于第四个参数,表示该函数调用方式,可选择MSG_DONTROUTE和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指向一个WSADATA结构,该结构描述了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(),connect()便是非阻塞调用.进程发出连接请求后就不管了,当连接建立好后,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   中的close())来关闭套接口,再调用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   lParam);  
  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_OVERLAPPEDWINDOW, 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   lParam)  
  {  
  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   Failed!\n");  
  closesocket(s);  
  break;  
  }  
  if   (!SetSelect(hWnd,   FD_READ))  
  closesocket(s);  
  break;  
  case   FD_CLOSE:  
  if   (WSAAsyncSelect(s,   hWnd,   0,   0)   ==   SOCKET_ERROR)  
  AlertUser(hWnd,   "WSAAsyncSelect   Failed.\n");  
  break;  
  default:  
  if   (WSAGETSELECTERROR(lParam)   !=   0)  
  {  
  AlertUser(hWnd,   "Socket   Report   Failure.");  
  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,hostnm.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",buffer[0],buffer[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",pktlen);  
  sleep(1);  
  if   (send(ns,buf,pktlen,0)<0)  
  {  
  perror("Send()");  
  break;  
  }  
  else  
  printf("Send():return   SUCCESS,packet   length   =   %d\n",pktlen);  
  }  
  close(ns);  
  close(s);  
  printf("Server   ended   successfully\n");  
  }   
 
  评论这张
 
阅读(119)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2018