- 客户/服务器通信模式
TCP/UDP协议推动了客户/服务器通信模式的广泛运用。在通信个进程中,一个进程为客户进程,另一个为服务器进程。客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求。如下图所示,通常,一个服务器进程会同时与多个客户端进程服务,图中进程B1同时为客户进程A1、A2和B2提供服务。以下伪代码演示了服务器进程的大致工作流程:
while(true){
监听端口,等待客户请求;
响应客户请求;
}
- 用java编写客户/服务器程序 java网络程序都建立在tcp/ip协议基础上,致力于实现应用层。传应用层提供了套接字Socket接口,Socket封装了下层的数据传输细节,应用层的程序通过Socket来建立与远程主机的连接,以及进行数据传输。 站在应用层的角度,两个进程之间的一次通信过程从建立连接开始,接着交换数据,到断开连接结束。Socket可看做通信两端的收发器,进程通过Socket来收发数据,如图所示
3.创建EchoServer
package 最简单的客户服务器例子;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class EchoServer {
private int port=8888;
private ServerSocket serverSocket;
/*
服务器通过一直监听端口,来接受客户程序的连接请求。
在服务器程序中需要先创建一个ServerSocket对象,在
构造方法中指定监听的端口。构造方法负责在操作系统中
把当前进程注册为服务器进程。
*/
public EchoServer() throws IOException {
serverSocket=new ServerSocket(port);
System.out.println("服务器已经启动,,,");
}
public static void main(String[] args) throws IOException {
new EchoServer().service();
}
private void service() {
while (true){
Socket socket=null;
try {
/*
服务器程序接下来调用serverSocket对象的accept()方法
,该方法一直监听端口,等待客户的连接请求,如果
接收到一个连接请求,该方法就返回一个Socket对象
这个Socket对象与客户端的Socket对象形成了一条
通信线路。
*/
socket=serverSocket.accept();
System.out.println("接受到客户端连接:"+socket.getInetAddress());
/*
Socket类提供了getInputStream()和getOutputStream()方法,分别返回
输入流InputStream对象和输出流OutputStream对象。程序只需要向
输出流写数据,就能向对方发送数据;
只需要从输入流读数据就能接受来自对方的数据。
*/
//Scanner和PrintWriter都是带缓冲的流,能够读入和写出一行数据
Scanner scanner=new Scanner(socket.getInputStream());
//参数true表示每写一行,PrintWriter缓存就自动溢出,把数据写到目的地
PrintWriter printWriter=new PrintWriter(socket.getOutputStream(),true);
String msg=null;
while ((msg=scanner.nextLine())!=null){
System.out.println("收到:"+msg);
printWriter.println("echo:"+msg);
if (msg.equals("bye")){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (socket!=null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
EchoServer类最主要的方法是service()方法,他不断等待客户的连接请求,当serverSocket.accept()方法返回一个Socket对象时就意味着与一个客户端建立了连接。当客户端发来“bye”时,就会结束与客户的通信,调用socket.close()方法断开连接。
4.创建EchoClient
package 最简单的客户服务器例子;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class EchoClient {
/*
在EchoClient程序中,为了能与EchoServer通信,需要先建立一个
Socket对象
*/
private String host="localhost";//host表示EchoServer进程所在的主机的名字,当取值为localhost时,表示EchoClient和EchoServer进程运行在同一个主机上
private int port=8888;//参数port表示EchoServer进程监听的端口
private Socket socket;
public static void main(String[] args)throws IOException{
new EchoClient().talk();
}
//如果Socket创建成功,就表示客户端和服务器端建立了连接
public EchoClient() throws IOException {
socket=new Socket(host,port);
}
private void talk(){
try (//Socket socket=new Socket(host,port);
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(),true);
Scanner scanner=new Scanner(socket.getInputStream());
Scanner localScanner=new Scanner(System.in)) {
String line=null;
while (localScanner.hasNextLine()){
line=localScanner.nextLine();
printWriter.println(line);
System.out.println(scanner.nextLine());
if (line.equals("bye"))
break;
}
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
在EchoClient类中,最主要的方法是他talk()方法。该方法不断的读取用户从控制台输入的字符串,然后把它发送给EchoServer,再把EchoServer返回的字符串打印到控制台。如果用户输入的是“bye”,就会结束与EchoSever的通信,调用socket.close();方法断开连接。
在客户进程中,Socket对象包含了本地及对方服务器进程的地址和端口信息,在服务器进程中,Socket对象也包含了本地以及对方客户进程的地址和端口信息。客户进程允许建立多个连接,每个连接都有唯一的端口。在编写网络程序时,一般显示的为服务器程序中的SeverSocket设置端口,而不必考虑客户进程所使用的端口。
5.小结
java程序致力于实现应用层。传输层向应用层提供了Socket接口,Socket封装了下层的数据传输细节,其应用层程序通过建立Socket来建立与远程主机的连接,以及进行数据传输。