美文网首页
Java Socket

Java Socket

作者: sunpy | 来源:发表于2018-08-12 18:31 被阅读13次
风景c.jpg

什么是套接字Socket

Socket本身就是网络上的两个程序可以互相发送请求和接收请求,应用程序可以利用套接字在网络上进行数据传输。
Socket本身作为应用程序和网络层TCP/UDP之间的一个抽象层,在TCP中主要采用流套接字,采用的方式就是点对点通过字节流传输;UDP主要采用数据报套接字。

面向连接的入门例子

功能:客户端发送请求,服务器接收请求
服务端:

public class MyServer {

    class HandleTask {
        
        private Socket socket;
        
        public HandleTask() {}

        public HandleTask(Socket socket) {
            this.socket = socket;
        }
        
        public void handle() {
            StringBuilder sb = new StringBuilder("Hello: ");
            InputStream is = null;
            BufferedReader br = null;
            
            try {
                is = socket.getInputStream();
                br = new BufferedReader(new InputStreamReader(is));
                sb.append(br.readLine());
                System.out.println(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != br) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(sb.toString() + "-- 结束执行 --");
        }
    }
    
    
    public static void main(String[] args) throws IOException{
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                socket = serverSocket.accept();
                new MyServer().new HandleTask(socket).handle();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (null != socket) {
                socket.close();
            }
            
            if (null != serverSocket) {
                serverSocket.close();
            }
        }
    }
}

思路:
① 服务器端启动了ServerSocket,然后绑定了端口9999。
② 调用accept方法,阻塞等待客户端连接。
③ 当接收到客户端的请求之后,通过字节流获取客户端发来的信息。
多个客户端:

public class MyClient {

    public static void main(String[] args) {
        Socket socket = null;
        BufferedWriter bw = null;
        Scanner scanner = new Scanner(System.in);
        
        try {
            socket = new Socket("localhost", 9999);
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write(scanner.nextLine());
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scanner.close();
            
            try {
                if (null != bw) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

public class MyClient2 {

    public static void main(String[] args) {
        Socket socket = null;
        BufferedWriter bw = null;
        Scanner scanner = new Scanner(System.in);
        
        try {
            socket = new Socket("localhost", 9999);
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write(scanner.nextLine());
            bw.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            scanner.close();
            
            try {
                if (null != bw) {
                    bw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:


1.jpg

解释:
上面这个例子的缺陷就是如果是多个请求去等待服务器端去处理,那么服务器端会按照收到请求的顺序执行,这样如果第一个请求执行时间很长,那么第二个请求就很长时间等不到执行。

多线程执行多个客户端

场景
基于上面的例子,思考:服务器在很多情况下是需要接收来自很多个客户端的请求的。
解决办法
根据多线程时间分片方式来解决问题:多个客户端的请求,那么服务器采用每次接收到一个请求就创建一个线程来执行。
采用多线程改进的服务端

public class MyThreadServer {
    
    class HandleTask extends Thread{

        private Socket socket;
        public HandleTask() {}

        public HandleTask(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            StringBuilder sb = new StringBuilder("Hello: ");
            InputStream is = null;
            BufferedReader br = null;
            
            try {
                is = socket.getInputStream();
                br = new BufferedReader(new InputStreamReader(is));
                sb.append(br.readLine());
                System.out.println(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != br) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(sb.toString() + "-- 结束执行 --");
        }
    }
    
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                socket = serverSocket.accept();
                new MyThreadServer().new HandleTask(socket).start();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != serverSocket) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
2.jpg
3.jpg

方案使用的模型:
上面的程序专门使用一个接收者线程来专门负责监听客户端的请求,接收到客户端的请求,就为其创建一个新的线程去执行业务处理,最后通过字节流进行返回响应。这种模型就是BIO(阻塞IO)。
方案的缺点:
这种工作的模型有一个问题,就是当请求数很多时,就会创建和请求数一样多匹配线程。最终当并发量上来,那么系统的性能将会下降。

线程池执行多个客户端

场景:
基于上面的例子,使用多线程方式来处理请求,每个请求都创建一个匹配的线程,浪费线程资源。采用线程池管理线程,可以充分利用线程。
采用线程池改进的服务端:

public class MyThreadPoolServer {

    private static ExecutorService es = Executors.newCachedThreadPool();
    
    class HandleTask extends Thread {

        private Socket socket;
        public HandleTask() {}

        public HandleTask(Socket socket) {
            this.socket = socket;
        }
        
        @Override
        public void run() {
            StringBuilder sb = new StringBuilder("Hello: ");
            InputStream is = null;
            BufferedReader br = null;
            
            try {
                is = socket.getInputStream();
                br = new BufferedReader(new InputStreamReader(is));
                sb.append(br.readLine());
                System.out.println(sb.toString());
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (null != br) {
                        br.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                
                try {
                    if (null != is) {
                        is.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            
            try {
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            System.out.println(sb.toString() + "-- 结束执行 --");
        }
    }
    
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(9999);
            while (true) {
                socket = serverSocket.accept();
                es.execute(new MyThreadServer().new HandleTask(socket));
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (null != socket) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            
            try {
                if (null != serverSocket) {
                    serverSocket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
4.jpg

方案使用的模型:


5.jpg

相关文章

网友评论

      本文标题:Java Socket

      本文链接:https://www.haomeiwen.com/subject/yrydbftx.html