【网络编程】网络编程概念 | TCP和UDP的区别 | UDP数据报套接字编程 | Socket

news2024/12/22 13:53:07

文章目录

  • 网络编程
        • 一、什么是网络编程
          • 1.TCP和UDP的区别
        • 二、UDP数据报套接字编程
          • DatagramSocket
          • DatagramPacket
          • 回显服务器(echo server)

网络编程

一、什么是网络编程
  • 通过网络,让两个主机之间能够进行通信。基于通信来完成一定的功能。

​ 进行网络编程的时候,需要操作系统提供一组API,通过这些API来完成。这些API可以认为的应用层和传输层之间交互的路径。这些API称为Socket API。通过一套Socket API 可以完成不同主机、不同系统之间的网络通信。

​ 传输层提供的网络协议主要有两个:TCP、UDP。这两个协议的特性差异很大,会导致使用这两种协议进行网络编程,会存在一定的差别。系统就分别提供了两套API。

1.TCP和UDP的区别

1.TCP是有连接的,UDP是无连接的。

​ 连接是抽象的概念:此处的连接本质上,就是建立连接的双方,各自保存对方的信息。两台计算机建立连接,就是双方彼此保存了对方的关键信息。TCP要想通信,就需要先建立连接(保存对方信息)。存完之后才能通信。

如果A想和B建立连接,但是B拒绝了,通信就无法完成。

​ UDP想要通信,自己不会去保存对方信息。直接发送数据,不需要征求对方同意。(程序员调用UDP的Socket API会传东对方信息)

2.TCP是可靠传输的,UDP是不可靠传输的。

​ 在网络上进行通信,A给B发送消息。并不能保证100%送达。所以这里可靠传输的概念是:A给B发消息。A可以感知到,消息有没有到达B。就可以在发送失败时,采取一定的措施(尝试重传等)。但同时可靠传输的代价就是机制更复杂以及降低传输效率。

​ TCP就内置了可靠传输机制。

3.TCP是面向字节流的,UDP是面向数据报的。

​ TCP也是和文件操作一样,以字节为单位来进行传输。UDP则是按照数据报为单位进行传输。 UDP数据报有严格的格式。

网络通信数据的基本单位:1.数据报(Datagram)2.数据包(Packet)3.数据帧(Frame)4.数据段(Segment)

4.TCP和UDP都是全双工的。

一个信道,允许双向通信,就是全双工。一个信道,只能单向通信,就是半双工。

在代码中使用一个Socket对象,就可以发送数据也能接受数据。

二、UDP数据报套接字编程

​ Socket是操作系统中的概念,本质上是一种特殊的文件。就相当于把“网卡”这个设备抽象成了文件。后续往Socket文件中写数据,就相当于通过网卡发送数据。从Socket文件中读数据,就相当于通过网卡接收数据。把网络通信和文件操作进行统一。

DatagramSocket

在Java中,使用DatagramSocket这个类来表示系统内部的Socket文件。

DatagramSocket是UDP Socket,用来发送和接收UDP数据报

    public UdpEchoServer(int port) throws SocketException {//指定一个端口号
        socket = new DatagramSocket(port);//创建的socket对象绑定这个指定的端口。
    }

在这里插入图片描述

1.这里的send发送 和receive接收方法,传进的参数类型都是DatagramPacket数据报。

2.receive方法中,参数同样是一个“输出型参数”。

DatagramPacket
  • DatagramPacket这个类,表示一个UDP数据报。

    构造方法:

    1.只指定字节数组缓冲区

            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //用来承载从网卡中读到的数据。收到数据的时候需要搞一个内存空间来保存这个数据
            //DatagramPacket内部不能自行分配内存空间,需要程序员手动创建空间,交给DatagramPacket处理
            socket.receive(requestPacket);

2.指定字节数组缓冲区,同时制定一个InetAdress对象(包含了IP和端口号)

            DatagramPacket responsePacket = new DatagramPacket(
                    response.getBytes(), //指定的数据
                    response.getBytes().length, //数据的长度
                    requestPacket.getSocketAddress());//发送来的地址就是要发送的地址。

    public synchronized SocketAddress getSocketAddress() {
        return new InetSocketAddress(getAddress(), getPort());
    }

