java网络编程 connection refused:connect_Java网络编程基础篇

news/2024/7/3 13:54:42

一、前言

网络通讯在系统交互中是必不可少的一部分,无论是面试还是工作中都是绕不过去的一部分,本节我们来谈谈Java网络编程中的一些知识

二、 网络通讯基础知识

网络通讯的本质用一句话来说是处于两个主机上的两个进程之间进行通讯,如下图:

4a1cbfd477a63ec5722f707b1528a0ec.png


image.png

如上图主机A和B上面有好多进程,比如QQ进程,手淘进程,微信进程,浏览器进程等等。
这里假如进程1为微信进程,在应用层微信肯定自己约定了自己的应用成层协议(比如约定协议包为协议头+消息内容)。

那么当主机A上的微信用户给主机B上的微信用户发送消息时候,发送的消息内容要首先经过程序把要发送的数据转换为自己的应用层协议格式的数据,把消息转换为应用层包是在用户程序代码里面做的。做完这些后网卡驱动程序会接着把应用层包转换为运输层的tcp包或者udp包,在运输层会把应用层包作为数据,然后在数据包前添加协议头组成运输层包,协议头里面会包含目的地址的网络端口号。

然后运输层的包会被作为数据包的数据部分,然后在数据部分前面添加ip层的头部部分,头部里面会含有当前主机的ip 和目的地址的ip组成ip层的包

ip层的包最后会被转换为数据链路层的数据包帧,在帧的头部会新增当前主机网卡mac地址和下一跳的主机的Mac地址,注意这里不是目的主机的mac地址,因为在源主机和目的主机之间很可能有好多路由器,这时候下一跳的mac地址就是当前主机连接的路由器的Mac地址。

006ab463534ecf5a9a5d78b4cd13cf79.png


image.png

最后数据链路层的数据帧会被转换会在物理层通过二进制流通过网络传递到网络上,网络流经过路由器时候路由器会首先把二进制流转换为数据链路层的数据帧,然后转换为网络层的ip数据包,然后读取目的地址的ip,然后查找路由表进行路由选择,然后把ip数据包重新转换为数据链路层的帧,这时候数据帧里面的目的Mac地址是路由选择的主机的Mac地址,然后把数据帧通过物理层透明的把二进制流传递到下一站,如果下一站就是目的ip所在主机,则网卡驱动会吧二进制流依次转换为 数据链路层数据帧、ip包、传输层tcp包或者udp包,最后交给应用程序进程进行处理,应用程序转换包为具体数据然后进行处理。

这里需要注意的是网络层只是能确定目的主机,还记得ip包里记录了目的主机的ip,但是一个主机上可能会有多个进程,那么具体把数据交给那个进程进行处理那?这个就是运输层的作用,运输层包里面记录的端口号,运输层会把数据交给具体端口号的进程。即网络通讯的socket地址实际是 ip+端口号。

另外整个通讯过程中传输的是二进制流,没有业务数据包的概念(比如我发送了一个业务请求包),当发送方发了多个业务包后,接受方的运输层并不知道每个包的边界,它只是把接受到的数据传输给应用层,所以应用层要自己根据约定好的协议解析二进制流为业务所需要的包,即半包粘包问题,可以参考(https://gitbook.cn/gitchat/activity/5b13e6a675742e21d6d14ea4)。

另外由于网络传输的都是二进制流,所以在发送方进程需要把要发送的数据(比如本文字符串)进行序列化为二进制流,而接受方应用层需要根据对应的反序列化把二进制流转换为具体的数据(比如文本字符串),即序列化与反序列化问题。

另外路由器只有网络层,数据链路层,物理层三层结构。

一次网络通讯看此很复杂,中间需要做好多协议包的转换,但是好在除了应用层协议部分是需要应用程序自己来做,其他下层都是由网卡驱动程序来完成的,这些对应用程序是透明的。
...

五、 Java IO模型与Java NIO中ByteBuffer

5.1 Java IO模型

846711ec581ebdf2483de2dc72448621.png


image.png

如上图当网络应用进程向socket写入数据时候,首先需要在应用程序内申请一个写buffer,然后把数据写入到写buffer,然后应用程序的执行会用用户态切换到核心态,核心态程序把应用程序写buffer里面的数据拷贝到操作系统层面的写缓存里面。当应用程序读取数据时候是需要把操作系统层面的读buffer里面的数据拷贝到应用程序层面的读buffer里面。一般情况下应用程序层面的buffer都是从堆空间里面申请的,这就需要在用户态和核心态之间数据传输时候进行一次数据copy。这是因为核心态是不能直接应用程序堆内存的,必须转换为直接内存。

如果用户态申请的堆外内存(直接内存)那么就会省去中间的拷贝操作,操作系统层面会直接使用用户态申请的堆外内存(直接内存)里面的数据。

5.2 使用ByteBuffer分配堆内存与堆外内存

Java NIO中提供了一个ByteBuffer用来分配发送和接受缓存用的,其分为两种模式的内存,一个是我们常用的堆内存,一个是堆外内存(直接内存)。

  • 当我们调用ByteBuffer的allocate方法时候,实际分配的是堆内存,其代码如下:
public static ByteBuffer allocate(int capacity) {
        if (capacity < 0)
            throw new IllegalArgumentException();
        return new HeapByteBuffer(capacity, capacity);
    }
HeapByteBuffer(int cap, int lim) {       
        super(-1, 0, lim, cap, new byte[cap], 0);
    }

可知这里是new了一个byte数组,所以分配的是堆内存。

ByteBuffer(int mark, int pos, int lim, int cap,  
                 byte[] hb, int offset)
    {
        super(mark, pos, lim, cap);
        this.hb = hb;
        this.offset = offset;
    }

可知其内部是通过hb这个指针来指向了分配的堆内存。

  • 当调用ByteBuffer的allocateDirect方法时候,分配的就是堆外内存:
public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }
protected static final Unsafe unsafe = Bits.unsafe();

 DirectByteBuffer(int cap) {                   // package-private

        super(-1, 0, cap, cap);
        boolean pa = VM.isDirectMemoryPageAligned();
        int ps = Bits.pageSize();
        long size = Math.max(1L, (long)cap + (pa ? ps : 0));
        Bits.reserveMemory(size, cap);
        //1 使用Unsafe分配堆外内存
        long base = 0;
        try {
            base = unsafe.allocateMemory(size);
        } catch (OutOfMemoryError x) {
            Bits.unreserveMemory(size, cap);
            throw x;
        }
       //2 对分配的堆外内存进行初始化
        unsafe.setMemory(base, size, (byte) 0);
        if (pa && (base % ps != 0)) {
            // Round up to page boundary
            address = base + ps - (base & (ps - 1));
        } else {
            address = base;
        }
       //3.创建堆外内存回收器
        cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
        att = null;
    }

