场景:时间服务器用于Internet众多的网络设备时间同步。时间服务器的默认端口为37,当时间服务器收到客户端的连接请求,立即向返回一个网络时间。
网络时间为一个4字节的无符号整数,它表示当前时间和1900年元月1日0点整的秒数值差。

JTimeClient.java

import java.net.*;
import java.io.*;

public class JTimeClient {
  private String server;  //时间服务器
  private int port;       //端口号

  public JTimeClient(String server) {
    this(server, 37);//时间服务器默认端口号37
  }	
  
  public JTimeClient(String server, int port) {
  	this.server = server;
  	this.port = port;
  }
  
  //返回网络时间, -1表示出错
  public long getNetTime() {
  	Socket socket = null;
  	InputStream in = null;
    try {
      //连接
      socket = new Socket(server, port);
      //时间服务器
      in = socket.getInputStream( );
      //读取数据,网络时间为4字节无符号整数,
      //表示基于1900年元月1日0点的秒数
      long netTime = 0;
      for(int i=0; i<4; i++) {
      	netTime = (netTime << 8) | in.read();
      }
      
      return netTime;
    }
    catch (UnknownHostException e) {
   	  //e.printStackTrace();
    }
    catch (IOException e) {
      //e.printStackTrace();
    }
    finally {//安全释放资源
      try {//关闭输入流
        if(in != null) in.close();
      }	catch (Exception e) {}

      try {//关闭连接
        if(socket != null) socket.close();
      }	catch (Exception e) {}
    }
  	return -1;
  }
  
  public static void main(String[] args) {
  	JTimeClient timeClient = null;
  	
	if(args.length == 1) {//命令行参数指定时间服务器
	  timeClient = new JTimeClient(args[0]);
	}
	else if(args.length == 2) {//指定时间服务器及其端口
	  timeClient = new JTimeClient(args[0], Integer.parseInt(args[1]));
	}
	else {
	  System.out.println("Usage: java JTimeClient TimeServer Port");
	  return;
	}

    System.out.println("Time:" + timeClient.getNetTime());
  }
}


如果未启动时间服务器,直接运行本程序结果:

java JTimeClient localhost

Time:-1


轮流工作方式:JTimeServer.java

import java.net.*;
import java.io.*;
import java.util.Date;

public class JTimeServer implements Runnable {
  private int port;
  	
  public JTimeServer() {
  	this(37);//时间服务器默认端口号37
  }

  public JTimeServer(int port) {
  	this.port = port;
  }
  
  public void run() {
  	try {
  	  //创建服务器套接字  	 
      ServerSocket server = new ServerSocket(port);
      //轮流处理多个客户端的请求
  	  while(true) {
        Socket connection = null;
        try {
          //等待客户端连接请求
          connection = server.accept();
          
          //利用Date.getTime获取当前系统时间(单位毫秒)
          //常数2208988800为网络时间和系统时间差
          Date now = new Date();
          long netTime = now.getTime()/1000 + 2208988800L;
        
          byte[] time = new byte[4];
          for(int i=0; i<4; i++) {
          	time[3 - i] = (byte)(netTime &0x00000000000000FFL);
          	netTime >>= 8;
          }
          //获取套接字输入流,并写入网络时间
          OutputStream out = connection.getOutputStream();
		  
          out.write(time);
          out.flush();      
        }
        catch (IOException e) {
        }
        finally {//关闭当前连接
          if (connection != null) connection.close(); 
        }
  	  }
  	}
  	catch(IOException e) {
  	}
  }
  
  
  public static void main(String[] args) {
  	JTimeServer timeServer = null;
  	
	if(args.length == 0) {
	  timeServer = new JTimeServer();
	}
	else if(args.length == 1) {//命令行参数指定时间服务器监听端口
	  timeServer = new JTimeServer(Integer.parseInt(args[0]));
	}
	else {
	  System.out.println("Usage: java JTimeServer Port");
	  return;
	}
	
	(new Thread(timeServer)).start();
  }
}


运行结果:

3558353322


并发工作方式:JTimeServer2.java

