网络通信(二)

news2024/12/30 3:27:47

UDP通信

特点:无连不是先接、不可靠通信

不事先建立连接;发送端每次把要发送的数据(限制在64KB内)、接收端IP、等信息封装成一个数据包,发出去就不管了

java提供了一个java.net.DatagramSocket类来实现UDP通信

DatagramSocket

DatagramSocket 用于创建客户端和服务器端的 UDP 通信。它提供了发送和接收 DatagramPacket 的方法。

构造器:
  • DatagramSocket(int port): 创建绑定到特定端口的 DatagramSocket

  • DatagramSocket(int port, InetAddress laddr): 创建绑定到特定地址和端口的 DatagramSocket

处理数据包方法:
  • send(DatagramPacket p): 发送 DatagramPacket

  • receive(DatagramPacket p): 接收 DatagramPacket

DatagramPacket

DatagramPacket 是用来封装 UDP 数据包的类。它包含数据、源端口、目标端口和 IP 地址。

构造器:
  • DatagramPacket(byte[] buf, int length): 创建一个空的数据包,数据存储在 buf 数组中,长度为 length

  • DatagramPacket(byte[] buf, int length, InetAddress address, int port): 创建一个数据包,指定数据、目标 IP 地址和端口。

创建数据包:
  1. 首先,你需要一个字节数组来存储你的数据。

  2. 然后,使用适当的构造器创建 DatagramPacket 对象。

  3. 对于发送,你需要设置目标 IP 地址和端口。

  4. 对于接收,DatagramSocket.receive() 方法会填充 DatagramPacket 的源 IP 地址和端口。

示例代码

//服务器端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
​
public class UDPServer {
    public static void main(String[] args) throws Exception {
        DatagramSocket serverSocket = new DatagramSocket(12345);//创建服务端对象,指定服务端端口
        byte[] receiveData = new byte[1024];//指定字节数组大小
        DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);//创建数据包
​
        serverSocket.receive(receivePacket);//数据包接收数据
        //接收到指定长度的字节数组并转为字符串
        String receivedMessage = new String(receivePacket.getData(), 0, receivePacket.getLength());
        System.out.println("Server received: " + receivedMessage);
​
        serverSocket.close();
    }
}
//客户端
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
​
public class UDPClient {
    public static void main(String[] args) throws Exception {
        InetAddress IPAddress = InetAddress.getByName("localhost");//创建客户端对象,指定接收主机IP
        int port = 12345;//指定要发送到主机的服务端端口
​
        String sentence = "Hello World!";
        byte[] buf = sentence.getBytes();//封装数据到字节数组中
        DatagramPacket packetToSend = new DatagramPacket(buf, buf.length, IPAddress, port);
​
        DatagramSocket clientSocket = new DatagramSocket();//创建数据包用于接收数据
        clientSocket.send(packetToSend);//数据包发送数据
​
        clientSocket.close();
    }
}

demo1:实现一发一收

  1. 实现客户端、服务端

  2. 创建客户端对象

  3. 创建数据包对象,封装发出的数据

  4. 开始使用数据包发送数据

  5. 释放资源(数据包)

  6. 创建服务端对象(注册端口)

  7. 创建数据包对象,接收数据

  8. 开始使用数据包接收数据

  9. 从字节数组中,把本次接收的数据直接打印出来(注意:可能多次接收数据,字节数组被多次不完全覆盖,因此需要接收多少就倒多少)

  10. (最后,可以通过socket获取客户端的IP地址)

demo2:实现多发多收

直接对demo1进行改造:

  1. 实现客户端时,直接在客户端发送信息的代码块处放入循环中

  2. 注意:此时,客户端只能指定的发一两段信息,因此,直接实现让客户从键盘输入

  3. 实现服务端时,直接在接收处代码块放入死循环中

  4. 注意:服务端的资源一定要在最后关闭,也就是说,资源关闭不包括在循环中,或者说,一般服务端的的资源不会关闭

  5. (最后,可以通过socket获取客户端的IP地址)

TCP通信

特点:面向连接、可靠通信

通信双方事先会采用“三次握手”方式建立可靠连接,实现端到端的通信;底层能保证数据成功传给服务端。

java提供了一个java.net.Socket类和ServerSocket类来实现TCP通信

模型:

在 Java 中,Socket 类是用于建立 TCP(传输控制协议)网络连接的。TCP 是一种面向连接的、可靠的、基于字节流的协议,它确保数据以发送顺序到达目的地。

Socket 类

Socket 类位于 java.net 包中,它代表了一个客户端的 TCP 连接端点。

构造方法

