网络编程(JavaEE初阶系列10)

目录

前言:

1.网络编程的基础

1.1为什么需要网络编程

1.2什么是网络编程

1.3网络编程中的基本概念

1.3.1发送端和接收端

1.3.2请求和响应

1.3.3客户端和服务端

2.Socket套接字

2.1概念

2.2分类

3.UDP数据报套接字编程

3.1DataGramSocket API

3.2DatagramPacket API

3.3基于UDP的回显服务器(echo server)

3.4简单的翻译服务器

4.TCP流套接字

4.1ServerSocket API

4.2Socket API

4.3基于TCP的回显程序

5.再谈协议

结束语:


前言:

在上一节中小编主要是与大家分享了一些有关于网络的基础知识,但是里面的细节和基础的编程还没有给大家来交代,这节中小编就给大家俩交代一下有关于网络基础编程方面的一些基础的编程吧,大家赶快跟上小编的步伐一起来往下看吧。如果还没有看小编网络基础知识的部分的同学建议先去看看这篇博文吧:☞http://t.csdn.cn/aj9ov

1.网络编程的基础

1.1为什么需要网络编程

用户在浏览器中,打开在线视频网站,比如抖音短视频其实质是通过网络,获取到网络上的一个视频资源,与本地打开视频文件类似,只是视频文件这个资源的来源是网络,相比本地资源来说,网络提供了更为丰富的网络资源,所谓的网络资源,其实就是在网络中可以获取的各种数据资源,而所有的网络资源,都是通过网络编程来进行数据传输的。

1.2什么是网络编程

网络编程:指网络上的主机,通过不同的进程,以编程的方式实现网络通信(网络数据传输)

当然,我们只要满足进程不同就行,所以即便是同一个主机,只要是不同进程,基于网络来传输数据,也属于网络编程。

1.3网络编程中的基本概念

1.3.1发送端和接收端

在一次网络数据传输时:

  • 发送端:数据的发送方进程,称为发送端。发送端主机即网络通信中的源主机。
  • 接收端:数据的接收方进程,称为接收端。接收端主机即网络通信中的目的主机。
  • 收发端:发送端和接收端两端,也简称Wie收发端。

注意:发送端和接收端只是相对的概念,只是一次网络数据传输产生数据流向后的概念。

1.3.2请求和响应

一般来说,获取一个网络资源,涉及到两次网络数据传输。

  • 第一次:请求数据的发送。
  • 第二次:响应数据的发送。

就像是在餐厅点饭一样,先发起请求:点一份蛋炒饭。餐厅在给一个响应:提供一份蛋炒饭。

1.3.3客户端和服务端

  • 服务端:在常见的网络数据传输的场景下,把提供服务的这一方进程,称为服务端,可以提供对外服务。
  • 客户端:获取服务的一方进程,称为客户端。

对于服务来说,一般是提供:

  • 客户端获取服务资源。
  • 客户端保存资源在服务端。

就像是我们在银行办事:

  • 银行提供存款服务:用户(客户端)保存现金(资源)在银行(服务端)。
  • 银行提供取款服务:用户(客户端)获取服务端资源(银行替用户保管现金)。

2.Socket套接字

2.1概念

Socket套接字是操作系统提供给应用程序的一组用于网络编程的API。他是基于TCP/IP协议的通信的的基本操作单元。

注意:操作系统原生的Socket API是C语言的但是这里我们学习的是Java封装之后的版本。

2.2分类

Socket套接字主要针对传输层协议划分为如下三类:

  • 数据报套接字:使用传输层UDP协议。

UDP,即User Datagram Protocol(用户数据报协议),传输层协议。它的特点是:无连接、不可靠传输、面向数据报、全双工。

  • 流套接字:使用传输层TCP协议。

TCP,即Transmission Control Protocol(传输控制协议),传输层协议。它的特点是:有连接、可靠传输、面向字节流、全双工。

