客戶端伺服器
外觀
| 導航併發程式設計主題: |
在 1990 年代,隨著 Unix 伺服器價格下降,趨勢從大型機計算轉向客戶端/伺服器。資料庫訪問和一些業務邏輯集中在後端伺服器上,收集來自使用者程式的資料安裝在前端使用者的“客戶端”計算機上。在 Java 世界中,前端和後端之間有三種主要通訊方式。
- 客戶端應用程式使用 JDBC(Java 資料庫連線 API)連線到資料庫伺服器(後端上的業務邏輯有限,除非使用儲存過程)。
- 客戶端應用程式使用 RMI (遠端方法呼叫) 與後端通訊。
- 客戶端應用程式使用套接字連線與後端通訊。

此頁面顯示了套接字連線的示例。
Java 語言的開發考慮了網路計算。因此,建立伺服器程式非常容易。伺服器是一段始終執行的程式碼,它在計算機上的特定埠上偵聽傳入請求。當請求到達時,它會啟動一個新執行緒來處理該請求。請檢視以下示例
- ComServer
- 類用於在埠上偵聽客戶端。
程式碼清單 1.1:ComServer
import java.net.ServerSocket;
/**
* -- Main Server Class; Listening on a port for client; If there is a client,
* starts a new Thread and goes back to listening for further clients. --
*/
public class ComServer
{
static boolean GL_listening = true;
/**
* -- Main program to start the Server --
*/
public static void main(String[] args) throws IOException
{
ComServer srv = new ComServer();
srv.listen();
} // --- End of Main Method ---
/**
* -- Server method; Listen for client --
*/
public int listen() throws IOException
{
ServerSocket serverSocket = null;
int iPortNumber = 9090;
// --- Open the Server Socket where this should listen ---
try {
System.out.println( "*** Open the listening socket; at:"+ iPortNumber + " ***" );
serverSocket = new ServerSocket( iPortNumber );
} catch (IOException e) {
System.err.println("Could not listen on port:"+iPortNumber );
System.exit(1);
}
while ( GL_listening )
{
ComServerThread clientServ;
// --- Listening for client; If there is a client start a Thread -
System.out.println( "*** Listen for a Client; at:"+ iPortNumber + " ***" );
clientServ = new ComServerThread( serverSocket.accept() );
// --- Service a Client ---
System.out.println( "*** A Client came; Service it ***" );
clientServ.start(); /* --- Use for multy Threaded --- */
// clientServ.run(); /* --- Use for Single Threaded --- */
}
// --- Close the Server socket; Server exiting ---
serverSocket.close();
return 0;
} // --- End of listen Method ---
} // --- End of ComServer Class ---
|
- ServerSocket( iPortNumber )
- 建立一個伺服器套接字,繫結到指定的埠。
- serverSocket.accept()
- 偵聽對該套接字的連線,並接受它。該方法會阻塞,直到建立連線。它會返回一個新的 Socket。
- ComServerThread
- 此類擴充套件自 Thread,負責為一個客戶端提供服務。客戶端和伺服器之間將開啟 Socket 連線。客戶端和伺服器之間必須定義一個簡單的協議,伺服器必須理解客戶端想要從伺服器獲取什麼。客戶端將傳送一個終止命令,伺服器將終止 Socket 連線。ComServerThread 類負責處理所有客戶端請求,直到客戶端傳送一個終止命令。
程式碼清單 1.2:ComServerThread
/**
* -- A class extended from a Thread; Responsible to service one client --
*/
class '''ComServerThread''' extends Thread
{
private Socket clientSocket = null;
COM_DATA tDataFromClient;
COM_DATA tDataToClient;
ObjectInputStream oIn;
ObjectOutputStream oOut;
/**
* -- Constructor --
*/
public ComServerThread( Socket socket )
{
super( "ComServerThread" );
this.clientSocket = socket;
} // -- End of ComServerThread() constructor --
/**
* -- Overrun from the Thread (super) class --
*/
public void run()
{
try {
// --- Create the Writer; will be used to send data to client ---
oOut = new ObjectOutputStream( clientSocket.getOutputStream() );
// --- Create the Reader; will be used to get data from client ---
oIn = new ObjectInputStream( clientSocket.getInputStream() );
// --- Create a new protocol object ---
ComProtocol comp = new ComProtocol();
// --- Send something to client to indicate that server is ready ---
tDataToClient = '''comp.processInput( null );'''
'''sendDataToClient'''( tDataToClient, oOut );
// --- Get the data from the client ---
while ( true )
{
try {
tDataFromClient = '''getDataFromClient( oIn )''';
// --- Parse the request and get the reply ---
tDataToClient = '''comp.processInput( tDataFromClient );'''
// --- Send data to the Client ---
'''sendDataToClient'''( tDataToClient, oOut );
}
catch ( EOFException e ) {
System.out.println( "Client Disconnected, Bye, Bye" );
break;
}
// --- See if the Client wanted to terminate the connection ---
if ( tDataToClient.bExit )
{
System.out.println( "Client said Bye. Bye" );
break;
}
}
// --- Close resources; This client is gone ---
comp.Final();
oOut.close();
oIn.close();
clientSocket.close();
} catch ( IOException e ) {
e.printStackTrace();
}
} // -- End of run() Method --
/**
* Get data from Client
*/
private static COM_DATA '''getDataFromClient'''( ObjectInputStream oIn ) throws IOException
{
COM_DATA tDataFromClient = null;
// --- Initialize variables ---
// tDataFromClient = new COM_DATA();
while ( tDataFromClient == null )
{
try {
// --- Read Line Number first --
tDataFromClient = (COM_DATA) oIn.readObject();
} catch ( ClassNotFoundException e ) {
System.out.println( "ClassNotFound" );
}
}
System.out.println( "Get: " + tDataFromClient.comData );
return tDataFromClient;
} // --- getDataFromClient() Method ---
/**
* Send data to Client
*/
private static void '''sendDataToClient'''( COM_DATA tDataToClient,
ObjectOutputStream oOut ) throws IOException
{
System.out.println( "Sent: " + tDataToClient.comData );
oOut.writeObject( tDataToClient );
return;
} // -- End of sendDataToClient() Method --
} // --- End of ComServerThread class ---
|
- COM_DATA tDataFromClient
- 此變數將包含來自客戶端的資料物件。
- COM_DATA tDataToClient
- 此變數將包含要傳送給客戶端的資料物件。
- sendDataToClient
- 此方法將資料物件傳送給客戶端。
- getDataFromClient
- 此方法從客戶端獲取資料物件。
- processInput( tDataFromClient )
- 此方法屬於類
ComProtocol,用於解釋客戶端命令並返回將傳送回客戶端的資料物件。
- ComProtocol
- 此類實現並封裝通訊邏輯(協議)。該協議如下所示
- 客戶端發起連線。
- 伺服器接受連線併發送確認,通知已準備好。
- 客戶端傳送請求。
- 伺服器根據請求做出響應。
- ...
- 客戶端傳送
BYE請求。 - 伺服器確認
BYE請求並斷開 Socket 連線。 - 客戶端收到對
BYE的確認。
- 客戶端傳送
- ...
- 客戶端傳送
SHUTDOWN請求。 - 伺服器確認
SHUTDOWN請求並斷開連線,並停止偵聽其他客戶端。 - 客戶端收到對
SHUTDOWN的確認。
- 客戶端傳送
程式碼清單 1.3:ComProtocol
class '''ComProtocol'''
{
private static final int COM_STATUS_WAITING = 0;
private static final int COM_STATUS_READY_SENT = 1;
private static final int COM_STATUS_DATA_SENT = 2;
private static final int COM_STATUS_WAITING_FOR_TERMINALID = 3;
private int state = COM_STATUS_WAITING;
// --- Reference to 'BACK-END' module ---
private MqTeAccess mqTe;
...
/**
* Create a protokol object; CAll MQ INI function
*/
public ComProtocol()
{
int iRet = 0;
// --- Initialize 'BACK-END' modules ---
mqTe. ...
...
}
/**
* --- Process the Input and Create the output to the Client ---
*/
public COM_DATA processInput( COM_DATA theInput )
{
COM_DATA theOutput;
// --- Initialize Variables ---
theOutput = new COM_DATA();
// --- Check if the Clients want to disconnect ---
if ( theInput != null )
{
if ( theInput.comData.equals('''"!BYE.@"''') )
{
// --- The Client wants to terminate; Echo data back to client
theOutput.comData = "BYE.";
// --- Mark the communication to be terminated ---
theOutput.bExit = true;
// --- Set the internal state to wait for a new client ---
state = COM_STATUS_WAITING;
// --- Return Data object to be sent to the client ---
return theOutput;
}
if ( theInput.comData.equals('''"!SHUTDOWN.@"''') )
{
// --- The Client wants to terminate; Echo data back to client
theOutput.comData = "BYE.";
// --- Mark the communication to be terminated ---
theOutput.bExit = true;
// --- Tell the server to stop listening for new clients ---
ComServer.GL_listening = false;
// --- Set the internal state to wait for a new client ---
state = COM_STATUS_WAITING;
// --- Return Data object to be sent to the client ---
return theOutput;
}
}
if ( state == COM_STATUS_WAITING )
{
// --- Send ready Message to the Client ---
theOutput.comData = "Ready:";
// --- Set the internal state ready; and wait for TerminalId ---
state = COM_STATUS_WAITING_FOR_TERMINALID;
}
else if ( state == COM_STATUS_WAITING_FOR_TERMINALID )
{
int iRet;
// --- Get the Terminal ID ---
sTermId = theInput.comData;
// --- Call 'BACK-END' modules ... ---
mqTe. ...
...
// --- Send ready Message with the Server Version to the Client ---
theOutput.comData = "Ready;Server Version 1.0:";
// --- Set the internal state raedy; and wait for TerminalId ---
state = COM_STATUS_READY_SENT;
}
else if ( state == COM_STATUS_READY_SENT )
{
int iRet;
String sCommand = theInput.comData;
// --- Call 'BACK-END' modules ...
...
/*
** --- Check if we should get Response data ---
*/
if ( theInput.iRet == COM_DATA.NOWAIT_FOR_RESPONSE ) {
// -- Set the Output Value ---
theOutput.iRet = iRet;
theOutput.comData = "";
}
else {
// --- Call 'BACK-END' modules ---
mqTe. ...
// --- Set the Output Value ---
theOutput.comData = mqTe.sResponseBuffer;
theOutput.iRet = iRet;
}
}
return theOutput;
} // --- End of Method processInput() ---
} // --- End of ComProtocol Class Definition ---
----
|
- COM_DATA
- 是透過網路傳輸的資料結構類。該類只包含資料。
程式碼清單 1.4:COM_DATA
/**
* COM_DATA data structure
*/
public class COM_DATA implements Serializable
{
public String comData;
public boolean bExit;
public int iRet;
/**
* --- Constants values can be passed in in iRet to the Server ---
*/
static final int WAIT_FOR_RESPONSE = 0;
static final int NOWAIT_FOR_RESPONSE = 1;
/**
* Initialize the data structure
*/
public COM_DATA()
{
comData = "";
bExit = false;
iRet = 0;
} // -- End of COM_DATA() Constructor --
/**
* Copy over it contents
*/
public void copy( COM_DATA tSrc )
{
this.comData = tSrc.comData;
this.bExit = tSrc.bExit;
this.iRet = tSrc.iRet;
return;
}
} // -- End of COM_DATA class --
|
用於伺服器/服務的客戶端程式碼通常是使用者應用程式用來與伺服器互動的 API。藉助客戶端 API,使用者應用程式無需瞭解如何連線到伺服器以獲取服務。
- ComClient
- 此類是客戶端 API。應用程式使用此類與伺服器通訊。
以下是上述伺服器的客戶端類
程式碼清單 1.5:ComClient
public class ComClient
{
private Socket comSocket;
private ObjectOutputStream oOut;
private ObjectInputStream oIn;
private boolean IsItOpen = false;
/**
* --- Open Socket ---
*/
public void openCom( String sServerName,
int iPortNumber ) throws UnknownHostException,
IOException
{
try {
// --- Open Socket for communication ---
comSocket = new Socket( sServerName, iPortNumber );
// --- Get Stream to write request to the Server ---
oOut = new ObjectOutputStream( comSocket.getOutputStream() );
// --- Get Stream// to read from the Server
oIn = new ObjectInputStream( comSocket.getInputStream());
// --- Set internal Member variable that the Communication opened ---
IsItOpen = true;
} catch ( java.net.UnknownHostException e ) {
System.err.println( "(openCom:)Don't know about host: "+sServerName );
IsItOpen = false;
throw( e );
} catch ( java.io.IOException e ) {
System.err.println("(openCom:)Couldn't get I/O for the connection to: "+ sServerName );
IsItOpen = false;
throw( e );
}
}
/**
* --- Check if Socket is open ---
*/
public boolean isItOpen()
{
return IsItOpen;
}
/**
* --- Get data string from the Server ---
*/
public void getServerData( COM_DATA tServData ) throws IOException
{
// --- Initialize Variables ---
tServData.comData = "";
// --- Get the Response from the Server ---
try {
tServData.copy( (COM_DATA) oIn.readObject() );
}
catch ( ClassNotFoundException e ) {
System.out.println( "Class Not Found" );
}
System.out.println( "Server: " + tServData.comData );
if ( tServData.comData.equals("BYE.") )
{
tServData.bExit = true;
}
return;
}
/**
* --- Send data to the Server ---
*/
public void sendDataToServer( COM_DATA tServData ) throws IOException
{
// --- Send the data string ---
System.out.println( "Send: " + tServData.comData );
oOut.writeObject( tServData );
return;
}
/**
* --- Close Socket ---
*/
public void closeCom() throws IOException
{
oOut.close();
oIn.close();
comSocket.close();
IsItOpen = false;
}
}
|
- getServerData( COM_DATA tServData )
- 此方法從伺服器讀取資料並將值複製到
tServData物件。 - sendDataToServer( COM_DATA tServData )
- 此方法將
tServData物件透過網路傳送到伺服器。 - oIn.readObject()
- 此方法返回伺服器傳送的資料物件。
- oOut.writeObject( tServData )
- 此方法將資料物件傳送到伺服器。
