• 【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
  • 相关阅读:
    jQuery插件开发入门
    [转]JS学习总结-技巧、方法、细节
    JS无法获取display为none的隐藏元素的宽度和高度的解决方案
    vuejs2.0运用原生js实现简单的拖拽元素功能
    HTML5效果:Canvas 实现圆形进度条并显示数字百分比
    git常用命令总结以及用github来展示你的前端页面
    jQuery 对AMD的支持(Require.js中如何使用jQuery)
    vue+springboot上传和下载附件功能
    springboot+vue实现文件上传
    Spring boot+Vue全栈开发---Spring Boot文件上传
  • 原文地址:https://www.cnblogs.com/nick-huang/p/5207655.html
Copyright © 2020-2023  润新知