客户端服务器
外观
< Java 编程
导航并发编程主题: ) |
在 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 )
- 此方法将数据对象发送到服务器。