对于字节流来说,可以简单理解为传输的数据是基于IO流的,流式数据的特征就是在IO流没哟关闭的情况下,是无边界的数据,可以多次发送,也可以分开多次接收。

  • 原始套接字:

原始套接字用于自定义传出层协议,用于读写内核没有处理的IP协议数据,这里我们对此不做过多讨论,我们重点是理解和应用前两个。

TCP特点vsUDP特点:

UDP特点TCP特点
无连接:使用UDP通信双方不需要刻意保存对方的相关信息有连接:使用TCP通信双方则需要刻意保存对方的相关信息
不可靠传输:消息发了就发了不关注结果可靠传输:不是说发送之后对方就可以100%能够达到对方,这要求就太高了,只是说尽可能的传输过去。
面向数据报:以UDP数据报为基本单位。面向字节流:以字节为传输的基本单位,读写方式非常灵活
全双工:一条路径,双向通信全双工:一条路径,全向通信。

解释:全双工vs半双工。

  • 全双工:是一条路径,全向通信,你可以理解为,一个双向通道的马路。
  • 半双工:是一条路径,只能由一侧向另一侧通信,你可理解为单向通道的马路。

针对上述的TCP协议和UDP协议也给我们提供了两组不同的API。下面我们来一步一步的了解一下。

3.UDP数据报套接字编程

3.1DataGramSocket API

DataGramSocket 是UDP Socket,用于发送和接收UDP数据报,所谓Socket,是一个特殊的文件,是网卡这个硬件设备的抽象表示,你也可以理解为是一个遥控器,想要进行网络通信就需要有socket文件这样的对象,借助这个socket文件对象,才能够间接的操作网卡。

  • 往这个socket对象里写数据,相当于通过网卡发送消息。
  • 从这个socket对象中读数据,相当于通过网卡接收消息。

DatagramSocket的构造方法,可以绑定一个端口号(服务器),也可以不显示指定客户端。

方法签名方法说明
DatagramSocket()创建一个UDP数据报套接字的Socket,绑定到本机任意一个随机端口(一般用于客户端)
DatagramSocket(int port)创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端)
  • 服务器这边的socket往往要关联一个具体的端口号。
  • 客户端这边则不需要手动指定,系统会自动分配一个闲置的端口号。

举个例子:

比如现在我开了一家餐厅,要发传单,那么在传单上面我这边可定是要标清楚我的餐厅的具体位置在哪,窗口号是多少,都得事先分配好,此时我开的这家餐馆就相当于是服务器,确定的地址和窗口号就是服务器事先分配好的端口号。那么如果此时客人看到我发的传单就来到我的餐馆吃饭了,那么它点完餐之后,就会随便找一个空着的位置坐下,等饭。此时客人就相当于是客户端,随便找的位置就是系统给随机分配的一个空闲的端口号。

 DatagramSocket方法

方法签名方法说明
void receive(DatagramPacket p)从此套接字接收数据报(如果没有接收到数据报,该方法会阻塞等待)
void send(DatagramPacket p)从此套接字发送数据报(不会阻塞等待,直接发送)
void close()关闭数据报套接字(释放资源)

注意:

  • DatagramPacket表示一个UDP数据报。
  • 在close的时候,到底啥时候调用close,一定是要socket/文件,确定一定以及肯定不再使用,此时才能调用close。

3.2DatagramPacket API

DatagramPacket是UDPSocket发送和接收的数据报。

DatagramPacket构造方法

方法签名方法说明
DatagramPacket(byte[] buf, int length)构造一个DatagramPacket以用来接收数据报,接收的数据保存在字节数组(第一个参数buf)中,接收指定长度(第二个参数length)
DatagramPacket(byte[] buf, int offset, int lenght, SocketAddress address)构造一个DatagramPacket以用来发送数据报,发送的数据为字节数组(第一个参数buf)中,从0到指定长度(第二个参数length),address指定目的的主机的IP和端口号。

 DatagramPacket方法

