乐趣区

关于netty:从零构建nettysocket基础

socket 根底

对于 java 网络编程来说,咱们能接触到的最底层便是 socket 了。我置信大部分浏览此篇文章的同仁都用过 socket。然而,我这篇教程的理念之一就是事无巨细,或者说,啰里啰嗦。因为原本就是一个思维的过程,所以,看官老爷们,就当做意识流格调来看吧(也是因为我文笔较差,因而文章的观赏性可能不是太好,给本人找个理由)。

我在刚学编程的时候,其实是一脸茫然的,尽管也会为屏幕输入“hello world”, 感到兴奋,然而兴奋之余会感觉,嗯~?,这货色跟我设想的不一样啊,就一个黑屏上输入几个字符,无能啥?这玩意跟网站啥的不沾边啊。包含学习了一些算法、数据结构后,还是感觉,网站啥的,跟我学的不沾边。

起初在学习加入了一个我的项目,波及到网络编程,我的项目简略点说就是树莓派跟 pc 通过路由器进行数据交换。从那时候起开始应用 socket,当然那个时候不求甚解,基本跟学过的网络模型也对不上号。什么三次握手,四次挥手,没用到啊。先不论,干就完了。

秉着这个徐循渐进的学习及思考过程,我就开始了 socket 编程。

链接

来一段服务端 socket 示例:

代码 1-1

public class OioServer {
    private ServerSocket serverSocket;
    private void openServer(int port) throws IOException {
        // 1 创立 ServerSocket
        serverSocket = new ServerSocket();
        // 2 绑定端口
        SocketAddress socketAddress = new InetSocketAddress(port);
        serverSocket.bind(socketAddress);
    }

    @Test
    public void testOpenServer() throws IOException {OioServer oioServer = new OioServer();
        oioServer.openServer(8081);
        // block
        Scanner scan = new Scanner(System.in);
        scan.next();}
}

运行 test 后,服务器开始监听 port 端口了。而后咱们验证下是不是真的在监听,我再 window 上测试的,所以这里以 window 为例。首先要关上 cmd 窗口,而后输出命令

代码 1-2

netstat -aon|findstr "8081"

图 1 -1

能够看到真的在监听了, 当初尽管开始监听了,然而,真的有客户端从 8081 进来该怎么办?咱们还须要解决连贯,这个连贯就是咱们的配角socket.

批改 1 - 1 代码如下:

代码 1 -2

public class OioServer {
    private ServerSocket serverSocket;
    private void openServer(int port) throws IOException {
        // 1 创立 ServerSocket
        serverSocket = new ServerSocket();
        // 2 绑定端口
        SocketAddress socketAddress = new InetSocketAddress(port);
        serverSocket.bind(socketAddress);
    }

    @Test
    public void testOpenServer() throws IOException {OioServer oioServer = new OioServer();
        oioServer.openServer(8081);
        Socket socket = oioServer.listenAccept();}
}

此时,服务端就运行有客户端进来连贯了。而后咱们再写一个客户端。

代码 1-3

public class OioClient{public Socket connect(String host, int port) throws IOException {Socket socket = new Socket();
        socket.connect(new InetSocketAddress(host, port));
        return socket
    }

    @Test
    public void testClient() throws IOException, InterruptedException {OioClient oioServer = new OioClient();
        oioServer.connect("127.0.0.1", 8081);
         // block
         Scanner scan = new Scanner(System.in);
         scan.next();}
}
 

运行 test,为例不让 test 退出,咱们用规范出入 block 住 test 线程。记住,当初咱们服务端还在监听,我服务端和客户端都跑在同一台 pc 上。成果如图

图 1 -2

能够看到第二行本地地址是 127.0.0.1:6283 近程地址是127.0.0.1:8081, 那咱们是不是能够猜到,这个 TCP 连贯是客户端的,客户端的 IP 是6283, 咱们尽管没指定,然而零碎会给咱们随机调配一个可用的端口,不信能够试一下,下次可能就不一样了。

