[14]从零开始的JAVAEE-UDP

目录

一、套接字(Socket)

二、协议特点

1)连接

2)可靠传输

3)面向数据报/字节流

4)全双工/半双工

三、UDP

1)核心的两个类

DatagramSocket():

构造器

方法

DatagramPacket():

构造器

方法

四、实例:回显服务器

服务端:

1.构造Socket(定义端口)

2.读取请求

3.根据请求计算响应

4.返回请求

总:

客户端:

1.构造socket对象(需要知道服务器的IP和端口,自己则不需要指定端口)

2.从控制台读取字符串

3.把字符串构造成UDP数据报并发送

4.读取从服务器返回的数据

5.打印返回的数据

总:

五、UDP报文的格式


一、套接字(Socket)

我们在编写网络程序时,主要操作编写的是应用层代码,真正要发这个数据,就需要上层协议调用下层协议,所以,应用层要调用的,就是传输层的协议

传输层会给应用层提供一组API,这组API就称为Socket api

Socket api主要分为两组,分别是

  • 基于UDP的api

UDP的特点:

无连接

不可靠传输

面向数据报

全双工

  • 基于TCP的api

TCP的特点:

有连接

可靠传输

面向字节流

全双工


二、协议特点

1)连接

连接,指的是通信时是否要记录双方的相关信息

例如发短信就是一个无连接的例子,发短信时不需要建立连接,只要把信息投递出去即可。

而打电话就是一个有连接的例子,打电话首先要双方建立连接,然后才能进行通信。

2)可靠传输

可靠传输指的是是否能感知信息传递是否成功

  • 不可靠传输:信息发送出去即可,不关注是否发送成功,不关注对方是否接收到数据
  • 可靠传输:信息不一定100%发送成功,但如果发送失败,自身可以感知到

3)面向数据报/字节流

  • 面向数据报:以一个数据报为单位进行传输
  • 面向字节流:以字节为基本单位进行传输,更为灵活

4)全双工/半双工

  • 半双工:单向通信,A向B通信时,B只能等待,无法向A传输信息
  • 全双工:一条路径,双向通信:A向B通信时,B也可以向A通信

三、UDP

1)核心的两个类

DatagramSocket():

这个类主要表示一个Socket对象

socket对象:Socket对象就相当于系统中一个特殊的文件,这个文件并非对应到硬盘的某个数据存储区域,而是对应到网卡这个硬件设备,简而言之,直接操作网卡并不方便,所以就引入了一个Socket文件,借助这个文件来对网卡进行操作。

 这些是DatagramSocket的常用构造器和内部方法


构造器

DatagramSocket提供了两个构造器,一个是无参版本,另一个是有参版本,有参版本中的参数是一个端口号,意为在构造Socket对象时给他指定一个端口号。

一个Socket对象可能被服务器/客户端都使用,而服务器的socket往往需要关联一个具体的端口号,客户端则不需要,让系统自动分配一个端口号即可

例如:你在学校里开了一个餐馆,你需要做宣传来吸引同学们来餐馆就餐,就需要明确指出你的餐馆在学校的哪个位置,此时你的餐馆就相当于一台服务器,端口号就相当于你的地址,同学们就相当于客户端,同学们根据你提供的地址找到你的餐馆,就相当于客户端根据端口号连接到服务器。

而同学们来餐馆点餐后,只需要就近找到一个没有人的位置坐下等餐即可,而下一次来你这里吃饭,就不需要坐到原来的位置上,所以说,客户端是不需要指定端口的,而服务器需要指定端口 


方法

如图所示,非常直观


DatagramPacket():

这个类主要表示一个UDP数据报


构造器
  • 第一个构造器,一般用于接收数据,不需要设置地址。
  • 第二个构造器,一般用于发送数据,需要手动设置地址。

方法

如图所示,非常直观


四、实例:回显服务器

回显服务器:一个最简单的客户端-服务器程序,客户端向服务器发送一个请求,服务器返回一个一模一样的请求。

构造一个回显服务器一共有以下几个步骤

服务端:

1.构造Socket(定义端口)

2.读取请求

3.根据请求计算响应

4.返回请求

服务端:

1.构造Socket(定义端口)

public class UdpEchoServer {
    //构造一个空的socket对象
    private DatagramSocket socket = null;

    //构造器:因为是服务端,需要指定分配一个端口号
    public UdpEchoServer(int port)throws SocketException {
        socket = new DatagramSocket(port);
    }


}

2.读取请求

public class UdpEchoServer {
    //构造一个空的socket对象
    private DatagramSocket socket = null;

    //构造器:因为是服务端,需要指定分配一个端口号
    public UdpEchoServer(int port)throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        while (true){
            //每次循环要做的三件事
            System.out.println("服务器,启动!");
            //1.接收收据
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);//这是一个输出型函数


}

这里是receive方法是一个输出型方法,大概意思就是,你需要传入一个空的数据类型进去,然后方法内部会填充这个数据类型。例如你拿着空饭盒去食堂打菜,食堂大妈会返回给你一个装满菜的饭盒。

3.根据请求计算响应

public class UdpEchoServer {
    //构造一个空的socket对象
    private DatagramSocket socket = null;