方法签名方法说明
InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址,或从发送的数据报中,获取接收端主机IP地址。
int getPort()从接收的数据报中,获取发送端主机的端口号,或从发送的数据报中,获取接收端主机端口号。
byte[] getData()获取数据报中的数据。

3.3基于UDP的回显服务器(echo server)

介绍了DatagramSocket 和 DatagramPacket API之后,我们基于UDP socket写一个简单的客户端服务器程序。 也就是让客户端发一个请求,在服务器上返回一个一模一样的响应。

首先来明确一点,一个服务器主要做的三个核心的工作:

  1. 读取请求并解析。
  2. 根据请求并计算响应。(代码中省略了)
  3. 把响应返回给客户端。

服务端代码:

package network;
//服务端
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class UdpEchoServer{
    //需要先定义一个Socket对象
    //通过网络通信,必须要使用socket对象
    private DatagramSocket socket = null;
    //绑定一个端口,不一定能成功
    //如果某个端口已经被别的进程占用了,此时这里的绑定操作就会出错。
    //同一个主机上,一个端口,同一个时刻,只能被一个进程绑定

    public UdpEchoServer(int port) throws SocketException {
        //构造socket的同时,指定要关联/绑定的端口。
        socket = new DatagramSocket(port);
    }

    //启动服务器的主逻辑
    public void start() throws IOException {
        System.out.println("服务器启动成功!");
        while (true) {
            //每次循环,要做三件事
            //1.读取请求并解析
            //  构造空饭盒
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            // 食堂大妈给饭盒里打菜(饭从网卡上来)
            //这里的receive会阻塞等待,等到客户端那边发送数据过来
            socket.receive(requestPacket);
            //为了方便处理这个请求,需要把数据报转换成String
            String request = new String(requestPacket.getData(), 0, requestPacket.getLength());
            //2.根据请求计算响应(此处省略这个步骤)
            String response = process(request);
            //3.把响应结果写回到客户端
            // 根据response 字符串,构造一个DatagramPacket
            // 和请求packet 不同,此处构造响应的时候,需要指定这个包要发给谁,这里调用requestPacket.getSocketAddress()就可以得知了
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);
            //打印一下请求的地址和请求的端口号,以及请求的内容和响应的内容
            System.out.printf("[%s:%d] req: %s, resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);
        }
    }


    //这个方法是希望我们根据请求计算响应。
    //由于咱们写的是个回显程序,请求是啥,响应就是啥
    //如果后续写一个别的服务器,不再回显了,而是具有具体的业务了,就可以修改process方法
    //根据需求来重新构造响应
    //之所以单独列成一个方法,就是想让大家知道这个是一个关键的环节。
    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        UdpEchoServer udpEchoServer = new UdpEchoServer(9090);
        udpEchoServer.start();
    }
}

客户端代码:

package network;
//客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;//服务器的地址
    private int serverPort;//服务器的端口

    //客户端启动,需要知道服务器在哪里
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //对于客户端来说,不需要显示关联空闲的端口
        //不代表没有端口,而是系统自动分配了一个空闲的端口
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        //通过这个客户端可以多次和服务器进行交互
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1.先从控制台,读取一个字符串过来
            //先打印一个提示符,提示用户要输入内容
            System.out.println("->");
            String request = scanner.next();
            //2.把字符串构造成UDP packet,并进行发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            socket.send(requestPacket);
            //3.客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            //4.把响应数据转换成String显示出来
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("req: %s, resp: %s\n", request, response);
        }
    }

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


启动服务器和客户端结果展示:

 

注意:这里一定是先启动服务器,再启动客户端!!!

执行流程如下所示:

 

注意:在上述过程中我们是客户端和服务器在同一个主机上,使用的是127这个IP,不同主机则就写实际的IP即可。

在上述通信过程中,站在客户端发送数据的角度:

  • 源IP是:127.0.0.1
  • 源端口是:64982,他是系统自动分配的空闲端口。
  • 目的IP是:127.0.0.1
  • 目的端口是:9090

