Design your own proxy server with Delphi
When writing an internet billing software, the author involves how to charge the Internet billing problem of all workstations in the LAN. In general, these workstations are online through the proxy server, and when using ready-made proxy software, because the proxy server software is a closed system, it is difficult to write the program to obtain real-time Internet timing information. Therefore, consider whether you can write your own proxy server, on the one hand, solve the group online, on the other hand, to solve the billing problem of the Internet? After experimental programming, the problem was finally solved. It is now written and shared with you.
1. There is a parameter in the system option of the current popular browser, that is, "Connect" through the proxy server ", after programming, the workstation in the Administration Domain Net Specifies the property, and the request data will be sent to the time of INTERnet request On the specified proxy server, the following is the request data package: get http://home.microsoft.com/intl/cn/ http / 1.0 accept: * / * accept-language: en-cn accept-eNCoding: Gzip, DEFLATE User-agent: mozilla / 4.0 (compatible; msie 5.0; windows nt) host: home.microsoft.com proxy-connection: Keep-alive sold in the first behavior target URL and related methods, protocols, "Host" line specified the goal The address of the host. This knows the process of the agent service: receives the request of the proxy, connect the true host, receive the data returned by the host, and send the received data to the proxy. To this end, a simple program can be prepared to complete the above network communication redirection. When designing Delphi, select Serversocket as a socket control communication with the proxy workstation, select the ClientSocket dynamic array as a socket control communication with the remote host. An important issue that should be solved when programming is a multi-connection processing problem. In order to speed up the speed of the agent service and the response speed of the agent, the properties of the socket control should be set to non-blocking type; each communication session and socket dynamic binding , Use a socketHandle property value to determine which session belonging to. The connection process of communication is shown below:
Proxy Server Serversocket (1) Received Remote Host (6) (2) (5) Browser ClientSocket (4) Web Server Receive Send (3)
(1), by the proxy browser issued a web request, the ServerSocket of the proxy server receives the request. (2), the proxy server automatically creates a clientSocket, and sets the host address, port, etc., and then connect the remote host. (3) After the remote communication is excited, the sending event is sent, and the Web request packet received by ServerSocket is sent to the remote host. (4) When the remote host returns page data, the clientSocket read event will read the page data. (5), the proxy server determines which Socket belonging to the ServerSocket control based on the binding information should be sent to the page information received from the host to the proxy. (6) The corresponding socket in ServerSocket sends the page data to the proxy. 2. Program writing Use Delphi Design or above communication process is very simple, mainly Serversocket, ClientSocket's related event drivers programming. The following is given the author's experienced proxy server interface and source list list, including a summary function description:
Unit main;
Interface
Uses Windows, Messages, Sysutils, Classes, Graphics, Controls, Forms, Dialogs, Extctrls, Scktcomp, Trayicon, Menus, stdctrls
TYPE session_record = record} ss_handle: integer; {proxy server socket; {proxy server socket} csockt: tclientsocket; {to connect remote socket} lookingup: boolean; {is looking for server} Lookuptime: integer; {Look Server Time} Request: Boolean; {Is a request} request_str: String; {Request data block} client_connected: boolean; {client online flag} remote_connected: boolean; {remote server connection flag} END;
type TForm1 = class (TForm) ServerSocket1: TServerSocket; ClientSocket1: TClientSocket; Timer2: TTimer; TrayIcon1: TTrayIcon; PopupMenu1: TPopupMenu; N11: TMenuItem; N21: TMenuItem; N1: TMenuItem; N01: TMenuItem; Memo1: TMemo; Edit1: TEdit ; Label1: TLabel; Timer1: TTimer; procedure Timer2Timer (Sender: TObject); procedure N11Click (Sender: TObject); procedure FormCreate (Sender: TObject); procedure FormClose (Sender: TObject; var Action: TCloseAction); procedure N21Click (Sender : TObject); procedure N01Click (Sender: TObject); procedure ServerSocket1ClientConnect (Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocket1ClientDisconnect (Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocket1ClientError (Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent Var errorcode: integer; procedure serversocket1clientRead (Sender: Tobject; Socket: tcustomwinsocket); Proce dure ClientSocket1Connect (Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Disconnect (Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Error (Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); procedure ClientSocket1Write (Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocket1Read (Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocket1Listen (Sender: TObject; Socket: TCustomWinSocket); procedure AppException (Sender: TObject; E: Exception); procedure Timer1Timer (Sender: TObject) PRIVATE {Private Declarations} public service_enabled: boolean;
{Proxy Service Open} Session: Array Of Session_Record; {Session Array} sessions: Integer; {Session Number} Lookuptimeout: Integer; {Connection Timeout Value} InvalidRequests: Integer; {Invalid Request Number;
IMPLEMentation
{$ R * .dfm}
File: // System Start Timer, Replenishment to System TRAY ... Procedure TFORM1.TIMER2TIMER (Sender: TOBJECT); begin Timer2.enable = false; {Close Timer} sessions: = 0; {Session Number = 0} Application.onexception: = APEXCEPTION; {{}}}} = 0; {0 Error} Lookuptimeout: = 60000; {Timeout value = 1 minute} Timer1.enabled: = true; {Open Timer} n11.enable; = false; {Open service menu item failure} n21.enabled: = true; {Close service menu item valid} ServerSocket1.port: = 988; {Proxy server port = 988} ServerSocket1.active: = True; {Open Service} Form1.Hide; {Hide Interface, zoom to System TRAY} END;
File: // Turn on the service menu item ... Procedure TFORM1.N11Click (Sender: TOBJECT); Begin Serversocket1.active: = true; {Open Service} END;
File: // Stop the service menu item ... procedure tform1.n21click (sender: TOBJECT); begin serversocket1.active: = false; {stop service} n11.enabled: = true; n21.enabled: = false; service_enabled: = false; service_enable {Logo clear zero} END;
File: // Main window is built ... Procedure TFORM1.FormCreate (Sender: TOBJECT); Begin Service_enabled: = false; Timer2.enabled: = true; {window is established, open the timer} END;
File: // When the window is closed ... Procedure TFORM1.FormClose (Sender: Tobject; var Action: Tclosection); begin time; Close service} END;
File: // Exit The program button ... procedure tform1.n01click (sender: TOBJECT); begin form1.close; {exit program} end;
File: // After opening the proxy service ... procedure tform1.serversocket1listen (SENDER: TOBJECT; SOCKET: TCUSTomWinsocket); becomue service_enabled: = true; {正 服务 标} n11.enable: = false; n21.enabled: = true; end; File: // After connecting to the proxy server by the agent, establish a session and binds with the socket ... Procedure TForm1.Serversocket1ClientConnect (Sender: Tobject; Socket: Tcustomwinsocket); Vari, J: Integer; Begin J: = -1; for i: = 1 to sessions do {Find if there is a blank item} if not session [i-1] .used and not session [i-1] .csocket.active dam j: = i-1; { Yes, allocate it} session [j] .used: = true; {在}} Break; end else if not session [I-1] .used and session [i-1] .csocket.active the session [i -1] .csocket.active: = false; if j = -1 Then Begin {None, add one} J: = sessions; inc (sessions); setlength (session, sessions); session [j] .used: = True; {在}} session [j] .csocket: = tclientsocket.create (nil); session [j] .CSocket.OnConnect: = ClientSocket1Connect; session [j] .CSocket.OnDisconnect: = ClientSocket1Disconnect; session [j] .CSocket.OnError: = ClientSocket1Error; session [j] .CSocket.OnRead: = ClientSocket1Read; session [ j] .csocket.onwrite: = clientsocket1write; session [j] .lookup: = false; end; session [j] .ss_handle: = socket.socketHandle; {Save handle, implement bind} session [j] .Request: = False; {No right request} session [j] .client_connected: = true; {client is connected} session [j] .remote_connected: = false; {remote unconnected} Edit1.Text: = INTOSTR (sessions);
File: // is intermitted by the agent ... Procedure TFORM1.Serversocket1ClientDisconnect (Sender: Tobject; Socket: Tcustomwinsocket); VARI, J, K: Integer; Begin for i: = 1 to sessions do if (session [i-1] .Ss_handle = socket.sockethandle) and session [i-1] .used the beginning [i-1] .client_connected: = false; {Client unconnected} if session [i-1] .remote_connected thession [i- 1] .csocket.active: = false {If you are connected, disconnect it} else session [i-1] .USED: = false; {If both are disconnected, then release resource logo} Break; end; J: = sessions; k: = 0; for i: = 1 to j DO {Statistical session number of tails There are several unused items} Begin if session [ji] .used the Break; Inc (K); End; if k > 0 THEN {Correction Session Array, Release Tail Unexpected} Begin Sessions: = Sessions-K; SETLENGTH (session, sessions); end; edit1.text: = INTOSTR (sessions);
file: // When a communication error occurs ... procedure TForm1.ServerSocket1ClientError (Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); vari, j, k: integer; begin for i: = 1 to sessions do if (session [i-1] .ss_handle = socket.sockethandle) and session [i-1] .used the beginning [i-1] .client_connected: = false; {Client unconnected} if session [i-1] .remote_connected the session [i-1] .csocket.active: = false {假 =}}} else session [i-1] .used: = false; {If both are disconnected, then release Resource logo} Break; end; j: = sessions; k: = 0; for i: = 1 to j do begin if session [ji] .used dam; inc (k); end; if k> 0 Then Begin sessions : = sessions-k; setlength (session, sessions); end; ed; edit1.text: = INTOSTOSTR (sessions); errorcode: = 0;
File: // Send the page request when sending ... Procedure TForm1.Serversocket1ClientRead (Snder: Tobject; Socket: Tcustomwinsocket); VARTMP, LINE, HOST: STRING; I, J, Port: Integer; Begin for i: = 1 To Sessions do {Judgment which session} if session [i-1] .used and (session [i-1] .ss_handle = socket.sockethandle) THEN BEGIN Session [i-1] .Request_Str: = Socket.ReceiveText; Save Request Data} TMP: = Session [I-1] .Request_Str; {Store to Temporary Variable} Memo1.Lines.Add (TMP); J: = POS (CHAR (13) CHAR (10), TMP); { One line of markers} while j> 0 do {progressive scan request text, find the host address} begin line: = COPY (TMP, 1, J-1); {Take a row} delete (TMP, 1, J 1); { Delete a line} J: = POS ('host', line); {Host address flag} if j> 0 THEN Begin delete (Line, 1, J 5); {Delete the inactive character} J: = POS (' : ', line); IF J> 0 Then Begin Host: = Copy (Line, 1, J-1); Delete (Line, 1, J); Try Port: = StrtOINT (LINE); Except Port: = 80; End; Else Begin Host : = Trim (Line); {Get Host Address} Port: = 80; END;
If not session [i-1] .Remote_Connected then {If the expedition has not been connected} Begin session [i-1] .Request: = true; {request data ready mark} session [i-1] .csocket.host: = Host ; {Set Remote Host Address} Session [i-1] .csocket.port: = port; {Setting Port} session [i-1] .csocket.active: = true; {Connection Remote Host} session [i-1] .Lookingup: = true; {标}} session [i-1] .lookuptime: = 0; {Start timing} end else {If remote connection, direct session [i-1] .csocket.socket .sendtext (session [i-1] .Request_str); Break; {Stop Scan request text} end; j: = POS (char (13) char (10), TMP); {Night a line} End; Break; {Stop loop} E Nd; end; file: // When connecting the remote host ... Procedure TFORM1.ClientSocket1Connect (Sender: Tobject; Socket: tcustomwinsocket); VARI: Integer; Begin for i: = 1 to sessions do if (session [i-1] .Csocket.socket.socketHandle = Socket.socketHandle) and session [i-1] .used the beginning [i-1] .csocket.tag: = socket.socketHandle; session [i-1] .remote_connected: = true; {远 主 主 已 有. 通}} session [i-1] .lookingup: = false; {Qing Gui} Break; End;
File: // When the remote host is disconnected ... Procedure TFORM1.ClientSocket1disconnect (Sender: Tobject; Socket: Tcustomwinsocket); VARI, J, K: Integer; Begin for i: = 1 to sessions do if (session [i-1] .Csocket.tag = Socket.sockethandle) and session [i-1] .used the beginning [i-1] .remote_connected: = false; {未 不 连接} if not session [i-1] .client_connected the session [I-1] .USED: = false {If the client has disconnected, then the resource flag is released} else fork: = 1 to serversocket1.socket.activeConnections do if (ServerSocket1.socket.connections [k-1]. SocketHandle = session [i-1] .ss_handle) and session [i-1] .used the beginning (k) [k-1] .close; break; end; break; end; j: = sessions; k: = 0; for i: = 1 to j Do Begin if session [ji] .used the break; inc (k); end; if k> 0 THEN {Correction session number} Beg In sessions: = sessions-k; setlength (session, sessions); end; ed; ed; eD;
File: // When there is an error with the remote host communication ... Procedure TFORM1.ClientSocket1ERROR (Sender: Tobject; Socket: Tcustomwinsocket; ErrorEvent: TerRorevent; Var ErrorCode: Integer; VARI, J, K: Integer; Begin for i: = 1 To sessions do if (session [i-1] .csocket.tag = socket.sockethandle) and session [i-1] .used the beginning (i-1); session [i-1] .remote_connected: = false; { Unconnected} if not session [i-1] .client_connected the session [i-1] .USED: = false {If the client has disconnected, set the resource flag} else fork: = 1 to serversocket1.socket. ActiveConnections Do IF (Serversocket1. Socket.connections [K-1]. SocketHandle = session [i-1] .ss_handle) and session [i-1] .used the beginning., Serversocket1.socket.connections [k-1] .close; Break; End; Break; end; j: = sessions; k: = 0; for i: = 1 to j Do Begin if session [ji] .used dam; INC; ErrorCode: = 0; if k> 0 THEN {Correction session array} Begin sessions: = sessions-k; setlength (session, sessions); end; ed; edit1.text: = INTOSTR (SESSITIONS); END; ;
File: // Send a page request to the remote host ... Procedure TForm1.ClientSocket1Write (Sender: Tobject; Socket: Tcustomwinsocket); VARI: Integer; Begin for i: = 1 to sessions do if (session [i-1] .csocket.tag = Socket.socketHandle) and session [i-1] .used the beginningness [i-1] .Request the beginning (session [i-1] .Request_str); {If there is a request, send} session [ I-1] .Request: = false; {清标} end; break; end; end; file: // Remote Host send page data ... Procedure TFORM1.CLIENTSOCKET1READ (Sender: Tobject; Socket: tcustomwinsocket); VARI, J: Integer; REC_BYTES: INTEGER; {Reploys Data Block Length} REC_Buffer: Array [0..2047] of char; {Reploquette Bar buffer} Begin for i: = 1 to sessions do if (session " I-1] .csocket.tag = socket.sockethandle) and session [i-1] .used the begin r_bytes: = socket.ReceiveBuf (Rec_buffer, 2048); {Receive Data} for J: = 1 to serversocket1.socket. ActiveConnections Do IF Serversocket1.sock Et.connections [J-1]. SocketHandle = session [i-1] .ss_handle the beginning "[j-1] .sendbuf (REC_Buffer, REC_BYTES); {Send Data} Break; End; Break; ;
File: // "You can't find" and wait ... Procedure TFORM1.APPEXCEPTION (Sender: TOBJECT; E: Exception); begin inc (INVALIDREQUESTS);
File: // Find remote host timing ... Procedure TFORM1.TIMER1TIMER (Sender: Tobject); VARI, J: Integer; Begin for i: = 1 to sessions do if session [i-1] .used and session [i-1] .Lookingup kilocause, if it is connected} begin inc (session [i-1] .lookuptime); if session [i-1] .lookuptime> Lookuptimeout dam {假 超} begin session [i-1] .lookingup: = false; Session [I-1] .csocket.active: = false; {Stop search} for j: = 1 to servets do if servets [j-1]. SocketHandle = session [i-1] .Ss_handle kilnness.close; {disconnected client} Break; end; end; end; end; end.3, after this design idea is only being proxy and remote A redirection function is added between the host, and it is preserved by the original cache technology of the agent. Therefore, the efficiency is high. After testing, when using 1 33.6K MODEM Internet, three to ten were online by the agent workstation, there is still a good response speed. Since the connection between the proxy workstation and the proxy server workstation is generally passed through the high-speed link, the bottleneck mainly appears on the network of the proxy server. Through the above method, the author successfully developed a complete agent server software and completely integrated with the machine room billing system, and realized the use of a workstation to complete the Internet agent, Internet billing, equipment billing. Friends with programming experience can be additionally additionally adding proxy server functions, such as setting up access sites, statistical customer traffic, web access list, and more.