NIO server socket demo,真正實戰的時候這個應該被替換爲MINa或者netty

一個nio服務端示例,這個服務端很垃圾的,能夠支持的處理的事情有限,當做練手了,本來是用來做一個react-native-tcp演示的demo的,現在就直接放到下面了。
package com.wang.reactnative.tcp;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * a simple demo for selector
 */
public class TCPServer {

    public static void main(String[] args) throws IOException {
        Map<String, Integer> map = new HashMap<String, Integer>();
        // 基於事件驅動的serversocket
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        // 綁定地址
        InetSocketAddress inetSocketAddress = new InetSocketAddress(Constants.HOST, Constants.PORT);
        serverSocketChannel.bind(inetSocketAddress);
        // 非阻塞
        serverSocketChannel.configureBlocking(false);
        // op_accept
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        while (true) {
            // 接收數據選擇channel(netty和mina與正常的socket通信可能更多的區別是在,那張圖裏面,在我們之前對於服務器的認知當中,很可能是如果我們需要進行數據的傳輸的時候,或者通信的時候,當存在兩個請求併發的時候,我們可能需要開兩個不同的線程,併發處理兩個請求)
            selector.select();
            Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                try {
                    if (key.isAcceptable()) {
                        ServerSocketChannel serverSocketChannel1 = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = serverSocketChannel1.accept();
                        System.out.println("Server: accept client socket " + socketChannel);
                        socketChannel.configureBlocking(false);
                        socketChannel.register(key.selector(), SelectionKey.OP_READ);
                    }
                    else if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
                        int readbytes = socketChannel.read(byteBuffer);
                        System.out.println(new String(byteBuffer.array(), 0, readbytes));
                        // 獲取socket
                        byteBuffer.flip();
                        socketChannel.write(byteBuffer);
                    }
                    else if (key.isWritable()) {
                        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                        byteBuffer.flip();
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        socketChannel.write(byteBuffer);
                        if (byteBuffer.hasRemaining()) {
                            key.interestOps(SelectionKey.OP_READ);
                        }
                        byteBuffer.compact();
                    }
                    keyIterator.remove();
                }
                catch (Exception e) {
                    keyIterator.remove();
                    key.channel().close();
                    continue;
                }
            }
        }
    }
}

react-native-tcp部分的代碼

import React from 'react';
import { View, Text, TextInput } from 'react-native'
import { Button } from 'antd-mobile';
import net from 'react-native-tcp';

class DrawerDemo1Screen extends React.Component {
    state = {
        client: null,
        text: null,
        response: null,
    }


    connect = () => {
        var host = "192.168.253.200";
        var port = 6349;

        var client = net.createConnection(6349, host, () => {
            client.write('Hello, server! Love, Client.\r\n');
        });

        // client.write("message");

        client.on('error', function (error) {
            console.log(error)
        });

        client.on('data', (data) => {
            console.log('message was received', String.fromCharCode(...data));
            this.setState({
                response: String.fromCharCode(...data),
            })
        });

        this.setState({
            client: client,
        })
    }
    componentDidMount = () => {
        this.connect();
    }

    sendMessage = () => {
        const { client } = this.state;
        client.write(this.state.text);
    }


    changeText = (text) => {
        this.setState({
            text: text,
        })
    }

    reconnect = () => {
        this.connect();
    }
    
    render() {
        return (
            <View>
                <Text>{this.state.response}</Text>
                <TextInput onChangeText={this.changeText} />
                <Button onClick={this.sendMessage}>send</Button>
                <Button onClick={this.reconnect}>connect</Button>
            </View>)
    }
}

export default DrawerDemo1Screen;

從這個簡單的demo中我們至少能夠發現nio所帶來的優勢=====線程更少了,我們可以用一個線程去應對不同的請求而不需要更多的代碼。

爲什麼nio能夠做到這些,我們常常可能會得到這樣一個原因,因爲我們的NIO是面向緩衝的,既然是面向緩衝的一個請求過來或者數據發送,會先被放到一個特定的緩衝區裏面,之後進行後續的處理,這樣的話,併發的連接被緩衝到相應的緩衝區裏面就能夠用更少的縣城進行處理。

面向緩衝的NIO帶來了一個優勢叫做Not blocking IO以及基於事件驅動的NIO。從這個角度來講NIO能夠給我們帶來更高的併發量,帶來更少的縣城,更加有效的事件處理。

對於Not blocking這個問題的理解,我們是這樣的,非阻塞的,其實和基於緩衝區的沒什麼太大的區別,沒有緩衝區的話,非阻塞無從談起。基於這個原因,我們理解一下面試題目中的一個所謂的,給你一個幾個GB的大文件怎麼去讀取。

不知道NIO或者不太瞭解過沒有經驗的可能沒法一時間想到用NIO去處理。我們可能直觀的就是想用一個簡單的比如說文件切割,大文件變小文件處理,當然Hadoop對於這種大文件的話也是有處理方式的,這個放到一邊先不談。但是單機情況下如何實現呢,文件分割可能太過籠統了。怎麼分割和如何處理最終都是一個問題。

想想基於緩衝區的NIO,這種東西能夠讓我們每次讀取一個緩衝區的數據內容,這個數據內容是很小的,對於內存而言不過就是一個little的東西,很可能就是幾個字節,幾十個字節,或者幾KB,同幾個G的文件相比佔用的內存小太多了。這就是NIO,這就是NIO的優勢。

關於NIO的東西還是要繼續看一下,這個demo只是用來熟悉一下nio的示例,如果需要進一步用到NIO的東西,包括在真正的項目和實際應用中,應使用開源框架mina或者netty。當然如果你覺得你自己寫的也很好,那就加油啊,你贏了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章