牵强附会,第三行就是咱们服务端的 TCP 连贯。

那么咱们是不是能够有一个这样的论断:socket其实就是一个 TCP 连贯的封装(当然也能够是 UDP,这里不做探讨)?或者 socket 的上层是 TCP/IP 层?

到当初咱们大略晓得了 socket 跟 TCP 的分割,既跟网络模型的分割。

有了 TCP 链接,咱们就能够开始传输数据了。很多网上例子是,简历 socket 连贯后,客户端给服务器发送数据,而后服务器接收数据并回复。然而请不要误会,过程不肯定全副是这样的。咱们客户端和服务端建设链接后,两边都失去一个 socket, 这两个socket 是等价的。如果你对 TCP 有理解的话,肯定会晓得 TCP 的四次挥手,网上对于四次挥手的图大略也都是下图这个样子:

图 1 -3

然而呢,四次挥手同样能够由服务端发动。因而,就算是在 TCP 层,两个 socket 的是等价的。

通信

咱们失去沉闷的 socket 后,就能够对 socket 进行读写操作了。既然 socket 没什么区别,那咱们能够无差别看待 socket。咱们来写 socket 读写取的办法:

代码 1 -4

public class SocketUtils {
    /**
     * 从 socket 中读数据
     */
    public static String read(Socket socket) {
        try {InputStream inputStream = socket.getInputStream();
            byte[] bytes = new byte[1024];
            int len;
            StringBuilder sb = new StringBuilder();
            while ((len = inputStream.read(bytes)) != -1) {
                // 留神指定编码格局,发送方和接管方肯定要对立,倡议应用 UTF-8
                sb.append(new String(bytes, 0, len, "UTF-8"));
            }
            return sb.toString();} catch (IOException e) {e.printStackTrace();
            return null;
        }
    }

    /**
     * 往 socket 中写数据
     */
    public static void write(Socket socket, String response) {
        try {OutputStream outputStream = socket.getOutputStream();
            outputStream.write(response.getBytes("UTF-8"));
            outputStream.flush();
            socket.shutdownOutput();} catch (IOException e) {e.printStackTrace();
        }
    }
}

下面代码以入参的局势将 socket 传入,而后就行读写,留神,咱们这两个并没有调用办法socket.close, 也就是说 socket 能够反复读写数据,而网上大部分教程间接会吧 socket 给敞开,造成初学者认为每次读写玩就得敞开 socket 的错觉。

在办法 read 中,依据 (len = inputStream.read(bytes)) != -1 来判断是否读取完数据。而写入数据端怎么通知接收端它实现了写入呢?咱们能够用 socket.shutdownOutput() 办法。

好,接下来咱们就来测试下发送与接收数据。

代码 1 -4

public class OioServer {
    ...

    @Test
    public void testOpenServer() throws IOException {OioServer oioServer = new OioServer();
        oioServer.openServer(8081);
        Socket socket = oioServer.listenAccept();
        SocketUtils.write(socket, "Can you hear me?");
        String msg = SocketUtils.read(socket);
        System.out.println(msg);
        // block
        Scanner scan = new Scanner(System.in);
        scan.next();}
}

代码 1 -5

public class OioClient {

   ...
    
    @Test
    public void testClient() throws IOException, InterruptedException {OioClient oioServer = new OioClient();
        Socket socket = oioServer.connect("127.0.0.1", 8081);
        String msg = SocketUtils.read(socket);
        System.out.println(msg);
        SocketUtils.write(socket, "Yes, I can hear you!");
        // block
        Scanner scan = new Scanner(System.in);
        scan.next();}
}

先执行 testOpenServer() 再执行 testClient()。就会实现对话。当然咱们能够反复调SocketUtils.read SocketUtils.write进行通信,只有两边 socket 不敞开。

小结

此篇文章总结了 Socket 的一些应用,包含服务器启动,socket 建设,并证实了 socket 的等价性

退出移动版