TCP VS UDP

news2025/1/9 0:00:42

程序员写网络程序,主要编写的应用层代码!

真正要发这个数据,需要上层协议调用下层协议,应用层要调用传输层,则传输层给应用层提供一组api,统称为:soket api

基于UDP的api

基于TCP的api

这两个协议差别很大!!提供的api的差异也很大!!

UDP:

  • 无连接:使用UDP通信的双方,不需要可以得存对端的相关信息
  • 不可靠传输:消息发了就发了,不关注结果(投递简历)
  • 面向数据报:以一个UDP数据报为基本单位
  • 全双工:一条路径,双向通信

TCP:

  • 有链接:使用TCP通信的双方,需要刻意保存对方的相关信息
  • 可靠传输:不是说发了就100%能够到达对方,尽可能的传输过去(自己知道没成功)(打电话)
  • 面向字节流:以字节为传输的基本单位,读写方式非常灵活
  • 全双工:一条路径,双向通信

在上述所说的连接:并不是拿一根绳子,把两个设备绑一块,而是一个”抽象的连接“,可以理解成通信双方,各自记录了对方的信息;比如:民政局领结婚证!!(男女双方建立连接)!!

全双工 VS 半双工

那么,有了上述的基础知识,我们先来了解一下UDP的api吧(比TCP的要简单!)

两个重要的方法:DatagramSocket()   DatagramPacket()

DatagramPacket() :这个对象是一个UDP数据报

DatagramSocket()  :Datagram:就是数据报;Socket说明这个对象是一个socket对象

socket对象相当于对应到系统中一个特殊的文件(socket文件)

socket文件并非对应到硬盘上的某个数据存储区域,而是对应到网卡,这个硬件设备!!

要想进行网络通信,就需要有socket文件这样的对象,借助这个socket文件对象,才能够间接的操作网卡!(遥控器)

往这个socket对象中写数据,相当于通过网卡发送数据

从这个socket对象中读数据,相当于通过网卡接收数据

那么,我们来看一下DatagramSocket()  的构造方法:

  1. DatagramSocket() :创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
  2. DatagramSocket(int port):创建一个UDP数据报套接字的Socket,绑定到本机指定的端口下(一般用于服务端)

    这两个构造方法中,一个带参数,一个不带参数

    服务器这边的socket往往要关联一个具体的端口号!(必须要不变)

    客户端这边则不需要手动指定,系统自动分配即可!(不要求)

  3. void  receive(DatagramPacket  p)从此套接字接收数据报(如果没有接受到数据报,该方法就会阻塞等待)
  4. void  send(DatagramPacket  p)从此套接字发送数据报(不会阻塞等待,直接发送)
  5. void  close()关闭此数据报套接字(一定是socket/文件,确定不用了,才能使用该close())

socket也是文件,文件用完了就得记得关闭,否则会出现文件资源泄露的问题!!

那么,我们来看一下DatagramPacket() 的构造方法:

  1. DatagramPacket(byte[]  buf , int length) 构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
  2. DatagramPacket(byte[]  buf , int offset , int length , SocketAddress address) 构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length),address指定目的主机的IP和端口号!!
    这个版本,需要显式的设置地址进去,通常要用来发送信息!!

那么,我们来基于UDP  Socket来写一个最简单的客户端服务器程序吧!!

回显服务器(echo server):客户端发了个请求,服务器返回一个一模一样的响应!

一个服务器:主要要做三个核心工作:

  1. 读取请求并解析
  2. 根据请求计算响应(省略)
  3. 把响应返回到客户端

主要代码:

服务器(读取请求,发送响应)

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

// UDP 版本的回显服务器
public class UdpEchoServer {
    // 网络编程, 本质上是要操作网卡.
    // 但是网卡不方便直接操作. 在操作系统内核中, 使用了一种特殊的叫做 "socket" 这样的文件来抽象表示网卡.
    // 因此进行网络通信, 势必需要先有一个 socket 对象.
    private DatagramSocket socket = null;

    // 对于服务器来说, 创建 socket 对象的同时, 要让他绑定上一个具体的端口号.
    // 服务器一定要关联上一个具体的端口的!!!
    // 服务器是网络传输中, 被动的一方. 如果是操作系统随机分配的端口, 此时客户端就不知道这个端口是啥了, 也就无法进行通信了!!!
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        System.out.println("服务器启动!");
        // 服务器不是只给一个客户端提供服务就完了. 需要服务很多客户端.
        while (true) {
            // 只要有客户端过来, 就可以提供服务.
            // 1. 读取客户端发来的请求是啥.
            //    receive 方法的参数是一个输出型参数, 需要先构造好个空白的 DatagramPacket 对象. 交给 receive 来进行填充.
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);
            // 此时这个 DatagramPacket 是一个特殊的对象, 并不方便直接进行处理. 可以把这里包含的数据拿出来, 构造成一个字符串.
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            // 2. 根据请求计算响应, 由于此处是回显服务器, 响应和请求相同.
            String response = process(request);
            // 3. 把响应写回到客户端. send 的参数也是 DatagramPacket. 需要把这个 Packet 对象构造好.
            //    此处构造的响应对象, 不能是用空的字节数组构造了, 而是要使用响应数据来构造.
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            // 4. 打印一下, 当前这次请求响应的处理中间结果.
            System.out.printf("[%s:%d] req: %s; resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }

    // 这个方法就表示 "根据请求计算响应"
    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        // 端口号的指定, 大家可以随便指定.
        // 1024 -> 65535 这个范围里随便挑个数字就行了.
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
}

客户端:发请求,接收响应

package network;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

// UDP 版本的 回显客户端
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp = null;
    private int serverPort = 0;

