Android面试题之Java网络通信基础知识

简介: Socket是应用与TCP/IP通信的接口,封装了底层细节。网络通信涉及连接、读写数据。BIO是同步阻塞,NIO支持多路复用(如Selector),AIO在某些平台提供异步非阻塞服务。BIO示例中,服务端用固定线程池处理客户端请求,客户端发起连接并读写数据。NIO的关键是Selector监控多个通道的事件,减少线程消耗。书中推荐《Java网络编程》和《UNIX网络编程》。关注公众号AntDream了解更多。

本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点

网络通信基础

socket

socket是应用层与TCP/IP协议族通信的中间软件抽象,操作系统把传输层一下的内容都包装了,应用层只需要用socket即可完成网络请求

Tcp是基于流;UDP是基于DatagramPacket数据报;socket可以利用DatagramPacket进行UDP通信

网络请求3部分
  • 连接(客户端和服务端)
  • 读网络数据
  • 写网络数据
BIO、NIO、AIO
  • BIO:阻塞的IO
  • NIO(IO多路复用):一个线程同时管理多个和客户端的连接,一般应用线程数量限制为1024个(系统限制,总共65535,要除去系统使用)
  • AIO(异步IO):目前只有Windows上完全实现

BIO网络编程

服务端
private static ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(10001));
        System.out.println("start server ....");
        try {
            while (true){
                executorService.execute(new ServerTask(serverSocket.accept()));
            }

        }finally {
            serverSocket.close();
        }
    }

    private static class ServerTask implements Runnable{
        private Socket socket;

        public ServerTask(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            //这样会自动关闭流
            try (ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                 ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());) {
                String input = inputStream.readUTF();
                System.out.println("accept input :" +input);
                outputStream.writeUTF("hello " + input);
                outputStream.flush();

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
客户端
public static void main(String[] args) throws IOException {
        Socket socket = new Socket();
        InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 10001);
        ObjectOutputStream outputStream = null;
        ObjectInputStream inputStream = null;

        try {
            socket.connect(inetSocketAddress);
            outputStream = new ObjectOutputStream(socket.getOutputStream());
            inputStream = new ObjectInputStream(socket.getInputStream());
            outputStream.writeUTF("hello");
            outputStream.flush();

            System.out.println(inputStream.readUTF());

        } finally {
            if (socket != null) {
                socket.close();
            }
            if (outputStream != null) {
                outputStream.close();
            }
            if (inputStream != null) {
                inputStream.close();
            }
        }
    }

BIO网络编程

Selector、ServerSocketChannel、SocketChannel、Buffer、应用程序

服务端一个线程里面,会有一个Selector,用于接收客户端发来的事件,比如连接、有数产生事件等

ServerSocketChannel:用于处理连接事件,会在Selector里注册OP_ACCEPT事件监听,Selector收到客户端连接事件后会通知ServerSocketChannel;

SocketChannel:用于处理数据读的事件,一个客户端会对应一个SocketChannel;同样它会注册数据读的事件到Selector,有数据发送来了以后,Selector就会通知对应的SocketChannel;

Buffer:用于SocketChannel与服务端应用程序直接的数据操作,有数据时,SocketChannel会写到缓冲区Buffer中,然后应用程序从Buffer中读数据,处理完又写回到Buffer,然后SocketChannel从Buffer中读

有新的客户端连接进来后,会创建新的SocketChannel,进行数据的读写通信

Buffer
Buffer是内存中的一块区域,是一个字节数组,有3个重要变量:Position、limit、capacity;模式的话有读和写2种模式,不能同时进行读和写
  • position:指示当前数据的位置,写模式下,会随着写入数据而移动;当调用flip方法切换到读的模式时,position和limit会发生变化,position会指向0,limit会变成position的值,也就是之前最后写入数据的position
  • capacity:Buffer的容量
Buffer的内存分配:可以在堆上分配,也可以在直接内存上分配
  • 堆上分配:分配速度会快一点,网络通信慢一点,一般业务处理方面多一点

    ByteBuffer buffer = ByteBuffer.allocate(20000);
    
  • 直接内存分配:分配速度会慢一点,网络通信会快一点,一般直接读写网络数据用这个

    ByteBuffer buffer = ByteBuffer.allocateDirect(20000);
    
  • 把byte数组转换为Buffer

    ByteBuffer.wrap(bytes)
    
Buffer的写
  • Channel向Buffer写:用Buffer的read方法,表示从网络读数据写到Buffer
  • 应用程序向Buffer写:用Buffer提供的各种put方法
Buffer的读
  • 从Buffer读数据写到Channel:用Buffer的write方法,表示从Buffer读数据写到channel
  • Buffer从应用程序读:用Buffer的get方法

NIO网络编程

注意点
  • ServerSocketChannel和SocketChannel都要设置为非阻塞,configBolcking(false)
  • selector和ServerSocketChannel初始化的方式都是调用open函数
  • 调用selector.select(1000)表示开启事件监听,每个1秒唤醒一次,但是有事件过来会立刻唤醒处理
  • ServerSocketChannel和SocketChannel通过register函数来向selector注册事件监听
  • register函数可以同时支持多个事件的监听,用位操作符连接:OP_CONNNECT | OP_READ
  • register函数还可以同时注册Buffer,作为第3个参数,这样可以在其他地方,通过SelectionKey的attachment拿到
  • 所有的事件都在SelectionKey里面,包括OP_CONNNECT,OP_READ,OP_WRITE,OP_ACCEPT
  • Buffer在读模式切换到写模式的时候,需要调用flip方法进行模式切换
OP_WRITE和OP_READ事件

我们一般很少使用OP_WRITE事件,因为操作系统在建立socket连接的时候,会为socket创建2个缓冲区(Buffer),一个发送Buffer,一个接收Buffer;当客户端向服务端发送数据的时候,服务端接收缓存接收到数据,就会触发OP_READ事件;这2个缓冲区由操作系统内核管理。

而发送缓存里面只要还有空间可写,哪怕一个字节,就会触发OP_WRITE事件

OP_WRITE使用方式
  • 用SelectionKey的isWritable判断是否有数据
  • Buffer用SelectionKey的attachment来获取
  • 根据Buffer的hasRemaining来判断是否还有数据可写
  • 数据都写完的话要取消写事件的监听,用SelectionKey的interestOps方法
OKhttp中的心跳包

websocket实现,websocket本身定义了跟心跳有关的2个数据帧,实现以后,服务端会自动解析和应答

书籍
  • 《Java网络编程》:网络编程、UDP用socket实现等
  • 《UNIX网络编程》:socket原理等
  • 《Netty实战》:Netty相关的网络开发

欢迎关注我的公众号AntDream查看更多精彩文章!

目录
相关文章
|
10天前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
11天前
|
存储 缓存 Java
大厂面试必看!Java基本数据类型和包装类的那些坑
本文介绍了Java中的基本数据类型和包装类,包括整数类型、浮点数类型、字符类型和布尔类型。详细讲解了每种类型的特性和应用场景,并探讨了包装类的引入原因、装箱与拆箱机制以及缓存机制。最后总结了面试中常见的相关考点,帮助读者更好地理解和应对面试中的问题。
35 4
|
23天前
|
网络协议 Shell 网络安全
解决两个 Android 模拟器之间无法网络通信的问题
让同一个 PC 上运行的两个 Android 模拟器之间能相互通信,出(qiong)差(ren)的智慧。
22 3
|
1月前
|
算法 Java 数据中心
探讨面试常见问题雪花算法、时钟回拨问题,java中优雅的实现方式
【10月更文挑战第2天】在大数据量系统中,分布式ID生成是一个关键问题。为了保证在分布式环境下生成的ID唯一、有序且高效,业界提出了多种解决方案,其中雪花算法(Snowflake Algorithm)是一种广泛应用的分布式ID生成算法。本文将详细介绍雪花算法的原理、实现及其处理时钟回拨问题的方法,并提供Java代码示例。
67 2
|
2月前
|
Java Android开发 C++
🚀Android NDK开发实战!Java与C++混合编程,打造极致性能体验!📊
在Android应用开发中,追求卓越性能是不变的主题。本文介绍如何利用Android NDK(Native Development Kit)结合Java与C++进行混合编程,提升应用性能。从环境搭建到JNI接口设计,再到实战示例,全面展示NDK的优势与应用技巧,助你打造高性能应用。通过具体案例,如计算斐波那契数列,详细讲解Java与C++的协作流程,帮助开发者掌握NDK开发精髓,实现高效计算与硬件交互。
132 1
|
1月前
|
JSON 安全 前端开发
第二次面试总结 - 宏汉科技 - Java后端开发
本文是作者对宏汉科技Java后端开发岗位的第二次面试总结,面试结果不理想,主要原因是Java基础知识掌握不牢固,文章详细列出了面试中被问到的技术问题及答案,包括字符串相关函数、抽象类与接口的区别、Java创建线程池的方式、回调函数、函数式接口、反射以及Java中的集合等。
28 0
|
3月前
|
存储 搜索推荐 Java
探索安卓开发中的自定义视图:打造个性化UI组件Java中的异常处理:从基础到高级
【8月更文挑战第29天】在安卓应用的海洋中,一个独特的用户界面(UI)能让应用脱颖而出。自定义视图是实现这一目标的强大工具。本文将通过一个简单的自定义计数器视图示例,展示如何从零开始创建一个具有独特风格和功能的安卓UI组件,并讨论在此过程中涉及的设计原则、性能优化和兼容性问题。准备好让你的应用与众不同了吗?让我们开始吧!
|
3月前
|
Java 调度 Android开发
Android经典实战之Kotlin的delay函数和Java中的Thread.sleep有什么不同?
本文介绍了 Kotlin 中的 `delay` 函数与 Java 中 `Thread.sleep` 方法的区别。两者均可暂停代码执行,但 `delay` 适用于协程,非阻塞且高效;`Thread.sleep` 则阻塞当前线程。理解这些差异有助于提高程序效率与可读性。
74 1
|
3月前
|
安全 算法 Java
java系列之~~网络通信安全 非对称加密算法的介绍说明
这篇文章介绍了非对称加密算法,包括其定义、加密解密过程、数字签名功能,以及与对称加密算法的比较,并解释了非对称加密在网络安全中的应用,特别是在公钥基础设施和信任网络中的重要性。
|
3月前
|
C# Windows 开发者
当WPF遇见OpenGL:一场关于如何在Windows Presentation Foundation中融入高性能跨平台图形处理技术的精彩碰撞——详解集成步骤与实战代码示例
【8月更文挑战第31天】本文详细介绍了如何在Windows Presentation Foundation (WPF) 中集成OpenGL,以实现高性能的跨平台图形处理。通过具体示例代码,展示了使用SharpGL库在WPF应用中创建并渲染OpenGL图形的过程,包括开发环境搭建、OpenGL渲染窗口创建及控件集成等关键步骤,帮助开发者更好地理解和应用OpenGL技术。
246 0