来源:远方网络 | 2006-1-6 | (有2289人读过)
客户机端程序设计实现
相比而言,客户端程序的网络通讯部分的实现较为简单,只需创建socket套接字端口,并用connect()同服务器建立起连接后就可以用recv()和send()同服务器收发数据了。
初始化Socket端口部分同服务器的实现部分类似,
…… wVersionrequested = MAKEWORD(2,0); //启动套接字 WSAStartup(wVersionrequested,&wsaData); …… SetTimer(hWnd,IDT_TIMER,US_TIME,NULL);
在此,通过设置定时器来及时地把远程计算机的当前屏幕以位图数据的形式传到客户端,并显示在屏幕上,使维护人员能及时了解到远程计算机的工作状态。首先要用connect()先建立一个到对等端(peer)的联接。connect()函数主要用于创建到指定的外部关联的联接。如果 socket 尚未绑扎(unbound),则由系统为本地关联指定一个唯一值。注意如果名字结构的地址域(the address field of the name structure)为全 0,connect()将返回错误 WSAEADDRNOTAVAIL。
对流式 sockets (SOCK_STREAM 类),将启动一个到使用名字(该 socket 名字空间的一个地址)的外部主机的活动联接。当调用成功完成时,该 socket 已准备好发送/接收数据。下面是定时器消息响应函数的部分主要代码:
…… clientSock = socket(AF_INET,SOCK_STREAM,0); if (clientSock < 0) return FALSE; //建立连接 client.sin_family = PF_INET; client.sin_port = htons(7016); client.sin_addr.s_addr = inet_addr(client_address); …… msgsock = connect(clientSock,(struct sockaddr*)&client,sizeof(client)); if (msgsock!=0) return FALSE; …… //获取屏幕尺寸 GetWindowRect(hWnd,&rect); BitWidth = rect.right - rect.left; BitHeight = rect.bottom - rect.top; recv(clientSock,(char*)&Flag,sizeof(Flag)+1,0); if (Flag == US_FLAG) { MouseEventFlag = false; //发送消息 Msg = US_DESKTOPBIT; send(clientSock,(char*)&Msg,sizeof(Msg)+1,MSG_OOB); //Send Bit Height and Weidth send(clientSock,(char*)&BitWidth,sizeof(BitWidth)+1,MSG_OOB); send(clientSock,(char*)&BitHeight,sizeof(BitHeight)+1,MSG_OOB); //接收数据 GetDesktopBit(hWnd); MouseEventFlag = true; } //关闭套接字,释放数据 closesocket(clientSock);
这里的在从服务器接收到数据后通过调用GetDesktopBit()来完成数据的显示,使从远程计算机传来的数据能在本地客户机中再现:
…… //Get Bit Size recv(clientSock,(char*)&bitSize,sizeof(bitSize)+1,0); send(clientSock,(char*)&Flag,sizeof(Flag)+1,MSG_OOB); //锁定内存 hDib = GlobalAlloc(GMEM_MOVEABLE,bitSize); p = (LPBYTE)GlobalLock(hDib); p2 = p; for(WORD i=0;i<bitSize/US_MAXSIZE;i++) { len = recv(clientSock,buf,US_MAXSIZE,0); CopyMemory(p2,buf,US_MAXSIZE); p2 = p2 + US_MAXSIZE; send(clientSock,(char*)&Flag,sizeof(Flag)+1,MSG_OOB); } if (bitSize%US_MAXSIZE) { len = recv(clientSock,buf,bitSize%US_MAXSIZE,0); CopyMemory(p2,buf,len); p2 = p2 + bitSize%US_MAXSIZE; send(clientSock,(char*)&Flag,sizeof(Flag)+1,MSG_OOB); } p2 = p2 - bitSize; …… hdc = GetDC(hWnd); GetClientRect(hWnd,&rect); //定义颜色 int color = (1<<((LPBITMAPINFOHEADER)p2)->biBitCount); if(color>256) color = 0; //显示 StretchDIBits(hdc, 0, 0, rect.right,rect.bottom,0,0, ((LPBITMAPINFOHEADER)p)->biWidth, ((LPBITMAPINFOHEADER)p)->biHeight, (LPBYTE)p+(sizeof(BITMAPINFOHEADER)+color*sizeof(RGBQUAD)), (LPBITMAPINFO)p,DIB_RGB_COLORS, SRCCOPY); ……
不论是服务器还是客户端,对于数据的传输都频繁地使用了recv和send函数。其中前者主要用于从一个 socket 接收数据、读取收到的数据。对流式套接字,将返回当前所有的尽可能多的数据,最长达到所提供的缓冲区的长度。如果该 socket 已经配置为线内(in-line)接收带外数据且有未读出的带外数据,则仅返回带外数据。应用程序可以使用 ioctlsocket() SIOCATMARK选项确定是否还有其它的带外数据待读。如果 socket 上没有到来的数据,则除非 socket 是非阻塞的,recv() 调用会等待数据到达。send()用于已联接的数据报或流式套接字,用来在一个 socket 上写出(write outgoing)数据。在这里需要特别指出的是:一个 send() 的成功完成并不能表明数据已被成功传递。如果保存(hold)待发送数据的传输系统中没有缓冲区空间可用,send() 将会阻塞,除非 socket 已设置为非阻塞 I/O 模式。
小结
本文通过Socket流式套接字实现了对计算机的远程监控。随着计算机网络化的深入,计算机网络已渗透到各种传统行业中,计算机网络编程尤其是基于Windows Socket套接字的网络程序的编程日益显得重要。本文采用直接利用动态连接库wsock32.dll,以WinSock API对程序进行设计讲解,虽实现比较繁琐,但能地对程序的设计实现有很好的理解。随着经验的丰富,也可以采用VC++的MFC类库中提供的CAsyncSocket套接字类来实现Socket编程,比较方便。
|