来源:远方网络 | 2006-1-6 | (有2196人读过)
服务器端程序设计实现
由于我们的目的是通过在位于中心机房的客户端来监控远程的服务器端,而根据前面介绍的面向连接套接字应用程序的工作方式,要求服务器必须先于客户端而运行。所以根据实际需要,我们应当让服务器程序能自启动。一般有三种方法:在Autoexec.bat里添加代码;在Win.ini的Run项里添加启动路径;在注册表里添加键值。本文在此采用后一种方法,通过向注册表的Software\\Microsoft\\Windows\\CurrentVersion\\Run下添加键值的方式来实现,另外也可以在RunServer下添加键值实现之:
…… //设定待添加的注册表的路径 LPCTSTR Rgspath="Software\\Microsoft\\Windows\\CurrentVersion\\Run" ; …… //获取系统路径 GetSystemDirectory(SysPath,size); GetModuleFileName(NULL,CurrentPath,size); …… //把服务程序从当前位置拷贝到系统目录中 FileCurrentName = CurrentPath; FileNewName = lstrcat(SysPath,"\\System_Server.exe"); ret = CopyFile(FileCurrentName,FileNewName,TRUE); …… //打开键值 ret=RegOpenKeyEx(HKEY_LOCAL_MACHINE,Rgspath,0,KEY_WRITE, &hKEY); if(ret!=ERROR_SUCCESS) { RegCloseKey(hKEY); return FALSE; } //设置键值 ret=RegSetValueEx(hKEY,"System_Server",NULL,type, (const unsigned char*)FileNewName,size); if(ret!=ERROR_SUCCESS) { RegCloseKey(hKEY); return FALSE; } //关闭键值 RegCloseKey(hKEY);
注册完之后就完成了自启动。下面进行本文的重点:对套接字进行编程,首先初始化Socket端口,并在初始化成功的前提下通过调用socket()创建一个套接字,然后调用bind()将该套接字和本地网络地址联系在一起,再调用listen()使套接字做好侦听的准备,并规定它的请求队列的长度。其中listen()函数主要用来建立一个socket套接字以侦听到来的联接,而且仅用于支持联接的 socket,即类型为 SOCK_STREAM 的 socket。该套接字被设为"被动"模式,负责响应到来的联接,并由进程将到来的联接排队挂起。该函数典型地用于需要同时有多个联接的服务器:如果一个联接请求到达且队列已满,客户端将收到一个 WSAECONNREFUSED 的错误。当没有可用的描述符时,listen() 将试图把函数合理地继续下去。它将接受联接直到队列为空。如果描述符变为可用,后来的对 listen() 或 accept() 调用将会把队列填充到当前或最近的累积数(the current or most recent "backlog’’),可能的话,继续侦听到来的联接。下面是这部分的主要代码:
…… wMajorVersion = MAJOR_VERSION; wMinorVersion = MINOR_VERSION; wVersionReqd = MAKEWORD(wMajorVersion,wMinorVersion); …… Status = WSAStartup(wVersionReqd,&lpmyWSAData); if (Status != 0) return FALSE; …… //创建Socket套接字 ServerSock = socket(AF_INET,SOCK_STREAM,0); if (ServerSock==INVALID_SOCKET) return FALSE; dstserver_addr.sin_family = PF_INET; dstserver_addr.sin_port = htons(7016); dstserver_addr.sin_addr.s_addr = INADDR_ANY;
//BIND Status = bind(ServerSock,(struct sockaddr far *)&dstserver_addr,sizeof(dstserver_addr)); if (Status != 0) return FALSE;
//LISTEN Status = listen(ServerSock,1); if (Status != 0) return FALSE;
接下来需要调用accept()来接收连接。客户在建立套接字后就可调用connect()和服务器建立连接。其函数原形为:SOCKET PASCAL FAR accept ( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen );该例程从在 s 上挂起的联接队列中取出第一个联接,用和 s 相同的特性创建一个新的 socket 并返回新 socket 的句柄。如果队列中没有挂起的联接,并且 socket 也未标明是非阻塞的,则 accept() 阻塞调用者直到有一个联接。已经接受联接的 socket (accepted socket)不应用于接受更多的联接。参数 addr 是一个返回参数,填入的是通信层的联接实体地址。地址参数 addr 的严格格式由进行通信的地址族确定。addrlen 是一个返回参数值;该值在调用前包含 addr 指向的缓冲区空间长度;调用返回时包含返回地址的实际长度。
//ACCEPT int len = sizeof(dstserver_addr); NewSock = accept(ServerSock,(struct sockaddr far *)&dstserver_addr,&len); if (NewSock < 0) { closesocket(ServerSock); return FALSE; } //获取屏幕大小 SysWidth = GetSystemMetrics(SM_CXSCREEN); SysHeight = GetSystemMetrics(SM_CYSCREEN); ……
连接一旦建立,客户机和服务器之间就可以通过调用read()和write()来发送和接收数据。最后,待数据传送结束后,调用close()关闭套接字。下面的函数就负责将当前的屏幕状态,以数据的形式通过send函数发送给客户程序,以实现对远程服务器端的计算机的远程监视:
…… //Send Falg FALG = US_FLAG; send(NewSock,(char*)&FALG,sizeof(FALG)+1,MSG_OOB); //Get Message length = recv(NewSock,(char*)&iMsg,sizeof(iMsg)+1,0); if (length < 0) { //Close Sock closesocket(NewSock); closesocket(ServerSock); return FALSE; } //GetMessageData if (iMsg < 4500) { send(NewSock,(char*)&SysWidth,sizeof(SysWidth)+1,MSG_OOB); send(NewSock,(char*)&SysHeight,sizeof(SysHeight)+1,MSG_OOB); } switch(iMsg) { case US_DESKTOPBIT: //发送当前屏幕图像 SendDesktop(); break; …… }
其中,SendDesktop()函数负责将屏幕保存成位图,然后再通过send()函数将其以数据的形式发送出去,这一部分牵扯较多的位图操作,比较繁琐,由于本文重点并不在此,仅作为一个功能函数将其关键性代码摘选如下:
void SendDesktop() { …… //创建桌面设备环境句柄 hdcmy = CreateDC("DISPLAY",NULL,NULL,NULL); hbufferdc = CreateCompatibleDC(hdcmy); //创建位图 hBit = CreateCompatibleBitmap(hdcmy, BitWidth, BitHeight); hOldBitmap = (HBITMAP)SelectObject(hbufferdc, hBit); StretchBlt(hbufferdc, 0, 0, BitWidth, BitHeight, hdcmy, 0, 0,SysWidth,SysHeight, SRCCOPY); hBit = (HBITMAP)SelectObject(hbufferdc, hOldBitmap); …… //DDBtoDIB hPal = (HPALETTE) GetStockObject(DEFAULT_PALETTE ); // 获取位图信息 GetObject(bitmap,sizeof(bm),(LPSTR)&bm); //初始化位图信息头 bi.biSize = sizeof(BITMAPINFOHEADER); bi.biWidth = bm.bmWidth; bi.biHeight = bm.bmHeight; bi.biPlanes = 1; //bi.biBitCount = bm.bmPlanes * bm.bmBitsPixel; bi.biBitCount = 4; bi.biCompression = BI_RGB; bi.biSizeImage = 0; bi.biXPelsPerMeter = 0; bi.biYPelsPerMeter = 0; bi.biClrUsed = 0; bi.biClrImportant = 0; …… lpbi = (LPBITMAPINFOHEADER)hDib; *lpbi = bi; GetDIBits(hdc, bitmap, 0L, (DWORD)bi.biHeight,(LPBYTE)NULL, (LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS ); bi = *lpbi; if (bi.biSizeImage == 0) { bi.biSizeImage = ((((bi.biWidth * bi.biBitCount) + 31) & ~31) / 8) * bi.biHeight; } dwLen += bi.biSizeImage; if (handle = GlobalReAlloc(hDib, dwLen, GMEM_MOVEABLE)) hDib = handle; …… lpbi = (LPBITMAPINFOHEADER)hDib; BOOL bgotbits = GetDIBits( hdc, bitmap0L, (DWORD)bi.biHeight,(LPBYTE)lpbi+ (bi.biSize + ncolors * sizeof(RGBQUAD)),(LPBITMAPINFO)lpbi, (DWORD)DIB_RGB_COLORS); SelectPalette(hdc,hPal,FALSE); …… send(NewSock,(char*)&bitSize,sizeof(bitSize)+1,MSG_OOB); recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0); plmagePoint = (LPBYTE)hDib; for(WORD i=0;i { send(NewSock,(char*)plmagePoint,sizeof(BYTE)*US_MAXSIZE,MSG_OOB); plmagePoint = plmagePoint + US_MAXSIZE; recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0); } if (bitSize%US_MAXSIZE) { send(NewSock,(char*)plmagePoint,sizeof(BYTE)*GlobalSize(hDib)%US_MAXSIZE,MSG_OOB); recv(NewSock,(char*)&BitMsg,sizeof(BitMsg)+1,0); } …… }
|