Socket 类有几个构造方法,用于以不同的方式创建套接字:

  1. 无参构造方法:

    Socket()

    创建一个新的未连接的套接字。

  2. 指定服务器地址和端口的构造方法:

    Socket(InetAddress address, int port)
    Socket(String host, int port)

    这两个构造方法都会创建一个新的套接字并尝试连接到指定的 IP 地址和端口,或者主机名和端口。

  3. 指定服务器地址、端口和本地端口的构造方法:

    Socket(InetAddress address, int port, InetAddress localAddress, int localPort)
    Socket(String host, int port, InetAddress localAddress, int localPort)

    这些构造方法允许你指定本地绑定的地址和端口,以及远程服务器的地址和端口。

  4. 指定服务器地址、端口、超时时间和操作模式的构造方法:

    Socket(InetAddress address, int port, boolean stream)
    Socket(String host, int port, boolean stream)

    这些构造方法允许你指定是否使用流模式(对于 TCP,通常设置为 true)。

数据处理方法

Socket 类提供了几个方法来处理数据:

  1. 获取输入流:

    InputStream getInputStream()

    返回一个输入流,用于从套接字读取数据。

  2. 获取输出流:

    OutputStream getOutputStream()

    返回一个输出流,用于向套接字写入数据。

  3. 关闭套接字:

    void close()

    关闭套接字,释放系统资源。

  4. 连接服务器:

    void connect(SocketAddress endpoint)
    void connect(SocketAddress endpoint, int timeout)

    这些方法用于建立到指定套接字地址的连接,可选地指定连接超时时间。

  5. 设置套接字选项:

    • setKeepAlive(boolean on): 设置 TCP 保活机制。

    • setReuseAddress(boolean on): 允许本地地址被重用。

    • setSoLinger(boolean on, int linger): 设置套接字的 SO_LINGER 选项,控制关闭套接字时的行为。

示例代码

客户端:

