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。当然如果你觉得你自己写的也很好,那就加油啊,你赢了。

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