import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.logging.Level; import java.util.logging.Logger; public class TCPServer implements Runnable{ /*绑定监听IP:port * 1、起一个主线程进行监听,采用 thread per request的方式处理请求; * 2、每个请求,回送一个应答*/ public static final int MAX_THREAD = 10; private String ip = null; private int port = 0; private SocketAddress listeningAddress = null; private ServerSocket serverSocket = null; private Logger logger = Logger.getLogger(this.getClass().getName()); //Use default logger of JAVA private Socket connSocket = null; private ExecutorService threadpool = Executors.newFixedThreadPool(MAX_THREAD); private Map<String,ConnectionInfo> connectionPool = Collections.synchronizedMap(new HashMap<String,ConnectionInfo>()); public volatile boolean isRunning = true; public TCPServer(String ip,int port) { this.ip = ip; this.port = port; listeningAddress = new InetSocketAddress(ip, port); try { serverSocket = new ServerSocket(port, 0, ((InetSocketAddress)listeningAddress).getAddress()); logger.log(Level.INFO, "Server started...listening on "+this.ip+":"+this.port); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void run() { // TODO Auto-generated method stub while( true == isRunning ) { try { connSocket = serverSocket.accept(); if( null != connSocket ) { //handle connect in newly constructed thread Future<?> future = threadpool.submit(new RequestHandler(connSocket)); String connectionKey = new String(connSocket.getInetAddress().getHostAddress()+":"+connSocket.getPort()); connectionPool.put(connectionKey, new ConnectionInfo(future,connSocket)); /*讲连接放入server的连接池,便于后续使用*/ } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } private class ConnectionInfo { public Future<?> future = null; public Socket socket = null; public ConnectionInfo(Future<?> future,Socket socket) { this.future = future; this.socket = socket; } } private class RequestHandler implements Runnable { private final int RECV_BUF_LEN = 1024; private Socket connSocket = null; private InputStream in = null; private OutputStream out = null; private BufferedInputStream bufin = null; private BufferedOutputStream bufout = null; private boolean isRunning = true; private byte[] recvbuffer = new byte[RECV_BUF_LEN]; private int handleswitch = 2; //recvbuffer := make([]byte,1024,1024) /*define a slice in golang*/ //var recvbuffer [1024] byte /*define an array in golang*/ public RequestHandler(Socket connSocket) { this.connSocket = connSocket; } @Override public void run() { if( null != connSocket ) { try { in = connSocket.getInputStream(); out = connSocket.getOutputStream(); bufin = new BufferedInputStream(connSocket.getInputStream()); //bufout = new BufferedOutputStream(connSocket.getOutputStream(),200); /*如果创建的时候不指定buffer大小,默认是8K;如果buffer较小,buffer满的时候会自动执行flush;stream关闭的时候,会自动flush*/ bufout = new BufferedOutputStream(connSocket.getOutputStream()); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } } // TODO Auto-generated method stub //get info from the socket via stream while( !Thread.currentThread().isInterrupted() && true == isRunning) { if( null != connSocket ) { if( handleswitch == 1 ) { //第一种方式,通过传统的stream方式读取 if( null != in && null != out) { // in.read(); /*返回stream中的下一个byte,作为int类型返回,如果stream到达末尾,则返回-1*/ try { int cnt = in.read(recvbuffer); if( cnt > 0) { //recv success String received = new String(recvbuffer,0,cnt); /*注意使用有效的数据长度,而不是整个buffer的长度创建string,避免乱码*/ //logger.log(Level.INFO, "Received: "+received); //send an echo as soon as possible String response = new String("I'm the server,received your message:"+received); logger.log(Level.INFO, response); out.write(response.getBytes()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); //send an event to TCPSever to notify it to handle connection failure. isRunning = false; } } } else if( handleswitch == 2 ) { //第二种方式,使用bufferedstream的方式读取 if( bufin != null && bufout != null ) { try { int cnt = bufin.read(recvbuffer); //底层是调用InputStream的read逐个字节读到buffer里的 if( cnt > 0 ) { String received = new String(recvbuffer,0,cnt); /*注意使用有效的数据长度,而不是整个buffer的长度创建string,避免乱码*/ //logger.log(Level.INFO, received); //send an echo as soon as possible String response = new String("I'm the server,received your message:"+received); logger.log(Level.INFO, response); bufout.write(response.getBytes()); bufout.flush(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); try { connSocket.close(); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } isRunning = false; } } /*综上:个人认为,bufferedstream的意义,在发送的时候大于接收的时候,从源码看到,接收的时候,基本还是调用底层的read逐个字节读的; * 在发送端,业务线程可以不断把数据往buffer里填,直到需要发送或者buffer满的时候,调用或触发flush将数据提交给OS(并非提交给 * 物理设备,如网卡),这样在某些场景下,业务线程不必每填一次数据都write一把,提高了效率。*/ } else if( handleswitch == 3 ) { //第三种方式,使用reader/writer方式读取 try { //从字节流到字符流再到文本 BufferedReader br = new BufferedReader(new InputStreamReader(connSocket.getInputStream(), "UTF-8")); if( null != br ) { String line = null; //BufferedReader 有几种读的方式:1、逐个字符读取;2、将字符流顺序读取到指定的缓存中;3、逐行读取(以换行符为准) while( null != ( line = br.readLine()) ) { //readline 以换行符、字符串结束符为准 //如果读不到行结束,该调用会阻塞并一直读取,直到字符流结束,内部用StringBuffer保存每次读取的string logger.log(Level.INFO, "Server received:"+line); } } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } else { logger.log(Level.INFO, "Socket is null! Stop reading!"); isRunning = false; } } } } }