    //构造器:因为是服务端,需要指定分配一个端口号
    public UdpEchoServer(int port)throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        while (true){
            //每次循环要做的三件事
            System.out.println("服务器,启动!");
            //1.接收收据
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);//这是一个输出型函数

            //2.处理数据
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            String response = process(request);
            //将得到的响应在构造成一个数据报
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());//这里得到的地址是客户端的IP和端口,因为requestPacket是从客户端发来的数据

        }
    }

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


}

注意:这里是把requestPacket包装成了一个String类型的数据,这一步不是必须的。requestPacket是一段二进制数据,把他包装成String类型的数据在进行处理,会更加的方便,但实际上直接对二进制数据进行处理也是完全没问题的。对String类型的数据处理完后,还需要重新转为二进制数据报进行返回,这是因为socket.send接收的数据类型是数据报而不是String。

4.返回请求

package network;

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

public class UdpEchoServer {
    //构造一个空的socket对象
    private DatagramSocket socket = null;

    //构造器:因为是服务端,需要指定分配一个端口号
    public UdpEchoServer(int port)throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        while (true){
            //每次循环要做的三件事
            System.out.println("服务器,启动!");
            //1.接收收据
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);//这是一个输出型函数

            //2.处理数据
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            String response = process(request);
            //将得到的响应在构造成一个数据报
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());//这里得到的地址是客户端的IP和端口,因为requestPacket是从客户端发来的数据

            //3.发送数据
            socket.send(responsePacket);
            System.out.printf("[%s:%d] 请求:%s,发送:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
        }
    }

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


}

总:

package network;

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

public class UdpEchoServer {
    //构造一个空的socket对象
    private DatagramSocket socket = null;

    //构造器:因为是服务端,需要指定分配一个端口号
    public UdpEchoServer(int port)throws SocketException {
        socket = new DatagramSocket(port);
    }

    public void start() throws IOException {
        while (true){
            //每次循环要做的三件事
            System.out.println("服务器,启动!");
            //1.接收收据
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);//这是一个输出型函数

            //2.处理数据
            String request = new String(requestPacket.getData(),0,requestPacket.getLength());
            String response = process(request);
            //将得到的响应在构造成一个数据报
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),response.getBytes().length,requestPacket.getSocketAddress());//这里得到的地址是客户端的IP和端口,因为requestPacket是从客户端发来的数据

            //3.发送数据
            socket.send(responsePacket);
            System.out.printf("[%s:%d] 请求:%s,发送:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
        }
    }

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


}

客户端:

1.构造socket对象(需要知道服务器的IP和端口,自己则不需要指定端口)

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP,int serverPort)throws IOException{
        socket = new DatagramSocket();
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }


        }
    }
}

2.从控制台读取字符串

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP,int serverPort)throws IOException{
        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.print("->:");
            String request = scanner.next();

        }
    }
}

3.把字符串构造成UDP数据报并发送

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP,int serverPort)throws IOException{
        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.print("->:");
            String request = scanner.next();

            //2.构建数据报并发送
            DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIP),serverPort);
            socket.send(requestPacket);


        }
    }
}

4.读取从服务器返回的数据

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP,int serverPort)throws IOException{
        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.print("->:");
            String request = scanner.next();

            //2.构建数据报并发送
            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);//输出型函数
            String response = new String(requestPacket.getData(),0,responsePacket.getLength());

        }
    }
}

5.打印返回的数据

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP,int serverPort)throws IOException{
        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.print("->:");
            String request = scanner.next();

            //2.构建数据报并发送
            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);//输出型函数
            String response = new String(requestPacket.getData(),0,responsePacket.getLength());

            //4.打印数据
            System.out.printf("req:%s,resp:%s\n", request, response);

        }
    }
}

总:

package network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Scanner;

public class UdpEchoClient {
    private DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    public UdpEchoClient(String serverIP,int serverPort)throws IOException{
        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.print("->:");
            String request = scanner.next();

            //2.构建数据报并发送
            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);//输出型函数
            String response = new String(requestPacket.getData(),0,responsePacket.getLength());

            //4.打印数据
            System.out.printf("req:%s,resp:%s\n", request, response);

        }
    }
}

五、UDP报文的格式

端口号:每个端口号在UDP报文里都占两个字节,所以端口号的取值范围是 0->65535 

一般来说,我们可以指定一个客户端-服务端的端口号,但也有例外

知名端口号:端口号小于1024的端口被称为知名端口号,这是给一些比较名气大的服务器预留的端口号,相当于VIP端口,我们平时应该不使用这些端口

  • 源端口:表示数据从哪里来
  • 目的端口:表示数据到哪里去
  • UDP长度:这是UDP的报文,大小一般限制在0-65535之间,也就是最大为64KB。
  • 校验和:网络传输并非非常稳定,网络环境会因为各种各样的因素波动,所以数据通信是很容易受到干扰的,所以校验和存在的意义就在于校验数据的传输是否出错。

校验和并不100%准确。例如:你妈妈给你了一个清单让你去超市买东西,清单上有四样东西:A B C D。你从超市出来以后,看着购物袋中的物品,如果数量为四,那也不一定购买正确,但是如果数量为三或者五,就肯定不正确。

所以说,校验和无法100%检测数据传输是否出错

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不卷啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值