3.指定字节数组缓冲区,指定IP + 端口号。

    	 DatagramPacket requestPacket = new DatagramPacket( 
                request.getBytes(), request.getBytes().length,
                InetAddress.getByName(serverIp), serverPort);

​ UDP是面向数据报的,每次进行传输,都要以UDP数据报为基本单位。在DatagramSocket的方法中,接收和发送传入的参数就是DatagramPacket数据报类型。

回显服务器(echo server)
  • 写一个简单的客户端、服务器通信程序。单纯调用Socket API。从控制台上输入一个请求发送给服务器,服务器收到字符串后,原封不动返回给客户端并显示出来。

​ 服务器和客户端都需要创建Socket对象。但是 服务器的socket一般要显示的指定一个端口号。而客户端的socket一般不能显示指定。(不显示指定,系统会自动分配一个随机的端口)

​ 服务器上有哪些程序,都使用哪些端口,都是程序员可控的。写代码时就可以指定空闲的端口,给当前的服务器使用。相比之下,客户端不可控。交给系统分配一个空闲的端口给客户端。所以服务器需要手动指定端口。客户端要交个系统来分配一个空闲端口。

UdpEchoServer

1.读取请求并解析

2.根据请求计算响应(一个服务器最核心的步骤)

3.把响应写回客户端

4.打印一个日志,把这次数据交互的详情打印出来

public class UdpEchoServer {
    //1.先出创建DatagramSocket对象 :后续操作网卡的基础
    private DatagramSocket socket = null;

    public UdpEchoServer(int port) throws SocketException {//指定一个端口号
        socket = new DatagramSocket(port);//创建的socket对象绑定这个指定的端口。
    }

