zoukankan      html  css  js  c++  java
  • 【Java】ServerSocket的学习笔记

    公司有本《Java网络编程》一直闲置在书架上,反正我对Socket方面不太懂,今天跟着书学习一番。

    > 参考的优秀书籍

    《Java网络编程》 --中国电力出版社

    > 最简单的服务器端

    当客户端连接进来,向客户端发送“welcome”以表咋程序员的亲切感~~

    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    
    public class SimpleServerSocket {
    
        public static void main(String[] args) {
            ServerSocket ss = null;
            Socket s = null;
            OutputStreamWriter osw = null;
            try {
                ss = new ServerSocket(9000);
                
                s = ss.accept();
                osw = new OutputStreamWriter(s.getOutputStream());
                osw.write("welcome..." + System.getProperty("line.separator"));
                osw.flush();
                System.out.println("outputed.");
                
            } catch (IOException e) {
                System.out.println("socket exception.");
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                CloseableCloser.close(osw);
                CloseableCloser.close(s);
                CloseableCloser.close(ss);
            }
        }
    
    }
    View Code

    有许多资源需要关闭,那就写一个小的工具类来关闭吧

    import java.io.Closeable;
    import java.io.IOException;
    
    
    public class CloseableCloser {
        
        public static void close(Closeable c) {
            if (c == null) {
                return;
            }
            
            try {
                c.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                System.out.println("exception when closing.");
                e.printStackTrace();
            }
        }
    
    }
    View Code

    通过Linux telnet一下,看到反馈了。

    当然用Windows telnet也一样的,只是Windows telnet跳了一个页面,不方便截图而已。

    注意:如果服务端输出语句时没有加换行符,我在Linux、Windows测试时都没看到打印welcome哦。

    telnet xx.xx.xx.xx 9000
    Trying xx.xx.xx.xx...
    Connected to xx.xx.xx.xx.
    Escape character is '^]'.
    welcome...
    Connection closed by foreign host.
    View Code

    当然也可通过Java Socket编写客户端去连接

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    
    public class SimpleSocket {
    
        public static void main(String[] args) {
            Socket s = null;
            BufferedReader br = null;
            String line = null;
            try {
                s = new Socket("127.0.0.1", 9000);
                br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                while ((line = br.readLine()) != null) {
                    System.out.println(line);
                }
                
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                CloseableCloser.close(s);
            }
    
        }
    
    }
    View Code

    > 提供持续的服务,同时处理好不同的异常

    看上面的服务端程序,可以发现它只能处理一个任务,而服务端一般来说是提供持续的服务的嘛,那么我们加一个while true呗。

    另外,与客户端交互的Socket的异常和ServerSocket的异常是不是需要分开处理一下呢?试想,你一定不想某一个业务出现异常了,导致整个服务端的服务都中止的。

    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    
    public class SimpleServerSocket {
    
        public static void main(String[] args) {
            ServerSocket ss = null;
            Socket s = null;
            OutputStreamWriter osw = null;
            try {
                ss = new ServerSocket(9000);
                
                while (true) {
                    try {
                        s = ss.accept();
                        osw = new OutputStreamWriter(s.getOutputStream());
                        osw.write("welcome..." + System.getProperty("line.separator"));
                        osw.flush();
                        System.out.println("outputed.");
                        
                    } catch (IOException e) {
                        System.out.println("socket exception.");
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                        
                    } finally {
                        CloseableCloser.close(osw);
                        CloseableCloser.close(s);
                    }
                }
                
            } catch (IOException e) {
                System.out.println("server socket exception.");
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (ss != null) {
                    CloseableCloser.close(ss);
                }
            }
        }
    
    }
    View Code

    这样,你用多个客户端不断地连接,它都给你响应了。

    > 并发地处理任务

    上述的服务端代码有一段线程睡眠的代码用于模拟业务处理所需的时间的,我们把它的注解解开,然后用不同客户端连接,可以发现,程序在同一时间只能处理一个任务嘛。

    而且,由于服务端同时只能处理一个请求,其他请求就堵塞了,操作系统会将请求同一端口的请求存储在一个先进先出的队列中,然而这个队列有长度限制。当然,这个限制各个操作系统不同。

    不信,那么我们用Java多线程发送150个请求:

    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    
    public class MultipleSocket extends Thread {
    
        public static void main(String[] args) {
            for (int i = 0; i < 150; i++) {
                new MultipleSocket().start();
            }
        }
    
        public void run() {
            Socket s = null;
            BufferedReader br = null;
            String line = null;
            try {
                System.out.println(this.getName() + " is started.");
                
                s = new Socket("127.0.0.1", 9000);
                br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                while ((line = br.readLine()) != null) {
                    System.out.println(line);
                }
                
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                CloseableCloser.close(br);
                CloseableCloser.close(s);
            }
        }
    
    }
    View Code

    不出意外,将报以下异常:

    java.net.ConnectException: Connection refused: connect
        at java.net.DualStackPlainSocketImpl.connect0(Native Method)
        at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:79)
        at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
        at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
        at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
    View Code

    那么我们就转为线程处理呗!

    这里换为多线程处理,同时限制同时最多处理2个线程(需要限制几个线程数,自己设置哦)。

    为什么要限制线程数量呢?如果同时许多了客户端连接,超过一定数量,最直接的结果就是内存耗尽了。

    关于如何限制线程数量,可以参考以前的博文:【多线程】并发执行指定数量的线程

    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    
    public class SimpleServerSocket {
    
        public static void main(String[] args) {
            ServerSocket ss = null;
            Socket s = null;
            OutputStreamWriter osw = null;
            ExecutorService es = Executors.newFixedThreadPool(2);
            
            try {
                ss = new ServerSocket(9000);
                
                while (true) {
                    s = ss.accept();
                    es.execute(new BusinessThread(s));
                }
                
            } catch (IOException e) {
                System.out.println("server socket exception.");
                // TODO Auto-generated catch block
                e.printStackTrace();
            } finally {
                if (ss != null) {
                    CloseableCloser.close(ss);
                }
            }
        }
    
    }
    View Code
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.net.Socket;
    import java.util.concurrent.TimeUnit;
    
    
    public class BusinessThread implements Runnable {
        
        Socket s = null;
    
        public BusinessThread(Socket s) {
            super();
            this.s = s;
        }
    
        public void run() {
            OutputStreamWriter osw = null;
            try {
                osw = new OutputStreamWriter(s.getOutputStream());
                osw.write("welcome..." + System.getProperty("line.separator"));
                osw.flush();
                System.out.println("outputed.");
                
                // 模拟这里的业务进行得很慢
                try {
                    TimeUnit.SECONDS.sleep(10);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                
            } catch (IOException e) {
                System.out.println("socket exception.");
                // TODO Auto-generated catch block
                e.printStackTrace();
                
            } finally {
                CloseableCloser.close(osw);
                CloseableCloser.close(s);
            }
        }
    
    }
    View Code
  • 相关阅读:
    oracle 11g 断电后 无法启动 ora 00600 kcratr_scan_lastbwr 修复小计
    arcgis for python (arcpy) 入门
    解决 构造函数 包含业务逻辑 IDE无法初始化界面的问题
    arcgis 10.1 发布 气温插值GP服务小计
    执行 Animation先决条件
    ASP.NET MVC3学习心得视图和模型
    Axure使用心得分享
    ASP.NET MVC3学习心得表单和HTML辅助方法
    ASP.NET MVC3学习心得入门和控制器
    WindowsPhone 中SQL CE数据库的使用
  • 原文地址:https://www.cnblogs.com/nick-huang/p/5207655.html
Copyright © 2011-2022 走看看