• 通信基石Socket结合OOP实现程序间的通信


    前言

    先分享一下最近看到的几篇面经:
    在这里插入图片描述
    看了一下关于Socket的知识点好像面试十分高频,它作为通信的基石许多组件,框架都是在他的基础之上进行封装,确实有必要深入了解一下
    特别是网络编程结合I/O操作来要求你实现一个网络聊天室,如果面试官突然问到你 你能娓娓道来吗?

    今天主要针对Socket展开

    一.Socket是用来干什么的

    首先
    标识每个端点IP地址和端口号称为套接字。socket={IP:PORT}。标识源IP地址,源port,目的IP 地址,目的port,协议统称为套接字对。套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。
    套接字(socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
    一个Socket是一对IP地址和端口。
    在这里插入图片描述

    Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。
    你可以这么理解:socket是进程之间用来对话的中间层工具。信息的传递需要Socket为我们架起桥梁!

    二.如何用代码建立通信连接

    站在面向对象的肩膀之上,设计者已经在java.net包中封装了用于通信连接的类与接口ServerSocket与Socket,只需要指定端口就能进行简单的连接
    在这里插入图片描述
    这是不是在你学JavaWeb里的Servlet中似曾相识呢?
    可以发现,通过Socket我成功地将两个程序(两个对象)串联了起来,而通信的秘密就蕴藏在其中,因为通过两端的Socket对象可以进行I/O操作,从而实现各式各样的通信!
    注意当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP来分配的,是不确定的,是随机的
    在这里插入图片描述

    三.Java实现TCP发收消息

    前面说过,支持TCP/IP协议的网络通信的基本操作单元
    TCP讲究准确安全(三次握手,四次挥手),他是基于连接的!
    写两个Java类来仿照客户端与服务器端之间基于TCP的消息收发
    客户端从本地发送一张图片到服务器端,服务器端接收并保持到指定位置,并回送消息,客户端显示收到消息
    在这里插入图片描述
    通信的本质就是以Socket通道为对象来做I/O操作
    客户端:
    面向指定端口的Socket编程

    public class Client {
        public static void main(String[] args) throws Exception {
            //客户端通过主机连接服务器9999 端口
            InetAddress inet = InetAddress.getLocalHost();
            Socket socket = new Socket(inet, 9999);
            //指定文件路径
            String filePath = "D:\\5.jpg";
            //将图片以文件流的形式传输到通道中
            BufferedInputStream bio = new BufferedInputStream(new FileInputStream(filePath));
            //通过工具类将流数据转成字节数组
            byte[] bytes = StreamUtils.streamToByteArray(bio);
    
            //将数据写入socket通道
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write(bytes);
    
            socket.shutdownOutput();
    
            //接收服务端回送的消息
            InputStream inputStream = socket.getInputStream();
            String s = StreamUtils.streamToString(inputStream);
            System.out.println(s);
            //关流
            bio.close();
            inputStream.close();
            socket.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    服务器端:
    面向返回的Socket编程

    public class Server {
        public static void main(String[] args) throws Exception {
            //指定连接的端口号
            ServerSocket serverSocket = new ServerSocket(9999);
            //得到连接后返回的socket对象
            Socket accept = serverSocket.accept();
            //通过对象得到输入流
            InputStream inputStream = accept.getInputStream();
            //将输入流写到字符输入流
            BufferedInputStream bis = new BufferedInputStream(inputStream);
            //通过工具类将字符输入流对象转成字节数组
            byte[] bytes = StreamUtils.streamToByteArray(bis);
            String path = "src\\mysqlSend.png";
            //创建输出流对象,将转成字节数组的文件流输出到指定目录
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path));
            bos.write(bytes);
            bos.flush();//刷新
                
            //向客户端发送收到图片
            BufferedWriter osw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
            osw.write("收到图片");
            osw.flush();
            accept.shutdownOutput();//设置结束标记
                
            accept.close();
            osw.close();
            serverSocket.close();
            bos.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    可以看到,这一切操作的基础都是在以循环往复的Socket通道为对象进行,所以强调TCP是基于连接的!

    四.UDP的Socket编程

    而UDP与之相反,他是不基于连接的!所以应用场景十分少。
    在B站上看到过这样一句话非常生动形象:

    UDP协议就相当于是写信给对方,寄出去信件之后不能知道对方是否收到信件,信件内容是否完整,也不能得到及时反馈。
    TCP协议就像是打电话通信,在这一系列流程都能得到及时反馈,并能确保对方及时接收到。

    在这里插入图片描述
    UDP的宗旨:
    使用UDP协议进行数据传输是,需要将需要传输数据定义为数据报(DatagramPaket),在数据报中指明数据所要到达Socket(主机地址和端口号),然后再将数据报发送出去。明白了这个再来结合I/O不是有手就行啊

    一个UDP通信小Demo:
    客户端:

    public class UDPSenderB {
        public static void main(String[] args) throws IOException {
    
            //1.创建 DatagramSocket 对象,准备在9998端口 接收数据
            DatagramSocket socket = new DatagramSocket(9998);
    
            //2. 将需要发送的数据,封装到 DatagramPacket对象
            byte[] data = "hello world!".getBytes(); //
    
            //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
            DatagramPacket packet =
                    new DatagramPacket(data, data.length, InetAddress.getByName("192.168.235.128"), 9999);
    
            socket.send(packet);
            //构建一个 DatagramPacket 对象,准备接收数据
            // 一个数据包最大 64k
            byte[] buf = new byte[1024];
            packet = new DatagramPacket(buf, buf.length);
            //调用 接收方法, 将通过网络传输的 DatagramPacket 对象
            //填充到 packet对象
            socket.receive(packet);
            //可以把packet 进行拆包,取出数据,并显示.
            int length = packet.getLength();//实际接收到的数据字节长度
            data = packet.getData();//接收到数据
            String s = new String(data, 0, length);
            System.out.println(s);
            //关闭资源
            socket.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    服务器端:

    public class UDPReceiverA {
        public static void main(String[] args) throws IOException {
            //1. 创建一个 DatagramSocket 对象,准备在9999接收数据
            DatagramSocket socket = new DatagramSocket(9999);
            //2. 构建一个 DatagramPacket 对象,准备接收数据
            //  一个数据包最大 64k
            byte[] buf = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            //3. 调用 接收方法, 将通过网络传输的 DatagramPacket 对象
            //   填充到 packet对象
            System.out.println("A 等待接收数据..");
            socket.receive(packet);
    
            //4. 可以把packet 进行拆包,取出数据,并显示.
            int length = packet.getLength();//实际接收到的数据字节长度
            byte[] data = packet.getData();//接收到数据
            String s = new String(data, 0, length);
            System.out.println(s);
            //===回复信息给B端
            //将需要发送的数据,封装到 DatagramPacket对象
            data = "Hello,Java!".getBytes();
            //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口
            packet =
                    new DatagramPacket(data, data.length, InetAddress.getByName("192.168.235.128"), 9998);
            socket.send(packet);//发送
            //5. 关闭资源
            socket.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    UDP通信不可靠,少用,了解就行~
    文章最后,分享一点脍炙人口的八股文:

    UDP协议是一种对等通信的实现,发送方只需要接受方的IP(地址)和Port(端口),就可以直接向它发送数据,不需要线连接。每个程序都可以作为服务器,也可以作为客户端。UDP是一种无连接的传输协议,每个数据报的大小限定在64KB以内。数据报是一个在网络上发送的独立信息,它的到达。到达时间以及内容本身等都不能得到保证。这种传输方式是无序的,也不能确保绝对的安全可靠,但它很简单也具有较高的效率。
    使用UDP协议进行数据传输是,需要将需要传输数据定义为数据报(DatagramPaket),在数据报中指明数据所要到达Socket(主机地址和端口号),然后再将数据报发送出去。实例化DatagramPacket时使用参数port和没有使用参数port的区别在与,提供port的一方可以让别人主动发送消息过来,而没有参数port的则会在发送消息时自动绑定一个本地没有使用的端口。在接收到发送的数据报(DatagramPaket)时,不仅可以获取数据,还可以获得发送方的IP和Port,这样就可以向发送方发送数据,因此,本质上二者是对等的。

  • 相关阅读:
    边缘云服务提供商[网心科技],入选2022信通院“可信边缘计算推进计划”首批成员单位
    中国日用化工市场竞争格局展望及未来战略分析报告2022年版
    go get x509:certificate signed by unknown authority
    选择合适的帧率和分辨率:优化RTSP流视频抓取(java)
    线性回归的正则方法:岭回归和Lasso
    想裁剪视频时长,用电脑怎么裁剪视频时长
    Android 10.0 Launcher3禁用widget微件功能实现
    我的sql没问题为什么还是这么慢|MySQL加锁规则
    获取包名下的所有接口
    【计算机基础】Git系列2:配置多个SSH
  • 原文地址:https://blog.csdn.net/weixin_57535055/article/details/127906917