在上述过程中就有同学好奇了不是说是要使用close来关闭资源的吗?为什么在代码中好像没有看到释放资源这一步,其实对于UdpEchoServer来说,这个socket对象是出了循环就不用了,但是循环结束,意味着start结束,意味着main方法结束,同时意味着进程结束,那么此时进程都结束了所以的资源也就自然释放了,所以就不必显示释放资源了。

3.4简单的翻译服务器

在上述中我们编写的是一个回显服务器,它是没有实际意义的。那么如何写一个提供实在价值的服务器呢?当响应和请求不一样了,响应是根据不同的请求计算得到的,这里就需要我们对上述过程没有写的process方法来进行编写,那么下来我们就具体来实现一下。我们就来编写一个简单的英文单词翻译服务器,请求是一个英文单词,响应是这个单词的中文翻译。

服务端代码展示:

package network;
//词典查询服务端
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

//使用继承,是为了复用之前的代码
public class UdpDicServer extends UdpEchoServer{
    private Map<String, String> dict = new HashMap<>();
    public UdpDicServer(int port) throws SocketException {
        super(port);
        dict.put("dog", "小狗");
        dict.put("cat", "小猫");
        dict.put("tiger", "老虎");
        //注意:这里可以无限添加很多个数据
    }
    @Override
    public String process(String request) {
        return dict.getOrDefault(request,"该单词没有查到!");
    }

    public static void main(String[] args) throws IOException {
        UdpDicServer udpDicServer = new UdpDicServer(9090);
        udpDicServer.start();
    }
}


客户端代码展示:

package network;
//客户端
import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;//服务器的地址
    private int serverPort;//服务器的端口

    //客户端启动,需要知道服务器在哪里
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //对于客户端来说,不需要显示关联空闲的端口
        //不代表没有端口,而是系统自动分配了一个空闲的端口
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        //通过这个客户端可以多次和服务器进行交互
        Scanner scanner = new Scanner(System.in);
        while (true) {
            //1.先从控制台,读取一个字符串过来
            //先打印一个提示符,提示用户要输入内容
            System.out.println("->");
            String request = scanner.next();
            //2.把字符串构造成UDP packet,并进行发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(), request.getBytes().length,
                    InetAddress.getByName(serverIP), serverPort);
            socket.send(requestPacket);
            //3.客户端尝试读取服务器返回的响应
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(responsePacket);
            //4.把响应数据转换成String显示出来
            String response = new String(responsePacket.getData(), 0, responsePacket.getLength());
            System.out.printf("req: %s, resp: %s\n", request, response);
        }
    }

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


运行结果展示:

 

注意:在上述编写服务端代码时我们是直接使用了继承,重写了父类的process方法。这样就减少了我们的工作。

4.TCP流套接字

在TCP中有两个核心的类:

  • ServerSocket:是给服务器使用的。
  • Socket:即会给客户端使用,又会给服务器端使用。

下面我们就来分别看看ServerSocket和Socket的具体使用方法。

4.1ServerSocket API

他是创建服务端使用的API。

SocketSocket构造方法:

方法签名方法说明
ServerSocket(int port)创建一个服务流套接字Socket,并绑定到指定端口

SocketSocket方法: 

方法签名方法说明
Socket accept()开始监听指定端口(创建时绑定的端口),有客户端连接后,返回一个服务端Socket对象,并基于该Socket建立与客户端的连接,否则阻塞等待。
void close()关闭此套接字

这里的accept意思就是接收,在客户端主动向服务器发起连接请求,服务器就得同意一下,但是实际上的这个accept又和我们上述给大家解释的意思不太一样,这里的accept只是在应用层面的接收,实际的TCP连接的接受是在该内核里已经完成了。这个后面在将TCP的时候会给大家交代的。 

4.2Socket API

