作者    孙卫琴

      Java 网络程序都采用客户/服务器通信模式, 致力于实现应用层。 传输层向应用层提供了套接字 Socket 接口, Socket 封装了下层的数据传输细节, 应用层的程序通过 Socket 来建立与远程主机的连接, 以及进行数据传输。


      站在应用层的角度,两个进程之间的一次通信过程从建立连接开始,接着交换数据,到断开连接结束。 套接字可看做是通信线路两端的接收器,进程通过套接字来收发数据。

      如 进程A1-------Socket <------tcp连接----->Socket-------进程B1

 

      在Java中, 有3种套接字类: java.net.Socket、java.net.ServerSocket 和 DatagramSocket。 其中 Socket 和 ServerSocket 类建立在 TCP协议基础上; DatagramSocket 类建立在UDP协议基础上。

 

  •  创建 EchoClient
package com.sockettest;
                
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;                                
public class EchoClient {
 private String host = "localhost";//EchoServer进程所在的主机的名字,这里为"localhost"表示EchoClient和EchoServe
           //进程运行在同一个主机上.
 private int port = 8000;    //EchoServer进程监听的端口
 private Socket socket;
 
 public EchoClient() throws IOException{
  if(socket == null){
   socket = new Socket(host,port);//如果Socket对象成功创建,就表示建立了EchoClient和EchoServer之间的连接.
   System.out.println("连接服务器!");
  }
 }
 
 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));
 }
 
 /**
  * EchoClient 类中最主要的方法. 该方法不断读取用户从控制台输入的字符串, 然后把它发送给 EchoServer, 再把EchoServer
  * 返回的字符串打印到控制台. 如果用户输入的字符串为 "bye", 就会结束与 EchoServer的通信, 调用socket.close()方法断开
  * 连接.
  */
 public void talk() {
  try{
   //EchoClient和EchoServer建立连接后, EchoClient从Socket对象中得到输出流和输入流, 就能与EchoServer交换数据.
   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"))   //如果客户发送的信息为"bye",就结束通信
     break;
   }
   
   
  }catch(IOException e){
   e.printStackTrace();
  }finally{
   try{
    if(socket != null) socket.close();  //断开连接
   }catch(IOException e){e.printStackTrace();}
  }
 }
 
 
 public static void main(String args[])throws IOException{
  new EchoClient().talk();
 }
} 
 
• 创建 EchoServer
package com.sockettest;
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 {
 private int port = 8000;
 private ServerSocket serverSocket;
 
 public  EchoServer() throws IOException{
  if(serverSocket == null){
   //服务器程序通过一直监听端口, 来接受客户程序的连接请求. 在服务器程序中, 需要先创建一个ServerSocket对象, 
   //在构造方法中指定监听的端口.
   //ServerSocket的构造方法负责在操作系统中把当前 进程注册为服务器进程.
   serverSocket = new ServerSocket(port);          //监听8000端口
   System.out.println("服务器启动!");
  }
 }
 
 public String echo(String msg){
  return "echo:" + msg;
 }
 
 //Socket类提供getOutputStream()方法,返回输出流OutputSteam对象,程序只需向输出流写数据, 就能向对方发送数据,
 //Socket的输出流可以用过滤留来装饰, 这个方法中先获取输出流, 然后用PrintWriter装饰它, PrintWriter的
 //println()方法能够写一行数据.
 private PrintWriter getWriter(Socket socket) throws IOException{
  OutputStream socketOut = socket.getOutputStream();
  return new PrintWriter(socketOut,true);//参数true表示每写一行, PrintWriter缓存就自动溢出, 把数据写到目的地
 }
 
 //Socket类提供getInputStream()方法, 返回输入流InputStream对象, 程序只需从输入流读数据,就能接收来自对方的数据,
 //Socket的输入流也可以用过滤流来装饰, 这个方法中先获取输入流, 然后用BufferedReader装饰它, BufferedReader的
 //readLine()方法能够读入一行数据.
 private BufferedReader getReader(Socket socket) throws IOException{
  InputStream socketIn = socket.getInputStream();
  return new BufferedReader(new InputStreamReader(socketIn));
 }
 
 /**
  * EchoServer 类最主要的方法, 它不断等待客户的连接请求, 当serverSocket.accept()方法返回一个Socket对象时, 
  * 就意味着与一个客户建立了连接,接下来从Socket对象中得到输出流和输入流, 并且分别用PrintWriter 和BufferedReader
  * 来装饰它们. 然后不断调用 BufferedReader 的readLine()方法读取客户发来的字符串XXX, 再调用PrintWriter 的 
  * println()方法向客户返回字符串 echo:XXX. 当客户发来的字符串为 "bye" 时, 就会结束与客户的通信,
  * 调用socket.close() 方法断开连接. 
  * PS.这个方法无法同时与多个客户通信. EchoServer接收到一个客户连接, 就与客户进行通信, 通信完毕后断开连接,然后再
  *    接收下一个客户连接. 假如同时有多个客户请求连接, 这些客户就必须排队等候EchoServer的响应.
  */
 public void service(){
  while(true){
   Socket socket = null;
   try{
    //服务器程序调用ServerSocket对象的accept()方法, 该方法一直监听端口, 等待客户的连接请求, 如果接受到一个
    //连接请求, accept()方法就会返回一个Socket对象, 这个Socket对象与客户端的Scoket对象形成了一条通信线路.
    socket = serverSocket.accept();    //等待客户连接
    
    //Socket对象包含了客户的地址和端口
    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"))  //如果客户端发送的信息为"bye", 就结束通信
      break;
    }
    
   }catch(IOException e){
    e.printStackTrace();
   }finally{
    try{
     if(socket != null) socket.close();  //断开连接
    }catch(IOException e){
     e.printStackTrace();}
   }
  }
 }
 
 
 
 public static void main(String[] args) throws IOException {
  // TODO Auto-generated method stub
  new EchoServer().service();
 }}

 

 

先运行 EchoServer,  再运行 EchoClient, 在EchoClient的控制台中输入字符串, 当输入的字符串为bye时,  就会结束与EchoServer的通信.