import java.net.*;
import java.io.*;
import java.util.Date;
class JTimeThread extends Thread {
  private Socket connection;  
  public JTimeThread(Socket connection) {
  	this.connection = connection;
  }  
  public void run() {
  	try {
      //利用Date.getTime获取当前系统时间(单位毫秒)
      //常数2208988800为网络时间和系统时间差
      Date now = new Date();
      long netTime = now.getTime()/1000 + 2208988800L;
        
      byte[] time = new byte[4];
      for(int i=0; i<4; i++) {
        time[3 - i] = (byte)(netTime &0x00000000000000FFL);
        netTime >>= 8;
      }
      //获取套接字输入流,并写入网络时间
      OutputStream out = connection.getOutputStream();
      out.write(time);
      out.flush();      
  	}
  	catch(IOException e) {
  	}  
    finally {//关闭和客户端的连接
      try {
        if (connection != null) connection.close(); 
      } catch (IOException e) {}
    }
  }
} 
public class JTimeServer2 implements Runnable {
  private int port;  	
  public JTimeServer2() {
  	this(37);//时间服务器默认端口号37
  }
  public JTimeServer2(int port) {
  	this.port = port;
  }  
  public void run() {
  	ServerSocket server = null;
  	try {
  	  //创建服务器套接字  	 
      server = new ServerSocket(port);
      //轮流处理多个客户端的请求
  	  while(true) {
        try {
          //等待客户端连接请求
          Socket connection = server.accept();
          //创建并启动一个JTimeThread线程来服务客户端请求
          (new JTimeThread(connection)).start();
        }
        catch (IOException e) {
        }
  	  }
  	}
  	catch(IOException e) {
  	}
  	finally { //关闭服务器Socket
      try {
        if(null != server) server.close();
      } catch (IOException e) {}
  	}
  }  
  public static void main(String[] args) {
  	JTimeServer2 timeServer = null;  	
	if(args.length == 0) {
	  timeServer = new JTimeServer2();
	}
	else if(args.length == 1) {//命令行参数指定时间服务器监听端口
	  timeServer = new JTimeServer2(Integer.parseInt(args[0]));
	}
	else {
	  System.out.println("Usage: java JTimeServer2 Port");
	  return;
	}	
	(new Thread(timeServer)).start();
  }
}


Date.getTime()获取当前系统时间,这是一个基于1927元月1日0点的long型毫秒数。


PS注意:

在向Socket写入数据的时候,我们还要注意一个非常重要的细节:网络字节顺序为高字节在前。


Java 新IO再进化Selector创建一个非阻塞的服务器,此服务器向客户端返回当前的系统时间。


import java.net.InetSocketAddress ;
import java.net.ServerSocket ;
import java.util.Set ;
import java.util.Iterator ;
import java.util.Date ;
import java.nio.channels.ServerSocketChannel ;
import java.nio.ByteBuffer ;
import java.nio.channels.SocketChannel ;
import java.nio.channels.Selector  ;
import java.nio.channels.SelectionKey  ;
public class DateServer{
	public static void main(String args[]) throws Exception {
		int ports[] = {8000,8001,8002,8003,8005,8006} ; // 表示五个监听端口
		Selector selector = Selector.open() ;	// 通过open()方法找到Selector
		for(int i=0;i<ports.length;i++){
			ServerSocketChannel initSer = null ;
			initSer = ServerSocketChannel.open() ;	// 打开服务器的通道
			initSer.configureBlocking(false) ;	// 服务器配置为非阻塞
			ServerSocket initSock = initSer.socket() ;
			InetSocketAddress address = null ;
			address = new InetSocketAddress(ports[i]) ;	// 实例化绑定地址
			initSock.bind(address) ;	// 进行服务的绑定
			initSer.register(selector,SelectionKey.OP_ACCEPT) ;	// 等待连接
			System.out.println("服务器运行,在" + ports[i] + "端口监听。") ;
		}
		// 要接收全部生成的key,并通过连接进行判断是否获取客户端的输出
		int keysAdd = 0 ;
		while((keysAdd=selector.select())>0){	// 选择一组键,并且相应的通道已经准备就绪
			Set<SelectionKey> selectedKeys = selector.selectedKeys() ;// 取出全部生成的key
			Iterator<SelectionKey> iter = selectedKeys.iterator() ;
			while(iter.hasNext()){
				SelectionKey key = iter.next() ;	// 取出每一个key
				if(key.isAcceptable()){
					ServerSocketChannel server = (ServerSocketChannel)key.channel() ;
					SocketChannel client = server.accept() ;	// 接收新连接
					client.configureBlocking(false) ;// 配置为非阻塞
					ByteBuffer outBuf = ByteBuffer.allocateDirect(1024) ;	//
					outBuf.put(("当前的时间为:" + new Date()).getBytes()) ;	// 向缓冲区中设置内容
					outBuf.flip() ;
					client.write(outBuf) ;	// 输出内容
					client.close() ;	// 关闭
				}
			}
			selectedKeys.clear() ;	// 清楚全部的key
		}
		
	}
}



注意:服务器运行后并不会像传统的Socket那样立刻关闭服务器,而是会继续等待下一次的连接。