Socket是客户端Socket,或服务端中接收到客户端建立连接(accept方法)的请求后,返回的服务端Socket。

不管是客户端还是服务端Socket,都是双方建立连接以后,保存的对端的信息,及用来与对方收发数据的。

Socket的构造方法:

方法签名方法说明
Socket(String host, int port)创建一个客户端流套接字Socket,并与对应IP的主机上,对应端口的进程建立连接。

这里的host和port指的是服务器的IP和端口,TCP是有连接的,在客户端new Socket对象的时候就会尝试和指定IP端口的目标建立连接了。 

Socket的方法:

方法签名方法说明
InetAddress getInetAddress()返回套接字所连接的地址
InputStream getInputStream()返回此套接字的输入流
OutPutStream getOutStream()返回此套接字的输出流

 InputStream getInputStream()和OutPutStream getOutStream()是字节流,就可以通过上述字节流对象,进行数据传输了。

  • 从 InputStream 这里读数据,就相当于是从网卡接收。
  • 往 OutPutStream 这里写数据,就相当于从网卡发送。

注意:

这个Socket和DatagramSocket定位类似,都是构造的时候指定一个具体的端口,让服务器绑定该端口,但是ServerSocket一定要绑定具体的端口。

4.3基于TCP的回显程序

服务端代码展示 :

