传输层向应用层提供了套接字Socket接口,Socket封装了下层的数据传输细节,应用层的程序通过Socket来建立与远程主机的连接,以及进行数据传输。
站在应用层的角度,两个进程之间的一次通信过程从建立连接开始,接着交换数据,到断开连接结束。套接字可看做是通信线路两端的收发器,进程通过套接字来收发数据。如下图:
套接字可看做是通信连接两端的收发器
java中有3中套接字类型:
1、java.net.Socket
2、java.net.ServerSocket
3、java.net.DatagramSocket。
其中Socket和ServerSocket类建立在TCP协议基础上,DatagramSocket类建立在UDP协议基础上。Java网络程序都采用客户/服务器通信模式。
以EchoServer和EchoClient为例,介绍如何使用ServerSocket和Socket来编写服务器程序和客户程序。
1、创建EchoServer:
服务器与客户利用ServerSocket和Socket来通信
EchoServer.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.Socket; import java.net.UnknownHostException; public class EchoClient { public static void main(String[] args) throws IOException { new EchoClient1().talk(); } } class EchoClient1{ private String host="localhost"; private int port=8001; private Socket socket; public EchoClient1() throws UnknownHostException, IOException{ socket=new Socket(host, port); } private PrintWriter getWriter(Socket socket)throws IOException{ OutputStream socketOut=socket.getOutputStream(); return new PrintWriter(socketOut,true); } private BufferedReader getReader(Socket socket) throws IOException{ InputStream socketIn=socket.getInputStream(); return new BufferedReader(new InputStreamReader(socketIn)); } public void talk() throws IOException{ try { BufferedReader br=getReader(socket); PrintWriter pw=getWriter(socket); BufferedReader localReader=new BufferedReader(new InputStreamReader(System.in)); String msg=null; while((msg=localReader.readLine())!=null){ pw.println(msg); System.out.println(br.readLine()); if(msg.equals("bye")){ break; } } } catch (IOException e) { e.printStackTrace(); }finally{ try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
EchoServer类最主要的方法为service()方法,它不断等待客户的连接请求,当serviceSocket.accept()方法返回一个Socket对象中得到输出流和输入流,并且分别为PrintWriter和BufferedReader来装饰它们。然后不断调用BufferedReader的readLine()方法读取客户发来的字符串XXX,在调用PriterWriter的println()方法向客户返回字符串echo:XXX。当客户发来的字符串为“bye”时,就会结束语客户的通信,调用socket.close()方法断开连接。
2、创建EchoClient
EchoClient.java
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class EchoServer { public static void main(String[] args) throws IOException { new EchoServer1().service(); } } class EchoServer1{ private int port=8001; private ServerSocket serverSocket; public EchoServer1() throws IOException{ serverSocket=new ServerSocket(port); System.out.println("服务器启动"); } public String echo(String msg){ return "echo:"+msg; } private PrintWriter getWriter(Socket socket) throws IOException{ OutputStream socketOut=socket.getOutputStream(); return new PrintWriter(socketOut,true); } private BufferedReader getReader(Socket socket) throws IOException{ InputStream socketIn=socket.getInputStream(); return new BufferedReader(new InputStreamReader(socketIn)); } public void service(){ while(true){ Socket socket=null; try { socket=serverSocket.accept(); System.out.println("New connection accepted"+socket.getInetAddress()+":"+socket.getPort()); BufferedReader br=getReader(socket); PrintWriter pw=getWriter(socket); String msg=null; while((msg=br.readLine())!=null){ System.out.println(msg); pw.println(echo(msg)); if(msg.equals("bye")){ break; } } } catch (IOException e) { e.printStackTrace(); }finally{ try { if(socket!=null){ socket.close(); } } catch (IOException e) { e.printStackTrace(); } } } } }
在EchoClient类中,最主要的方法为talk()方法。该方法不断读取用户从控制台输入的字符串,然后把它发送给EchoServer,在把EchoServer返回的字符串打印到控制台。如果用户输入的字符串为“bye”,就会结束与EchoServer的通信,调用socket.close()方法断开连接。
运行范例是,需要打开两个Dos界面,先在一个DOS界面中运行“java EchoServer”命令,再在另一个DOS界面中运行“java EchoClient”命令。如下图运行两个程序的DOS界面。在EchoClient控制台,用户输入字符串"hello",程序就会输出:“echo:hello”。
运行EchoServer和EchoClient程序