    /**
     * 通过这个方法启动服务器
     */
    public void start() throws IOException {
        System.out.println("服务器启动!");
        //在服务器程序中,经常出现while true的代码
        while (true) {
            //1.读取请求并解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            //用来承载从网卡中读到的数据。收到数据的时候需要搞一个内存空间来保存这个数据
            //DatagramPacket内部不能自行分配内存空间,需要程序员手动创建空间,交给DatagramPacket处理
            socket.receive(requestPacket);
            //读取数据,并填充进DatagramPacket,如果没有接收到数据报,receive方法会阻塞等待
            //此时是以二进制的形式存到DatagramPacket中,需要把二进制转换成字符串
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            //获取到字符数组,取[0,getLength]区间内的字节,构造成String. getLength不是4096,是实际的收到的数据长度

            //2.根据请求计算响应(一个服务器最核心的步骤)
            //由于此处是回显服务器,请求是啥样,响应就是啥样。
            String response = process(request);
            //3.把响应写回客户端
            //创建一个响应对象,DatagramPacket,往对象里构造刚才的数据,再通过send进行返回。
            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 {
        UdpEchoServer server = new UdpEchoServer(9090);
        server.start();
    }
  • 需注意的是,这里并没有写close.因为socket是文件描述符表的一个表项,文件描述符表在PCB上(跟随进程)。
  • socket在整个程序运行的过程中都需要使用,不能提前关闭。当不在再需要时,意味着程序就要结束了,进程结束,文件描述符表就会销毁。随着销毁,被系统自动回收
public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIp = "";
    private int serverPort = 0;

    public UdpEchoClient(String ip, int port) throws SocketException {
        //客户端的socket对象的端口,由系统自动分配
        socket = new DatagramSocket();
        //由于UDP不会持有对端的信息,需要在应用程序里,把对端的情况记录下来。
        serverIp = ip;
        serverPort = port;
    }

    public void start() throws IOException {
        System.out.println("客户端启动!");
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1.从控制台读取数据,作为请求
            System.out.print("->");
            String request = scanner.next();
            //2.把请求内容构造成DatagramPacket对象,再发给服务器
            DatagramPacket requestPacket = new DatagramPacket(
                    request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIp), serverPort);//把转换字符串ip
            socket.send(requestPacket);
            //3.尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(responsePacket);
            //4.把响应,转换成字符串,并显示出来
            String response = new String(responsePacket.getData(),0,responsePacket.getLength());
            System.out.println(response);
        }
    }

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

1.服务器先启动,服务器启动之后,就会进入循环,执行到receive这里并进行阻塞

2.客户端开始启动,也会先进入while循环,执行到scanner.next,进行阻塞。当用户输入完成后,next就会返回,从而构造请求数据并进行发送给服务器。

3.服务器从receive中返回,进一步执行解析请求为字符串,执行process操作,执行send操作。

与此同时,客户端继续往下执行,执行到receive等待服务器的响应,进行阻塞。

4.客户端收到从服务器返回的数据后,就会从receive中返回,执行打印

5.服务器完成一次循环后,有执行到receive进行阻塞。客户端完成一次循环后,又执行到scanner.next进入阻塞,直到用户进行输入。

点击移步博客主页,欢迎光临~

偷cyk的图

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

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

相关文章

Linux网络-DNS域名解析服务

目录 一.DNS相关介绍 1.DNS是什么 2.DNS系统的分布式数据结构 根域 顶级域 二级域 子域 主机 3.服务器类型 主域名服务器 从域名服务器 缓存域名服务器 转发域名服务器 二.DNS域名解析 1.DNS域名解析方式及功能 2.DNS域名解析查询方式 2.1.递归查询&#xff0…

Verilog基础语法——parameter、localparam与`define

Verilog基础语法——parameter、localparam与define 写在前面一、localparam二、parameter三、define写在最后 写在前面 在使用Verilog编写RTL代码时,如果需要定义一个常量,可以使用define、parameter和localparam三种进行定义与赋值。 一、localparam …

施耐德EOCR-2CT-300/5电流互感器 50HZ 5VA

EOCR主要产品有电子式电动机保护继电器,电子式过电流继电器,电子式欠电流继电器,电子式欠电压继电器,其它保护和监视装置,电流互感器。 施耐德EOCR-2CT-300/5电流互感器 EOCR-2CT系列型号: EOCR 2CT 100…

操作系统安全:Linux安全审计,Linux日志详解

「作者简介」:2022年北京冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础对安全知识体系进行总结与归纳,著作适用于快速入门的 《网络安全自学教程》,内容涵盖系统安全、信息收集等…

18.Nacos配置管理-微服务读取Nacos中的配置

需要解决的问题 1.实现配置更改热更新,而不是改动了配置文件还要去重启服务才能生效。 2.对多个微服务的配置文件统一集中管理。而不是需要对每个微服务逐一去修改配置文件,特别是公共通用的配置。 配置管理服务中的配置发生改变后,回去立…

leetcode-二叉树的镜像-91

题目要求 思路1 1.遍历一遍二叉树,将左边的结点对应创建一个右边的结点 2.用此方法空间复杂度O(n),并不是最优 思路2 1.将一个结点的左右子树进行交换,如果左子树还有左右结点,就再交换左子树的左右结点,以此递归下去…

vscode 创建代码模版

在vscode中快捷创建代码模版 1.在VSCode中,按下Ctrl Shift P(Windows/Linux)或Cmd Shift P(Mac)打开命令面板。 2.然后输入"Preferences: Configure User Snippets"并选择该选项。打开一个json文件用户…

Python函数小知识

目录 一、函数的定义和调用 二、函数参数 三、函数作用域 四、递归函数和匿名函数 一、函数的定义和调用 def 函数名(参数): 自定义函数可以分为有参函数和无参函数 。 函数的作用: 在Python中定义函数可以提高代码的复用率,避免重复的代码,…

matlab保存示波器数据

再重新运行一下示波器 然后就可以在工作区看见(这里没有运行所以没有) 将保存到文件夹中方便后续绘图

服务于金融新核心系统 星辰天合与中电金信完成产品兼容认证

近日,北京星辰天合科技股份有限公司(简称:XSKY星辰天合)与中电金信软件有限公司(简称:中电金信)完成产品兼容性认证,星辰天合的企业级分布式统一数据平台 XEDP 符合金融级数字底座&q…

“一个有趣的C语言代码”分析

“一个有趣的C语言代码” 一个有趣的C语言代码-流浪的海豚-ChinaUnix博客 #include <stdio.h> int print() {printf("hello world!\n");return 0; } int main(void) {long base[0];long* result base3;*(result1) *result;*result (long)print;return 0; …

Qt : 在QTreeWidget中添加自定义右键菜单

一、引言 如图&#xff0c;我们需要在一个QTreeWidget 控件中添加了自定义右键菜单。 二、思路 如何做到的呢&#xff0c;很简单。浅浅记录和分享一下。 继承QTreeWidget&#xff0c;定义一个子类CustomTreeWidget &#xff0c;在重写contextMenuEvent 事件即可。 三、代…

数据结构初阶——树和二叉树

数据结构初阶——树和二叉树 1. 树的概念和结构1.1 树的概念1.2 树的表示 2. 二叉树2.1 二叉树的概念和结构2.2 二叉树的存储结构2.2.1 顺序存储2.2.2 链式存储 3. 二叉树的顺序结构及实现——堆3.1 堆的概念和结构3.2 堆的实现3.2.1 堆的定义3.2.2 堆的向上调整3.2.3 堆的向下…

Pycharm/Dataspell中使用jupyter导入ros humble包

配置ros humble对应python包路径文件 首先在~/.local/lib/python3.10/site-packages目录下新建一个.pth文件&#xff0c;如下图所示。 将对应的ros humble的python包的路径配置在上述文件中&#xff0c;一行放置一个路径&#xff0c;对应的路径如下图所示。 完成上述操作后…

ESP32 IDF环境 连接WIFI

新建wifi.h #ifndef __WIFI_H_ #define __WIFI_H_#include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h&…

IntelliJ IDEA - 10 款 IDEA 宝贝插件,YYDS!

好久没发这种实用贴了&#xff0c;最近用到了一些能提升工作效率的IDEA插件&#xff0c;给小伙伴们分享一下。相信我&#xff0c;我分享的这些插件&#xff0c;都是实实在在能解决实际开发场景中痛处的。 1、POJO to JSON 开发工作中&#xff0c;常常在设计完API后&#xff0c…

recat组件使用Antd

安装和初始化 项目中进行安装yarn add antd或者npm i antd安装 使用 在App.js文件中引入按钮并使用 需要引入自己的css文件 模块化 import React, { Component } from react import { Button } from antd; //引入按钮 import antd/dist/antd.css; //还需要引入css样式 …

javascript使用setTimeout函数来实现仅执行最后一次操作

在JavaScript中&#xff0c;setTimeout函数用于在指定的毫秒数后执行一个函数或计算表达式。它的主要用途是允许开发者延迟执行某些代码&#xff0c;而不是立即执行。 当我们想要确保仅最后一次更新UI时&#xff0c;我们可以使用setTimeout来合并多次连续的更新请求。具体做法…

Ansible自动化

Ansible自动化 自动化的需求&#xff1a; 1. 在什么样的场景下需要自动化&#xff1f; 批量化的工作&#xff1a; 装软件包、配置服务、升级、下发文件… 2. 为什么在自动化工具中选择ansible&#xff1f; 对比shell脚本&#xff1a; 相对于用shell的脚本来实现自动化&#x…

vue+element 树形结构 改成懒加载模式(原理element有),这里只做个人理解笔记

1 找到属性标签添加 lazy 和 :load"loadNode" 这两个属性 2 引入树形接口,并和后端约定好传值,(拿我的举例 第一次获取全部父级默认第一次传参数:{ parentId : 0},可获取全部父级 第二次通过点击的子级把子级id传进去,这一步就用到了:load"loadNode&quo…