package network;
//服务端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TcpEchoServer {
    //serverSocket只有一个,clientSocket会给每一个客户都分配一个
    private ServerSocket severSocket = null;

    public TcpEchoServer(int port) throws IOException {
        severSocket = new ServerSocket(port);
    }

    public void start() throws IOException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        System.out.println("服务器启动成功!");
        while (true) {
            Socket clientSocket = severSocket.accept();
            //如果直接调用,该方法会影响这个循环的二次执行,导致accept不及时了。
            //创建新的线程,用新线程来调用processConnection
            //每次来一个新的客户端都搞一个新的线程即可!
//            Thread t = new Thread(() -> {
//                try {
//                    processConnection(clientSocket);
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
//            });
//            t.start();

            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        processConnection(clientSocket);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    //读取这个方法来处理一个连接
    //读取请求
    //根据请求计算响应
    //把响应返回给客户端
    private void processConnection(Socket clientSocket) throws IOException {
        System.out.printf("[%s:%d] 客户端上线!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
        //使用try()这种写法,()中允许写多个流对象,使用;来分隔
        try(InputStream inputStream = clientSocket.getInputStream();
            OutputStream outputStream = clientSocket.getOutputStream()) {
            //没有这个scanner和printWriter,完全可以,但是代价就是得一个字节一个字节扣,找到哪个是请求结束的标记\n
            //不是不能做,而是代替代码比较麻烦
            //为了简单,把字节流包装成了更方便的字符流
            Scanner scanner = new Scanner(inputStream);
            PrintWriter printWriter = new PrintWriter(outputStream);
            while (true) {
                //1.读取请求
                //采用hasNext判定接下来还有没有数据了,如果对端关闭了连接(客户端关闭连接),此时hasNext就会返回false,循环就结束
                if (!scanner.hasNext()) {
                    //读取的流到了结尾了(对端关闭了)
                    System.out.printf("[%s:%d] 客户端下线了!\n", clientSocket.getInetAddress().toString(), clientSocket.getPort());
                    break;
                }
                //直接使用scanner读取一段字符串
                //next会一直往后读,读到空白符结束(空格、换行、制表符、翻页符...都算空白符)
                //nextLine只是读到换行符结束,所以这里没有使用它
                String request = scanner.next();
                //2.根据请求计算响应
                String response = process(request);
                //3.把响应写回给客户端,不要忘记了,响应也是要带上换行的
                //返回响应的时候要把换行符加回来,方便客户端那边来区分从哪里到哪里是一个完整的响应。
                printWriter.println(response);
                //flush当数据不够大的时候直接进行强制刷新,将缓冲区中的数据发给客户端
                printWriter.flush();
                System.out.printf("[%s:%d] req: %s; resp: %s\n", clientSocket.getInetAddress().toString(), clientSocket.getPort(), request, response);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            //clientSocket只是一个连接提供服务的,这个还是要进行关闭的
            clientSocket.close();
        }
    }

    private String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        TcpEchoServer tcpEchoServer = new TcpEchoServer(9090);
        tcpEchoServer.start();
    }
}


客户端代码展示:

package network;
//客户端
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;

public class TcpEchoClient {
    private Socket socket = null;

    public TcpEchoClient(String serverIP, int port) throws IOException {
        //这个操作相当于让客户端和服务器建立TCP连接
        //这里的连接连上了,服务器的accept就会返回
        socket = new Socket(serverIP, port);
    }

    public void start() throws IOException {
        Scanner scanner = new Scanner(System.in);
        try (InputStream inputStream = socket.getInputStream();
             OutputStream outputStream = socket.getOutputStream()) {
            PrintWriter printWriter = new PrintWriter(outputStream);
            Scanner scannerFromSocket = new Scanner(inputStream);
            while (true) {
                //1.从键盘上读取用户输入的内容
                System.out.println("->");
                String request = scanner.next();
                //2.把读取到的内容构造成请求,发送给服务器
                //注意,这里的发送,是带有换行的。
                printWriter.println(request);
                printWriter.flush();
                //3.从服务器读取响应的内容
                String response = scannerFromSocket.next();
                //4.把响应结果显示到控制台上
                System.out.printf("req: %s; resp: %s\n", request, response);
            }
        }
    }

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

结果展示:

执行流程如下所示:

 

那么这里我们只是启动了一个客户端,在实际中不可能是一个服务器只给一个客户端进行服务,那么如何启动多个客户端呢?这里在idea中是默认下只能启动一个的,那么这里我们需要打开idea配置一下。配置过程如下所示:

 

 

此时当我们再次点击上述的三角形就可以再次启动另一个客户端了。

 

 

5.再谈协议

回顾并理解我们为什需要协议

以上我们实现的UDP和TCP数据传输,除了UDP和TCP之外,程序还存在应用层定义协议,可以想想分别都是什么样的协议格式。

对于客户端及服务端应用程序来说,请求和响应,需要约定一致的数据格式:

  • 客户端发送请求和服务端解析请求和要使用相同的数据格式。
  • 服务端返回响应和客户端解析响应也要使用相同的数据格式。
  • 请求格式和响应格式可以相同,也可以不同。
  • 约定相同的数据格式,主要目的是为了让接收端在解析的时候明确如何解析数据中的各个字段。
  • 可以使用知名协议(广泛使用的协议格式),如果想自己约定数据格式,就属于自定义协议。

结束语:

这节中小编主要是和大家分享了网络编程中的两个重要的编程UDP和TCP,后期小编还会继续出有关于网络方面的知识的,希望这节对大家了解网络有一定帮助,想要学习的同学记得关注小编和小编一起学习吧!如果文章中有任何错误也欢迎各位大佬及时为小编指点迷津(在此小编先谢过各位大佬啦!)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

VR全景乡村旅游浇灭乡愁,近距离体验自然之美

说起乡愁&#xff0c;可能每位漂泊的游子都有所感受&#xff0c;在外漂泊数十载&#xff0c;每到佳节倍思亲&#xff0c;家乡的一草一木都浮现在脑海中&#xff0c;满载着儿时的回忆。为了留住那抹儿时回忆&#xff0c;VR全景助力数字化乡村建设。 乡村振兴是国家的重大战略&am…

内网横向移动—ARP攻击图片捕捉数据劫持DNS劫持

内网横向移动—ARP攻击&图片捕捉&数据劫持&DNS劫持 1. ARP1.1. APR介绍1.1.1. ARP工作原理1.1.2. APR欺骗工作原理 1.2. 环境准备1.3. 适用场景 2. ARP断网攻击演示2.1. 使用kali进行演示2.1.1. nmap判断存活2.1.2. 安装工具2.1.3. 攻击Windows 10虚拟机2.1.3.1. 查…

Ubuntu常用压缩指令总结

一、tar tar是Linux系统中最常用的压缩工具之一&#xff0c;它的一个优点是它可以保留文件的权限和所有权信息。tar可以创建.tar文件&#xff08;通常称为"tarball"&#xff09;&#xff0c;或者与gzip或bzip2等工具结合使用来创建.tar.gz或.tar.bz2文件。gzip工具的…

CSS:盒子模型 与 多种横向布局方法

目录 盒子模型块级盒子内联级盒子内联块级盒子弹性盒子display 改变模型区域划分text 内容区padding 填充区border 边框区margin 外边距直接设置盒子大小 布局横向布局方法一 float 浮起来方法二 内联块级元素实现方法三 弹性盒子模型 盒子模型 块级盒子 独占一行&#xff0c…

轻松转换TS视频为MP4,实现优质视频剪辑体验

如果你是一个视频剪辑爱好者&#xff0c;你一定会遇到各种视频格式之间的转换问题&#xff0c;特别是将TS视频转换为MP4格式。别担心&#xff0c;我们的视频剪辑软件将为你提供最简单、高效的解决方案&#xff01; 首先第一步&#xff0c;我们要进入媒体梦工厂主页面&#xff…

如何使用webpack打包一个库library,使用webpack打包sdk.

如何使用webpack打包一个库library 如果你需要自己封装一些包给别人使用,那么可以参考以下方法 初始化库 mkdir library cd library npm init -y经过以上步骤后会生成一个library文件夹&#xff0c;里面包含一个package.json文件。然后简单修改为如下所示&#xff1a; {&qu…

idea中提示Unsupported characters for the charset ‘ISO-8859-1‘

application.properties中文注释拉黄线 &#xff0c;提示Unsupported characters for the charset ISO-8859-1 解决办法&#xff1a; 注意&#xff1a; 改完之后之前输入的中文就变成“ &#xff1f;&#xff1f;&#xff1f;”了&#xff0c;建议备份一下 1、打开setti…

Unity C# 之 Http 获取网页的 html 数据,并去掉 html 格式等相关信息

Unity C# 之 Http 获取网页的 html 数据&#xff0c;并去掉 html 格式等相关信息 目录 Unity C# 之 Http 获取网页的 html 数据&#xff0c;并去掉 html 格式等相关信息 一、简单介绍 二、实现原理 三、注意事项 四、效果预览 五、关键代码 一、简单介绍 Unity中的一些知…

解决GitHub的速度很慢的几种方式

1. GitHub 镜像访问 这里提供两个最常用的镜像地址&#xff1a; https://hub.njuu.cf/search https://www.gitclone.com/gogs/search/clonesearch 也就是说上面的镜像就是一个克隆版的 GitHub&#xff0c;你可以访问上面的镜像网站&#xff0c;网站的内容跟 GitHub 是完整同步…

【变形金刚03】使用 Pytorch 开始构建transformer

一、说明 在本教程中&#xff0c;我们将使用 PyTorch 从头开始构建一个基本的转换器模型。Vaswani等人在论文“注意力是你所需要的一切”中引入的Transformer模型是一种深度学习架构&#xff0c;专为序列到序列任务而设计&#xff0c;例如机器翻译和文本摘要。它基于自我注意机…

【Quarkus技术系列】打造基于Quarkus的云原生微服务框架实践(1)

前提介绍 本系列文章主要讲解如何基于Quarkus技术搭建和开发"专为Kubernetes而优化的Java微服务框架"的入门和实践&#xff0c;你将会学习到如何搭建Quarkus微服务脚环境及脚手架&#xff0c;开发Quarkus的端点服务&#xff0c;系统和应用层级的配置介绍与Quarkus的…

一文读懂c++语言

一文读懂C语言 C的发展C的设计目标C的特性C的挑战 C的发展 C是一种通用的、高级的编程语言&#xff0c;它是C语言的扩展。C由Bjarne Stroustrup于1983年首次引入&#xff0c;并在之后的几十年中不断发展壮大。C被广泛应用于各种领域&#xff0c;包括系统开发、游戏开发、嵌入式…

概率图模型(Probabilistic Graphical Model,PGM)

概率图模型&#xff08;Probabilistic Graphical Model&#xff0c;PGM&#xff09;&#xff0c;是一种用图结构来描述多元随机变量之间条件独立性的概率模型。它可以用来表示复杂的概率分布&#xff0c;进行有效的推理和学习&#xff0c;以及解决各种实际问题&#xff0c;如图…

传输控制协议TCP

目录 TCP报文格式 TCP的特点 TCP原理: 1.确认应答机制 2.超时重传机制 3.连接管理机制 建立连接 ​编辑关闭连接 4.滑动窗口机制 ​5.流量控制 6.拥塞控制 7.延迟应答 8.捎带应答 TCP报文格式 1.源端口号:发送端的哪一个端口发出的 2.目的端口号:接收端的哪一个端…

Jupyter Notebook 500 : Internal Server Error

1. 这个问题的根本原因在于&#xff1a; pygments 包 版本过高。 安装pygments 2.6.1 2.jupyter版本如下 如果某个版本有冲突&#xff0c;卸载了重新安装一下就行。 安装命令&#xff1a; pip install pygments 2.6.1 -i https://pypi.tuna.tsinghua.edu.cn/simple 另外…

NanoPi NEO移植LVGL8.3.5到1.69寸ST7789V屏幕

移植前准备 移植好fbtft屏幕驱动 参考链接&#xff1a;友善之臂NanoPi NEO利用fbtft驱动点亮1.69寸ST7789V2屏幕 获取源码 名称地址描述lvglhttps://github.com/lvgl/lvgl.gitlvgl-8.3.5lv_drivershttps://github.com/lvgl/lv_drivers.gitlv_drivers-6.1.1 创建工程目录 创…

UGUI组件EventTrigger用法

一.Unity编辑器中EventTrigger组件用法 1.添加事件类型 2.绑定gameObject指定组件的方法 3.方法执行逻辑 public class NewBehaviourScript : MonoBehaviour {public void PointerDown(){Debug.Log("Trigger PointerDown");} } 4.按下鼠标&#xff0c;绑定方法成功…

Spring Boot 统一功能处理(拦截器实现用户登录权限的统一校验、统一异常返回、统一数据格式返回)

目录 1. 用户登录权限校验 1.1 最初用户登录权限效验 1.2 Spring AOP 用户统⼀登录验证 1.3 Spring 拦截器 &#xff08;1&#xff09;创建自定义拦截器 &#xff08;2&#xff09;将自定义拦截器添加到系统配置中&#xff0c;并设置拦截的规则 1.4 练习&#xff1a;登录…

idea如何上传项目到github(超详细)

idea如何上传项目到github 1、IDEA配置2、项目上传到本地仓库2.1、创建本地git仓库2.2、Add操作2.3、Commit操作 3、项目上传到Github4、拿到登录Github的token 1、IDEA配置 File-Settings-VersionControl-Git Git的安装路径下bin目录下的git.exe可执行文件 可以直接点 Gene…

UVA-1601 万圣节后的早晨 题解答案代码 算法竞赛入门经典第二版

GitHub - jzplp/aoapc-UVA-Answer: 算法竞赛入门经典 例题和习题答案 刘汝佳 第二版 以三个点的当前位置作为状态&#xff0c;广度优先遍历&#xff0c;找到终点即为最短次数。 注意&#xff1a; 一次可以移动多个点&#xff0c;但是每个点只能移动一步。在同一次中&#xf…