可知堆外内存是使用UNSAFE类进行内存分配的。


http://www.niftyadmin.cn/n/1998496.html

相关文章

交叉熵代价函数(作用及公式推导)

交叉熵代价函数&#xff08;Cross-entropy cost function&#xff09;是用来衡量人工神经网络&#xff08;ANN&#xff09;的预测值与实际值的一种方式。与二次代价函数相比&#xff0c;它能更有效地促进ANN的训练。在介绍交叉熵代价函数之前&#xff0c;本文先简要介绍二次代价…

你应该知道的Windows XP的小秘密

自从微软发布Windows XP以来&#xff0c;Windows XP已经得到人们越来越广泛的应用&#xff0c;微软也宣称它是迄今为止最好的操作系统。Windows XP到底有什么出色的地方&#xff1f;让我先来告诉你其中的一些小秘密吧&#xff01; ● 关闭自动播放功能 一旦您将媒体插入驱动器&…

精通Java设计模式从初见到相爱全集(1-23)

为什么80%的码农都做不了架构师&#xff1f;>>> 1、如何使用&#xff1f; 设计模式是为了解决问题产生的,一种解决方案&#xff0c;你的问题是什么决定你用什么设计模式, 你没有问题却偏要生搬硬套一个设计模式上去, 这就是没事找事&#xff0c;你的问题就像: 自己…

Windows XP深入问答

1、我使用Windows XP系统&#xff0c;请问使用什么方法可以提高启动速度&#xff1f; 使用微软提供的“Bootvis”软件可以有效的提高Windows XP启动速度。这个工具是微软内部提供的&#xff0c;专门用于提升 Windows XP启动速度。下载后解压缩到一个文件夹下&#xff0c;并在“…

android 音频合成_文字怎样转语音,语音怎样合成,视频剪辑怎样配音,语音转文字?...

文字怎样转语音&#xff0c;语音怎样合成&#xff0c;视频剪辑怎样配音&#xff0c;语音怎样转文字&#xff1f;林力早大家好&#xff0c;上一节我们介绍了如何一键分发、怎样录制视频、视频怎样剪辑&#xff0c;这一节我们聊一聊怎样给视频配置声音。想必大家都会遇到这样的问…

java aop做一个接口耗时的计算

看代码&#xff1a; Aspect Component public class TimeCostAspect {private static Logger logger LoggerFactory.getLogger(TimeCostAspect.class);private static final String POINT "execution (* com.ming..*.controller..*.*(..))";Pointcut(POINT)public …

Windows XP 控制台命令详解 - 目录和文件操作命令

编者语&#xff1a;   Windows XP&#xff08;包括 Windows 2000&#xff09;的控制台命令是在系统出现一些意外情况下的一种非常有效的诊断和测试以及恢复 系统功能的工具。小编的确一直都想把这方面的命令做个总结&#xff0c;这次辛苦老范给我们整理了这份实用的秘笈。 A…

Unity CommandBuffer的一些学习整理

1.前言 近期在整理CommandBuffer这块资料&#xff0c;之前的了解一直较为混乱。 算不上新东西了&#xff0c;但个人觉得有些时候要比加一个摄像机再转RT廉价一些&#xff0c;至少省了深度排序这些操作。 本文使用两个例子讲解CommandBuffer如何使用&#xff0c;但在此之前稍稍总…