import java.io.*;
import java.net.*;
​
public class TCPClient {
    public static void main(String[] args) {
        try (Socket socket = new Socket("localhost", 1234)) {
            OutputStream output = socket.getOutputStream();
            output.write("Hello Server!".getBytes());
​
            InputStream input = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int bytesRead = input.read(buffer);
            System.out.println(new String(buffer, 0, bytesRead));
​
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
服务器端:

import java.io.*;
import java.net.*;
​
public class TCPServer {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(1234)) {
            Socket socket = serverSocket.accept();
            InputStream input = socket.getInputStream();
            byte[] buffer = new byte[1024];
            int bytesRead = input.read(buffer);
            System.out.println(new String(buffer, 0, bytesRead));
​
            OutputStream output = socket.getOutputStream();
            output.write("Hello Client!".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这些示例中,客户端创建了一个 Socket 对象来连接服务器,并使用输入输出流来发送和接收数据。服务器端使用 ServerSocket 来监听连接请求,并接受客户端的连接,然后也使用输入输出流来处理数据。

demo1:实现一发一收

  1. 创建客户端的Socket对象(套接字对象),请求与 服务端的连接

  2. 使用socket对象调用getOutputStream()方法得到字节输出流

  3. 使用数据输出流(其他高级流也行,这个最适合)完成数据的发送

  4. 释放资源:关闭socket管道

  5. 创建ServerSocket的对象,同时为服务端注册端口

  6. 使用serverSocket对象,调用accept方法,等待客户端的连接请求得到socket对象

  7. 从socket通信管道中得到一个字节输入流

  8. 把原始的字节输入流包装为数据输入流(其他高级流也行,这个最适合)

  9. 使用数据输入流读取客户端发送的消息

    注意:接收数据时数据输入流对象调用的方法和数据输出流对象调用的方法相对应

    (最后,可以通过socket获取客户端的IP地址)

demo2:实现多发多收

改造demo1:

  1. 将客户端写数据部分代码块放入死循环

  2. 将死循环内改造为用户键盘输入(发送后,调用flush方法刷新立即写出)

  3. 将服务端读取客户端消息放入死循环,并把关闭套接字去掉,实现多收

  4. 注意:如果客户端离线,服务端处会出现异常,因此在服务端捕获异常,就可以知道客户端的在线离线状态

支持服务端与多个客户端同时通信【客户端多开】

改造demo2:

  1. 由于需要每一个套接字都需要一个线程负责,所以需要专门写一个服务端的读取线程类,用于接收并处理套接字

  2. 主要核心的接收信息功能就在run方法中写了

  3. 主线程中拿到套接字时可以适当的输出客户端上线状态

  4. run方法中也可以添加客户端的下线状态(客户端的下线会使得服务端的接收消息处一直等待发生异常,因此需要捕获异常----提示客户端下线)

问题:

为什么UDP通信可以单线程实现多客户端收发信息,但是TCP通信不可以单线程实现多客户端收发信息?

答:

UDP(用户数据报协议)和TCP(传输控制协议)在设计上有着本质的区别,这导致了它们在处理多客户端通信时的不同方式。

UDP通信单线程模型

UDP 是无连接的协议,它的主要特点是简单和高效。在 UDP 中,数据以数据报的形式发送,每个数据报都是独立的,不保证数据报的顺序、可靠性或完整性。因此,UDP 通信可以很容易地使用单线程模型来处理多个客户端:

  1. 无状态:UDP 服务器不需要维护任何关于客户端状态的信息,每个数据报都是独立的,服务器可以简单地接收和发送数据报,而不需要跟踪任何特定的客户端。

  2. 并发处理:由于 UDP 是无连接的,服务器可以同时接收来自多个客户端的数据报,并且不需要为每个客户端创建独立的连接或线程。

  3. 简单性:UDP 的简单性使得它很容易实现一个单线程的服务器,该服务器可以循环接收数据报,并根据需要广播或转发给其他客户端。

TCP通信多线程或非阻塞模型

TCP 是面向连接的协议,它提供了数据的顺序、可靠性和完整性保证。TCP 连接需要经过三次握手过程来建立,并且在连接期间,服务器需要维护每个客户端的状态信息:

  1. 连接状态:TCP 服务器需要为每个客户端维护一个连接状态,这包括序列号、确认号、窗口大小等信息。

  2. 顺序保证:TCP 保证数据的顺序,因此服务器需要确保数据按照正确的顺序发送和接收。

  3. 流量控制和拥塞控制:TCP 需要处理流量控制和拥塞控制,这需要服务器为每个连接维护额外的状态信息。

  4. 资源消耗:由于 TCP 连接需要更多的资源和处理,因此当客户端数量增加时,单线程模型可能会成为性能瓶颈。

  5. 并发限制:单线程处理多个 TCP 连接可能会导致性能问题,因为每个连接都需要进行数据的读取和写入,这可能会阻塞线程,影响其他连接的处理。

因此,为了有效地处理多个 TCP 客户端,通常需要使用多线程模型或非阻塞 I/O(如 Java 的 NIO):

  • 多线程模型:为每个客户端连接创建一个新线程,这样可以并行处理多个连接,但可能会消耗大量资源。

  • 非阻塞 I/O:使用非阻塞 I/O 和事件驱动的方法,如 Java 的 NIO(New I/O)库,可以更高效地处理多个连接,而不需要为每个连接创建一个线程。

总之,UDP 的无连接特性和简单性使其适合于单线程模型,而 TCP 的面向连接特性和对可靠性的保证使其更适合于多线程或非阻塞 I/O 模型。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1697898.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

20.SkyWalking

一.简介 SkyWalking用于应用性能监控、分布式链路跟踪、诊断: 参考连接如下: https://github.com/apache/skywalking https://skywalking.apache.org/docs/ 二.示例 通过官网连接进入下载页面:https://archive.apache.org/dist/skywalkin…

普通人转行程序员,最大的困难是找不到就业方向

来百度APP畅享高清图片 大家好,这里是程序员晚枫,小破站也叫这个名。 我自己是法学院毕业后,通过2年的努力才转行程序员成功的。[吃瓜R] 我发现对于一个外行来说,找不到一个适合自己的方向,光靠努力在一个新的行业里…

美团Java社招面试题真题,最新面试题

如何处理Java中的内存泄露? 1、识别泄露: 使用内存分析工具(如Eclipse Memory Analyzer Tool、VisualVM)来识别内存泄露的源头。 2、代码审查: 定期进行代码审查,关注静态集合类属性和监听器注册等常见内…

Leetcode算法题笔记(3)

目录 矩阵101. 生命游戏解法一解法二 栈102. 移掉 K 位数字解法一 103. 去除重复字母解法一 矩阵 101. 生命游戏 根据 百度百科 , 生命游戏 ,简称为 生命 ,是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子…

Redis简介与安装到python的调用

前言 本文只不对redis的具体用法做详细描述,做简单的介绍,安装,和python代码调用详细使用教程可查看一下网站 https://www.runoob.com/redis/redis-tutorial.html https://pypi.org/project/redis/ 官方原版: https://redis.io/ 中文官网:…

齿轮常见故障学习笔记

大家好,这期咱们聊一聊齿轮常见的失效形式,查阅了相关的资料,做个笔记分享给大家,共同学习。 介绍 齿轮故障可能以多种方式发生。如果在设计阶段本身就尽量防止这些故障的产生,则可以产生改更为优化的齿轮设计。齿轮…

Top期刊:针对论文Figure图片的7个改进建议

我是娜姐 迪娜学姐 ,一个SCI医学期刊编辑,探索用AI工具提效论文写作和发表。 通过对来自细胞生物学、生理学和植物学领域的580篇论文,进行检查和归纳总结,来自德国德累斯顿工业大学的Helena Jambor及合作者,在PLOS Bio…

五分钟搭建一个Suno AI音乐站点

五分钟搭建一个Suno AI音乐站点 在这个数字化时代,人工智能技术正以惊人的速度改变着我们的生活方式和创造方式。音乐作为一种最直接、最感性的艺术形式,自然也成为了人工智能技术的应用场景之一。今天,我们将以Vue和Node.js为基础&#xff…

第12章-ADC采集电压和显示 基于STM32的ADC—电压采集(详细讲解+HAL库)

我们的智能小车用到了ADC测量电池电压的功能,这章节我们做一下。 我们的一篇在这里 第一篇 什么是ADC 百度百科介绍: 我们知道万用表 电压表可以测量电池,或者电路电压。那么我们是否可以通过单片机获得电压,方便我 们监控电池状…

WPF学习日常篇(一)--开发界面视图布局

接下来开始日常篇,我在主线篇(正文)中说过要介绍一下我的界面排布,科学的排布才更科学更有效率的进行敲代码和开发。日常篇中主要记录我的一些小想法和所考虑的一些细节。 一、主界面设置 主界面分为左右两部分,分为…

查分数组总结

文章目录 查分数组定义应用举例LeetCode 1109 题「[航班预订统计] 查分数组定义 差分数组的主要适用场景是频繁对原始数组的某个区间的元素进行增减。 通过这个 diff 差分数组是可以反推出原始数组 nums 的,代码逻辑如下: int res[diff.size()]; // 根…

(2024,SDE,对抗薛定谔桥匹配,离散时间迭代马尔可夫拟合,去噪扩散 GAN)

Adversarial Schrdinger Bridge Matching 公众号:EDPJ(进 Q 交流群:922230617 或加 VX:CV_EDPJ 进 V 交流群) 目录 0. 摘要 1. 简介 4. 实验 0. 摘要 薛定谔桥(Schrdinger Bridge,SB&…

flash-linear-attention中的Chunkwise并行算法的理解

这里提一下,我维护的几三个记录个人学习笔记以及社区中其它大佬们的优秀博客链接的仓库都获得了不少star,感谢读者们的认可,我也会继续在开源社区多做贡献。github主页:https://github.com/BBuf ,欢迎来踩 0x0. 前言 …

MySQL——索引与事务

目录 前言 一、索引 1.索引概述 (1)基本概念 (2)索引作用 (3)索引特点 (4)适用场景 2.索引的操作 (1)查看索引 (2)创建索引…

【算法题】520 钻石争霸赛 2024 全解析

都是自己写的代码,发现自己的问题是做题速度还是不够快 520-1 爱之恒久远 在 520 这个特殊的日子里,请你直接在屏幕上输出:Forever and always。 输入格式: 本题没有输入。 输出格式: 在一行中输出 Forever and always…

2024年推荐的适合电脑和手机操作的线上兼职副业平台

总是会有人在找寻着线上兼职副业,那么在如今的2024年,互联网提供了诸多方便,无论你是宝妈、大学生、程序员、外卖小哥还是打工族,如果你正在寻找副业机会,那么这篇文章将为你提供一些适合电脑和手机操作的线上兼职副业…

[Linux]文件/文件描述符fd

一、关于文件 文件=内容+属性 那么所有对文件的操作,就是对内容/属性操作。内容是数据,属性也是数据,那么存储文件,就必须既存储内容数据,又存储属性数据。默认就是在磁盘中的文件。当进程访问…

知识分享:隔多久查询一次网贷大数据信用报告比较好?

随着互联网金融的快速发展,越来越多的人开始接触和使用网络贷款。而在这个过程中,网贷大数据信用报告成为了评估借款人信用状况的重要依据。那么,隔多久查询一次网贷大数据信用报告比较好呢?接下来随小易大数据平台小编去看看吧。 首先&…

YOLOV5 改进:替换backbone为EfficientNet

1、介绍 本章将会把yolov5的主干网络替换成EfficientNet V2,这里直接粘贴代码 详细的可以参考之前的内容:YOLOV5 改进:替换backbone(MobileNet为例)_yolov5主干网络更换为mobile-CSDN博客 更多的backbone更换参考本专栏: YOLOV5 实战项目(训练、部署、改进等等)_听风吹…

2024电工杯B题平衡膳食食谱的优化设计及评价原创论文分享

大家好,从昨天肝到现在,终于完成了2024电工杯数学建模B题的完整论文啦。 实在精力有限,具体的讲解大家可以去讲解视频: 给大家看一下目录吧: 目录 摘 要: 10 一、问题重述 14 二.问题分析 …