博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
从Socket入门到BIO,PIO,NIO,multiplexing,AIO(未完待续)
阅读量:4947 次
发布时间:2019-06-11

本文共 11256 字,大约阅读时间需要 37 分钟。

Socket入门

最简单的Server端读取Client端内容的demo

public class Server {    public static void main(String [] args) throws Exception{        ServerSocket ss = new ServerSocket(9000);        Socket s = ss.accept();        InputStream input = s.getInputStream();        byte[] bytes = new byte[1024];        int len = input.read(bytes);        System.out.println(new String(bytes,0,len));    }}

打开浏览器,输入localhost:9000

可以看到控制台输出了如下内容(HTTP请求头)

 或者命令行输入:telnet localhost 9000 , 然后按下ctrl+]   然后再输入: send 发送的内容    , 然后就可以再IDEA控制台看到send的内容了.

 但是现在手头是Mac系统,不知道为啥send不好使....windows下测过,肯定好使.

最简单的Server端写入到Client端内容的demo

public class Server2 {    public static void main(String[] args) throws Exception {        ServerSocket ssocket = new ServerSocket(9000);        Socket socket = ssocket.accept();        OutputStream os = socket.getOutputStream();        os.write("http/1.0 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());        os.flush();        os.close();    }}

 可以在浏览器输入localhost:9000来请求内容 , http响应头的那部分会被浏览器解析掉.所以只输出一段hello

 

或者可以使用telnet localhost 9000来访问, 得到的结果就是那段写入的内容

 最简单的Client端写入到Server端的Demo 

public class Client {    public static void main(String[] args) throws Exception{        Socket socket = new Socket("localhost",9000);        OutputStream output = socket.getOutputStream();        output.write("你好".getBytes());        output.close();        socket.close();    }}

  注:需要先运行上面的Server类, 然后再运行这个Client.    然后再点击Server的控制台标签, 就会发现Server类的控制台输已经出了"你好"字样.

最简单的用Client端来模拟浏览器http请求Demo

用socket作为Client来模拟浏览器访问网站, 来获取网站的html内容.以www.sohu.com 为例...为什么选sohu呢? 你试试百度,会报302....

public class Client2 {    public static void main(String[] args) throws Exception {        Socket socket = new Socket("www.sohu.com", 80);        InputStream input = socket.getInputStream();        OutputStream output = socket.getOutputStream();        StringBuilder str = new StringBuilder();        //http协议中请求行,必须,不然不会被识别为HTTP        str.append("GET / HTTP/1.1\r\n");        //http协议中的请求头        str.append("Host: www.sohu.com\r\n");        str.append("Connection: Keep-Alive\r\n");        // 用于模拟浏览器的user-agent        str.append("user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\r\n");        //这里一定要一个回车换行,表示消息头完,不然服务器会一直等待,认为客户端没发送完        str.append("\r\n");        byte[] bytes = new byte[1024];        output.write(str.toString().getBytes());        while (true) {            int len = input.read(bytes);            if (len > 0) {                String result = new String(bytes, 0, len);                System.out.println(result);            } else {                break;            }        }    }}

 运行后, 前面是http响应头, 后面是html代码

改进Server类, 循环读取

前面提到的"最简单的Server端读取Client端内容的demo"这段代码, 也就是Server类. 其实不管客户端发来多少内容, 都只能读取1024字节以内的数据. 因为代码就是这么写的....

下面进行改进, 让他循环读取, 直到读完为止.

public class Server3 {    public static void main(String[] args) throws Exception {        ServerSocket ss = new ServerSocket(9000);        Socket s = ss.accept();        InputStream input = s.getInputStream();        byte[] bytes = new byte[1024];        while (true) {            int len = input.read(bytes);            // 如果读到了内容,说明得输出啊            if (len > 0) {                System.out.println(new String(bytes, 0, len));            }            // 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.            if (len < bytes.length) {                break;            }        }    }}

可以配合着用前文中的Client类来进行测试.先跑Server来监听端口, 再运行Client发送请求. 去看Server类的控制台是否输出相应的内容.

将服务器改为循环运行

之前是Server响应一个客户端就终止了,这回用while(true)来让Server不停地进行服务.

public class Server4 {    public static void main(String[] args) throws Exception {        ServerSocket ss = new ServerSocket(9000);        while (true) {            Socket s = ss.accept();            InputStream input = s.getInputStream();            byte[] bytes = new byte[1024];            while (true) {                int len = input.read(bytes);                // 如果读到了内容,说明得输出啊                if (len > 0) {                    System.out.println(new String(bytes, 0, len));                }                // 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.                if (len < bytes.length) {                    break;                }            }        }    }}

可以配合着用前文中的Client类来进行测试.先跑Server来监听端口, 再运行Client发送请求. 去看Server类的控制台是否输出相应的内容.

Server运行一次就行, 一直在提供服务.  而Client这回可以运行多次了, Client运行几次, Server的控制台下就会收到几个'你好'. 

BIO

同步阻塞IO, 每个线程都处理着一个客户端.客户端与线程数是1:1

public class Server5 {    public static void main(String[] args) throws Exception {        ServerSocket ss = new ServerSocket(9000);        System.out.println("服务端启动");        // 循环着监听        while (true) {            Socket s = ss.accept();            System.out.println("接收到客户端");            // 一旦接收到客户端,就开一个线程            new Thread(() -> {                //为了让代码简短,try多包一些代码...                try {                    InputStream input = s.getInputStream();                    OutputStream output = s.getOutputStream();                    byte[] bytes = new byte[1024];                    while (true) {                        int len = input.read(bytes);                        // 如果读到了内容,说明得输出啊                        if (len > 0) {                            System.out.println(new String(bytes, 0, len));                        }                        // 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.                        if (len < bytes.length) {                            break;                        }                    }                    output.write("http/1.1 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());                    output.flush();                    input.close();                    output.close();                } catch (Exception e) {                    e.printStackTrace();                } finally {                    //...其实close应该出现在这里...                    System.out.println("断开连接");                }            }).start();        }    }}

 可以在浏览器上用两个标签栏来访问localhost:9000来进行测试.

PIO

伪异步IO,为了避免Server5无限制地开一个新线程,使用线程池来统一管理线程.

public class Server6 {    public static void main(String[] args) throws Exception {        ServerSocket ss = new ServerSocket(9000);        System.out.println("服务端启动");        ExecutorService pool = Executors.newFixedThreadPool(40);        // 循环着监听        while (true) {            Socket s = ss.accept();            System.out.println("接收到客户端");            // 一旦接收到客户端,就放入线程池            pool.submit(() -> {                //为了让代码简短,try多包一些代码...                try {                    InputStream input = s.getInputStream();                    OutputStream output = s.getOutputStream();                    byte[] bytes = new byte[1024];                    while (true) {                        int len = input.read(bytes);                        // 如果读到了内容,说明得输出啊                        if (len > 0) {                            System.out.println(new String(bytes, 0, len));                        }                        // 如果没读取到内容,或者没读满bytes数组 说明读完了, 不用再读下一次了, 该退出循环了.                        if (len < bytes.length) {                            break;                        }                    }                    output.write("http/1.1 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes());                    output.flush();                    input.close();                    output.close();                } catch (Exception e) {                    e.printStackTrace();                } finally {                    //...其实close应该出现在这里...                    System.out.println("断开连接");                }            });        }    }}

NIO

首先推荐一本书,讲的详细:

nio博客   ,这个更适合快速入门,但原理最好还是看书..讲的很详细

所以相关原理就不在这里粘贴了....再整理也没人家讲的全面易懂,这本书还是非常推荐看的,一开始我就是在网上看各种博客(当时也知道这本书), 但直到耐下心看了这本书,才发现很多地方这里讲的非常详细, 这本书是基于api讲的, 以后需要分析看源码的话还是借助博客更好一些,网上有很多大神的源码分析

public class NIOServer {    private static int BUFFER_SIZE = 1024;    private static int PORT = 9000;    public static void main(String[] args) throws Exception {        Selector selector = Selector.open();        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();        serverSocketChannel.socket().bind(new InetSocketAddress(PORT));        serverSocketChannel.configureBlocking(false);        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);        while (true) {            selector.select();            Iterator
iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = iter.next(); iter.remove(); if (key.isAcceptable()) { SocketChannel socketChannle = serverSocketChannel.accept(); socketChannle.configureBlocking(false); socketChannle.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(BUFFER_SIZE)); } else if (key.isReadable()) { SocketChannel socketChannel = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); // 读浏览器发送的HTTP请求 long bytesRead = socketChannel.read(buf); while (bytesRead > 0) { buf.flip(); while (buf.hasRemaining()) { System.out.print((char) buf.get()); } System.out.println(); buf.clear(); bytesRead = socketChannel.read(buf); } //向浏览器返回HTTP请求 buf.put("http/1.1 200 OK\nContent-Type:text/html;charset:GBK\n\nhello".getBytes()); socketChannel = (SocketChannel) key.channel(); buf.flip(); while (buf.hasRemaining()) { socketChannel.write(buf); } // 关闭 socketChannel.close();//这样浏览器才不继续拉取 key.cancel(); } } } }}  

然后再浏览器中访问localhost:9000即可看到"hello"

或者用普通SocketClient来访问

或者用如下的NIOClient访问:

public class NIOClient {    public static void main(String[] args) throws Exception {        ByteBuffer buffer = ByteBuffer.allocate(1024);        SocketChannel socketChannel = SocketChannel.open();        socketChannel.configureBlocking(false);        socketChannel.connect(new InetSocketAddress("localhost", 9000));        if (socketChannel.finishConnect()) {            //向服务器写入            for (int i = 0; i < 3; i++) {                String info = "hello:<" + i + ">";                buffer.clear();                buffer.put(info.getBytes());                buffer.flip();                while (buffer.hasRemaining()) {                    socketChannel.write(buffer);                }            }                        //从服务器读取            buffer.clear();            long bytesRead = socketChannel.read(buffer);            while (bytesRead > 0) {                buffer.flip();                while (buffer.hasRemaining()) {                    System.out.print((char) buffer.get());                }                System.out.println();                buffer.clear();                bytesRead = socketChannel.read(buffer);            }        }    }}

  

未完待续.... 

转载于:https://www.cnblogs.com/noKing/p/8372536.html

你可能感兴趣的文章
等保2.0所需设备
查看>>
jquery easyui-linkButton获取和设置按钮text并且解决火狐不支持innerText的方法
查看>>
IBM大型主机(Mainframe)技术简介之六——数据库和系统集成(二)
查看>>
【BZOJ5336】[TJOI2018]party(动态规划)
查看>>
【BZOJ4944】[NOI2017]泳池(线性常系数齐次递推,动态规划)
查看>>
我只为一瓶啤酒
查看>>
timestamp与timedelta,管理信息系统概念与基础
查看>>
linux 服务器时间同步
查看>>
蓝桥杯 历届试题 幸运数
查看>>
SQL游标(cursor)详细说明及内部循环使用示例
查看>>
[转]【HTTP】Fiddler(二) - 使用Fiddler做抓包分析
查看>>
[转]C#使用 Salt + Hash 来为密码加密
查看>>
Tornado入门
查看>>
9. 回文数
查看>>
外部获取IndexPath的几种方式(关联对象等)
查看>>
Python多线程简单例子
查看>>
线性回归 及 正则化 公式推导
查看>>
多线程交替输出1234
查看>>
梦断代码阅读笔记02
查看>>
河南省第十届省赛 Binary to Prime
查看>>