    // 一次通信, 需要有两个 ip, 两个端口.
    // 客户端的 ip 是 127.0.0.1 已知.
    // 客户端的 port 是系统自动分配的.
    // 服务器 ip 和 端口 也需要告诉客户端. 才能顺利把消息发个服务器.
    public UdpEchoClient(String serverIp, int serverPort) throws SocketException {
        socket = new DatagramSocket();
        this.serverIp = serverIp;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            // 1. 从控制台读取要发送的数据
            System.out.print("> ");
            String request = scanner.next();
            if (request.equals("exit")) {
                System.out.println("goodbye");
                break;
            }
            // 2. 构造成 UDP 请求, 并发送
            //    构造这个 Packet 的时候, 需要把 serverIp 和 port 都传入过来. 但是此处 IP 地址需要填写的是一个 32位的整数形式.
            //    上述的 IP 地址是一个字符串. 需要使用 InetAddress.getByName 来进行一个转换.
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);
            socket.send(requestPacket);
            // 3. 读取服务器的 UDP 响应, 并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            // 4. 把解析好的结果显示出来.
            System.out.println(response);
        }
    }

    public static void main(String[] args) throws IOException {
        UdpEchoClient client = new UdpEchoClient("127.0.0.1", 9090);
        // UdpEchoClient client = new UdpEchoClient("42.192.83.143", 9090);
        client.start();
    }
}

对上述代码进行分析:

对于客户端:

  1. 服务器先启动,执行到receive进行阻塞
  2. 客户端运行之后,从控制台读取到数据,并进行send
  3. 客户端这边send之后,继续往下走,走到receive读取响应,会阻塞等待
  4. 客户端这边真正收到服务器send回来的数据之后,就会接解除阻塞,执行下面的打印操作
  5. 客户端继续进入下一轮循环,阻塞在Scanner.next这里,等待用户输入新的数据

对于服务器:

  1. 服务器先启动,执行到receive进行阻塞
  2. 客户端运行之后,从控制台读取到数据,并进行send
  3. 服务器这边,就从reserve返回,读取到请求数据(客户端发来的),往下走到process,生成响应,再往下走,到send,并且打印日志
  4. 进入下一轮循环,在此阻塞在receive,等待客户端下一个请求

小结一下瞬间开心:

客户端:

读取用户输入

构造请求并发送

客户端读取服务器响应

客户端把响应转成字符串并显示出来

服务器:

读取用户的请求

根据请求计算响应

把响应写回到客户端

因此:总的大致过程为:

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

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

相关文章

基于电容电流前馈与电网电压全前馈的三相LCL并网逆变器谐波抑制Simulink仿真

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

java Spring Boot RequestHeader设置请求头,当请求头中没有Authorization 直接400问题解决

我在接口中 写了一个接收请求头参数 Authorization 但是目前代理一个问题 那就是 当请求时 请求头中没有这个Authorization 就会直接因为参数不匹配 找不到指向 这里其实很简单 我们设置 value 为我们需要的字段内容 required 是否必填 我们设置为 false 就可以了 这样 没有也…

网友咨询:网上申请的流量卡为什么营业厅买不到,原因你知道吗?

在网上买过流量卡的朋友应该都清楚,很多便宜的套餐只能够在网上购买,营业厅里是无法办理的,那么您知道这是为什么吗? 作为一个专业从事的流量卡行业的小编,今天就告诉你,为什么网上的流量卡在营业厅里买不到…

C++ - 右值引用 和 移动拷贝

右值引用 我们先来了解什么是左值,什么是右值: 左值 和 有值 区分 首先,左值 和 右值 并不是完全意味着 在 "" 左边的就是 左值 ; 在 "" 右边的就是右值。这是不一定的。只能说,在左边的大概率是…

苹果怎么设置铃声?个性化铃声设置教程来了!

朋友们!苹果系统自带的铃声听腻了,有什么方法能将喜欢的歌曲设为铃声吗? 苹果手机系统的铃声一般都是通用的,虽然种类很多但是会让大家感觉比较单调。为了使铃声更加有趣,更加有个性化,很多用户选择将自己喜…

谷粒商城错误总结

