Visual c ++ Remote Computer Monitoring
[Date:2005-10-20] |
Source:Author: |
[Font: large, medium, and small] |
Abstract: This article describes the general technology of network programming using Socket sockets, and through this technology to achieve remote monitoring of computers.
Keywords: Socket socket, server, client, remote monitoring
Introduction
In the construction of the project, the master data center of the center is often separated from the project site. This requires engineering engineers to travel between the center data center and the project site frequently, sometimes it is necessary for relevant personnel to perform on-site operations to modify data. In addition, real-time monitoring on the project site is not good, which brings great inconvenience to the project construction and system maintenance. At present, LAN technology is quite mature, and it is not difficult to build a LAN between the central data center and the engineering site. Therefore, based on the physical architecture of the LAN, we can use socket sockets to implement communication between computers, the maintenance personnel can monitor and control the computer working status on the project site in real time without leaving the center room. This article is similarProgramThe implementation method.
General idea of Socket network program
The Windows Sockets Specification defines a Microsoft Windows-based network programming interface derived from Berkeley Software release (BSD) at the University of California Berkeley ). It includes both familiar Berkeley socket-style routines and a set of Windows-specific extensions, allowing programmers to use the original windows message-driven mechanism for network programming. The most common mode of such programs is the customer/Server mode. In this framework, the customer application requests the service from the server application. Server applications generally listen for (Listen) service requests on a listener address. That is, until a client sends a connection request to the server, the server process is sleep. When receiving the request, the server process "wake up" to complete the corresponding activities of the customer request.
There are three types of sockets: stream socket, datagram socket, and original socket. Streaming sockets define a reliable connection-oriented service that enables sequential data transmission without errors or duplicates. datagram sockets define a connectionless service, data is transmitted through independent packets, which is unordered and not reliable. The original socket allows direct access to low-layer protocols, such as IP addresses or ICMP protocols, it is mainly used to test the implementation of new network protocols. Non-connection servers are generally transaction-oriented. A single response to a request completes the interaction between the client program and the service program. The connection server-oriented requests are often complicated. They cannot be solved by a one-to-one request response, and are often concurrent servers.
This article uses connection-oriented sockets. The working process is as follows: the server first starts, creates a socket by calling socket (), and then calls BIND () associate the socket with the local network address, call listen () to prepare the socket for listening, specify the length of its request queue, and then call accept () to receive connections. After creating a socket, the customer can call connect () to establish a connection with the server. Once a connection is established, the client and server can send and receive data by calling read () and write. Finally, after the data transfer is completed, both parties call close () to close the socket. The main process sequence can be expressed in Figure 1:
Server-side program design and implementation
because our goal is to monitor remote servers by using clients in the central data center, and according to the preceding working methods for connection socket applications, the server must run before the client. Therefore, based on actual needs, we should enable the server program to start itself. Generally, there are three methods: In autoexec. add Code in bat. add the startup path in the run entry of INI; add a key value in the registry. This article uses the last method, which is implemented by adding a key value to the Software \ Microsoft \ Windows \ CurrentVersion \ Run of the Registry, you can also add a key value under runserver:
...... // Set the path of the Registry to be added Lpctstr rgspath = "SOFTWARE \ Microsoft \ Windows \ CurrentVersion \ Run "; ...... // Obtain the system path Getsystemdirectory (syspath, size ); Getmodulefilename (null, currentpath, size ); ...... // Copy the service program from the current location to the system directory Filecurrentname = currentpath; Filenewname = lstrcat (syspath, "\ system_server.exe "); Ret = copyfile (filecurrentname, filenewname, true ); ...... // Enable the key value Ret = regopenkeyex (HKEY_LOCAL_MACHINE, rgspath, 0, key_write, & hkey ); If (Ret! = Error_success) { Regclosekey (hkey ); Return false; } // Set the key value Ret = regsetvalueex (hkey, "system_server", null, type, (Const unsigned char *) filenewname, size ); If (Ret! = Error_success) { Regclosekey (hkey ); Return false; } // Close the key value Regclosekey (hkey ); |
after registration, the Automatic startup is completed. The focus of this article is as follows: to program sockets, first initialize the socket port, and then create a socket by calling socket () and then calling BIND () associate the socket with the local network address, and then call listen () to prepare the socket for listening and specify the length of its request queue. The listen () function is mainly used to establish a Socket socket to listen for incoming connections. It is only used to support the connected socket, that is, the socket type of sock_stream. This socket is set to "passive" mode and is responsible for responding to incoming connections, and the process queues the incoming connections. This function is typically used for servers that require multiple connections at the same time: if a connection request arrives and the queue is full, the client will receive a wsaeconnrefused error. When no descriptor is available, listen () tries to reasonably continue the function. It accepts connections until the queue is empty. If the descriptor becomes available, the subsequent call to listen () or accept () will fill the queue with the current or recent cumulative number (the current or most recent "backlog ''), if possible, continue listening for incoming connections. The following is the main code:
...... wmajorversion = major_version; wminorversion = minor_version; wversionreqd = makeword (wmajorversion, wminorversion); ...... Status = wsastartup (wversionreqd, & lpmywsadata); If (status! = 0) return false; ...... // create a Socket 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; |
Next, you need to call accept () to receive connections. After creating a socket, the customer can call connect () to establish a connection with the server. The original function form is: Socket Pascal far accept (socket S, struct sockaddr far * ADDR, int far * addrlen ); this routine extracts the first join from the connection queue suspended on S, creates a new socket with the same features as S, and returns the handle of the new socket. If no suspended connections exist in the queue and the socket is not marked as non-blocking, accept () blocks the caller until there is a connection. The accepted socket should not be used to accept more connections. The ADDR parameter is a return parameter that is filled with the link object address of the communication layer. The strict format of the ADDR parameter is determined by the address family for communication. Addrlen is a return parameter value. The value contains the length of the buffer space that ADDR points to before calling, and the actual length of the return address when calling the return value.
// Accept Int Len = sizeof (dstserver_addr ); Newsock = accept (serversock, (struct sockaddr far *) & dstserver_addr, & Len ); If (newsock <0) { Closesocket (serversock ); Return false; } // Obtain the screen size Syswidth = getsystemmetrics (sm_cxscreen ); Sysheight = getsystemmetrics (sm_cyscreen ); ...... |
Once a connection is established, the client and server can send and receive data by calling read () and write. Finally, call close () to close the socket after data transmission is complete. The following function is responsible for sending the current screen status to the client program in the form of data to achieve remote monitoring of the computer on the remote server:
...... // 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_?topbit: // send the current screen image Senddesktop (); Break; ...... } |
The senddesktop () function saves the screen as a bitmap, and then sends the screen as data through the send () function. This part involves many bitmap operations, this article does not focus on the complex issues. As a function, the key code of this function is extracted as follows:
Void senddesktop () { ...... // Create a desktop device environment handle Hdcmy = createdc ("display", null ); Hbufferdc = createcompatibledc (hdcmy ); // Create a bitmap 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 ); // Obtain bitmap Information GetObject (bitmap, sizeof (BM), (lpstr) & BM ); // Initialize the bitmap information Header 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 <bitsize/us_maxsize; 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 ); } ...... } |
Client Program Design and Implementation
In contrast, the implementation of the network communication part of the client program is relatively simple. You only need to create a Socket socket port and connect () with the server to establish a connection, then you can use Recv () and send () the same server has sent and received data.
The initialization of the socket port is similar to that of the server,
...... Wversionrequested = makeword (2, 0 ); // Start the socket Wsastartup (wversionrequested, & wsadata ); ...... Settimer (hwnd, idt_timer, us_time, null ); |
In this way, you can set a timer to transmit the current screen of the remote computer to the client in the form of bitmap data and display it on the screen in a timely manner, the maintenance personnel can learn the working status of the remote computer in time. First, use connect () to create a connection to peer. The CONNECT () function is used to create a connection to a specified external Association. If the socket has not been bound (unbound), the system specifies a unique value for the local association. Note: If the address field of the name structure is all 0, connect () will return the error wsaeaddrnotavail.
For the streaming Sockets (sock_stream class), an active connection to the external host using the name (an address of the socket namespace) will be started. When the call is successful, the socket is ready to send/receive data. The following is the main code of the timer message response function:
...... clientsock = socket (af_inet, sock_stream, 0); If (clientsock <0) return false; // establish a connection 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; ...... // obtain the screen size 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) {< br> mouseeventflag = false; // send a message MSG = us_0000topbit; 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 ); // receive data getasktopbit (hwnd); mouseeventflag = true; }< br> // close the socket, release Data closesocket (clientsock); |
After receiving data from the server, call getasktopbit () to display the data so that the data sent from the remote computer can be reproduced in the local client:
...... // Get bit size Recv (clientsock, (char *) & bitsize, sizeof (bitsize) + 1, 0 ); Send (clientsock, (char *) & flag, sizeof (FLAG) + 1, MSG_OOB ); // Lock the memory 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 ); // Define the color Int color = (1 <(lpbitmapinfoheader) P2)-> bibitcount ); If (color> 256) Color = 0; // Display 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 ); ...... |
Both the server and client use the Recv and send functions frequently for data transmission. The former is mainly used to receive and read data from a socket. Streaming sockets return as much data as possible, up to the length of the provided buffer. If the socket has been configured as in-line to receive out-of-band data that has not been read, only out-of-band data is returned. Applications can use ioctlsocket () siocatmark to determine whether there are other out-of-band data to be read. If there is no incoming data on the socket, The Recv () call will wait for the data to arrive unless the socket is not blocked. Send () is used for the connected datagram or stream socket to write (write outgoing) data on a socket. It is worth noting that the successful completion of a send () does not indicate that the data has been successfully transmitted. If no buffer space is available in the transmission system that saves (hold) the data to be sent, send () will be blocked unless the socket has been set to non-blocking I/O mode.
Summary
This article uses Socket stream sockets to remotely monitor computers. With the deepening of computer network, computer networks have penetrated into various traditional industries. Computer network programming, especially the programming of network programs based on Windows Socket sockets, is becoming increasingly important. This article uses the dynamic Connection Library wsock32.dll to explain the program design with Winsock API. Although the implementation is cumbersome, it can have a good understanding of the program design and implementation. With rich experience, you can also use the casyncsocket socket class provided in the VC ++ MFC class library to implement socket programming, which is more convenient.