1. P10:老师的mysql配置文件为my.cnf,我的改成my.conf 2. P16: 前端用npm install 命令报错(Error: Integrity checksum failed when using sha512: wanted sha512-o;我的解决方案:重启VSCode,重新npm install,问题解决…

重构我们的代码_遵循DRY原则

在JavaScript中,DRY原则是指"Don’t Repeat Yourself",即不要重复自己的代码。它是一种编程原则,强调避免在代码中出现重复的逻辑、功能和结构。 遵循DRY原则有以下几个好处: 代码重用:通过将重复的代码抽象…

IDEA工具之debug第三方jar包源码顺序错乱

IDEA工具之debug第三方jar包源码顺序错乱 场景描述原因分析解决方案 场景描述 概述 使用IDEA排查问题时,经常需要debug源码,当F7跟进依赖jar包时,总是出现Decompiled.class file, bytecode version:51.0 (Java 7),提示Source code…

Go-Python-Java-C-LeetCode高分解法-第九周合集

前言 本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接:LeetCode-Go-Python-Java-C 欢迎订阅CSDN专栏,每日一题,和博主一起进步 LeetCode专栏 我搜集到了50道精选题,适合速成概览大部分常用算法 突…

双机并联逆变器自适应虚拟阻抗下垂控制(Droop)策略Simulink仿真模型

💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…

深挖 Python 元组 pt.1

哈喽大家好,我是咸鱼 好久不见甚是想念,2023 年最后一次法定节假日已经结束了,不知道各位小伙伴是不是跟咸鱼一样今天就开始“搬砖”了呢? 我们知道元组(tuple)是 Python 的内置数据类型,tupl…

案例解读【淘宝API接口的运用:抓取用户数据从而驱动精准营销

我国网络购物用户规模8.12亿占网民整体80.3%(来源:中商产业研究院)。由此可见,网络购物逐渐成为人们普遍选择的一种消费方式。作为连接买卖双方的服务方,电商平台掌握了海量的用户数据,用户数据作为一种宝贵…

electron.js入门-为生产环境构建应用程序

在本章中,我们将学习如何使用可执行文件生成生产应用程序;为此,我们将使用以下软件包: https://www.electron.build/ 需要注意的是,当您有兴趣生成应用程序的可执行文件时,必须在每个Electron.js项目中安装…

pmql基本使用

简介 Prometheus 通过指标名称(metrics name)以及对应的一组标签(labelset)唯一 定义一条时间序列。指标名称反映了监控样本的基本标识,而 label 则在这个基本特征上为 采集到的数据提供了多种特征维度。用户可以基于…

练[极客大挑战 2019]BuyFlag

[极客大挑战 2019]BuyFlag 文章目录 [极客大挑战 2019]BuyFlag掌握知识解题思路关键paylaod 掌握知识 ​ cookie字段的修改,代码审计,数字和字符串弱等于绕过,科学计数法替换长数字,GET请求包转换POST请求包 解题思路 打开题目…

php递归生成树形结构 - 无限分类 - 构建树形结构 - 省市区三级联动

直接上代码 示例 <?php/*** php递归生成树形结构 - 无限分类 - 构建树形结构 - 省市区三级联动* * param array $lists 一维数组&#xff0c;包括不同级别的各行数据* param int $parentId 目标节点的父类ID (可以是顶级分类的父ID&#xff0c;也可以是任意节点的父ID)* …

EfficientViT: 高效视觉transformer与级联组注意力

文章目录 摘要1、简介2、用视觉transformer加速2.1. 内存效率2.2. 计算效率2.3. 参数效率 3、高效视觉Transformer3.1、EfficientViT的构建块3.2、EfficientViT网络架构 4、实验4.1、实现细节4.2、ImageNet上的结果4.3、迁移学习结果4.4、 消融实验 5、相关工作6、结论 摘要 2…

手机投屏电脑软件AirServer5.6.3.0最新免费版本下载

随着智能手机的普及&#xff0c;越来越多的人喜欢用手机观看视频、玩游戏、办公等。但是&#xff0c;有时候手机屏幕太小&#xff0c;不够清晰&#xff0c;也不方便操作。这时候&#xff0c;如果能把手机屏幕投射到电脑上&#xff0c;就可以享受更大的视野&#xff0c;更流畅的…

【C++设计模式之命令模式:行为型】分析及示例

简介 命令模式是一种行为型设计模式&#xff0c;它将请求封装成一个对象&#xff0c;从而使不同的请求可以被参数化、队列化或记录化。这种模式允许请求的发送者和接收者进行解耦&#xff0c;同时提供更高的灵活性和可扩展性。 描述 命令模式的核心思想是通过命令对象来封装…

WIN11右键菜单更改为WIN7风格

1.按键盘上的 Win R 组合键&#xff0c;打开运行&#xff0c;输入 regedit 命令&#xff0c;按确定或回车&#xff0c;打开注册表编辑器&#xff1b; 2.注册表编辑器窗口中&#xff0c;依次展开到以下路径&#xff1a; HKEY_CURRENT_USER\Software\Classes\CLSID\ 3.左侧定位…