网络编程套接字之二【UDP】

导读:本篇文章讲解 网络编程套接字之二【UDP】,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

目录

1. TCP 和 UDP 的区别

2. DatagramSocket

3. DatagramPacket

4. 写回显—服务器

5. 回显服务器代码 

6. 写回显—客户端 

7. 回显—客户端代码

8. 回显服务器客户端的,执行流程步骤 

9. 运行服务器 再运行客户端

10. 写翻译—服务器


网络编程套接字,就是研究如何写代码完成网络编程

套接字(socket):是操作系统给应用程序提供的API,来让应用层和传输层进行交互,实际上API也就是传输层给应用层提供的

网络传输层中,有很多种协议,其中最常见的就是 TCP 和 UDP,这两种协议的特性差别是比较大的,所以操作系统就提供了两种不同风格的API

1. TCP 和 UDP 的区别

TCP UDP 解释
有连接 无连接

打电话是有连接,这是通信双方建立好连接,才能通信(交互数据)

A打电话给B,B接了,才能说话

发短信/ 发微信 是无连接

直接一发就过去了

可靠传输 不可靠传输

首先明确,可靠传输不是指A发给B的数据,B100%能收到

而是指 A能够知道B是不是收到了,这才是可靠传输

面向字节流 面向数据报

面向字节流:TCP和文件操作一样,也是属于 流 的

面向数据报:这个“数据报”为基本单位

全双工 全双工

全双工:一个通道,双向通信

和全双工相对的就是 “半双工”

半双工:一个通道,单向通信

UDP 的 socket

客户端 – 服务器

2. DatagramSocket

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

构造方法

方法签名 说明
DatagramSocket() 创建一个UDP数据包套接字的Socket,绑定到本机任意的一个随机接口(一般用于客户端
DatagramSocket 创建一个UDP数据报套接字的Socket,绑定到本机指定的端口(一般用于服务端

方法

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

3. DatagramPacket

DatagramPacket 代表了一个UDP数据报,也就是一次 发送 / 接收的基本单位 

构造方法

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

方法

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

4. 写回显—服务器

服务器  UdpEchoServer

1.先写DatagramSocket对象

    private DatagramSocket socket = null;

2.然后再写Udp回显服务器的构造方法

(参数的端口表示我们自己的服务器要绑定的端口)

    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

3.写一个启动服务器的方法start(),在start中写上while循环来执行具体逻辑

    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true) {

        }
     }

   a)读取请求并解析

            DatagramPacket requestPacket = new DatagramPacket(new byte[4096],4096);
            socket.receive(requestPacket);
            // 把这个DatagramPacket 对象转为字符串,方便去打印
            // 里面的参数对应的是 取出字节数组,对应的范围
            String request = new 
            String(requestPacket.getData(),0,requestPacket.getLength());

   b)根据请求计算响应(这里写一个process方法)

            String response = process(request);
 // 当前写的是回显服务器,响应数据和请求是一样的
    public String process(String request) {
        return request;
    }

   c)把响应写回到客户端

            // 以这个对象为单位进行发送。这就是面向数据报,发送和接收都是以这个DatagramPacket为基本单位
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,
                    requestPacket.getSocketAddress());
            //getSockAddress() 此时只是有数据了,还需要知道数据发给谁,要写上客户端的地址的端口
            //getBytes() 得到字符串里面包含的字节数组

            socket.send(responsePacket);

   d)打印一个日志,记录当前的情况

            System.out.printf("[%s:%d] req: %s; resp: %s\n", requestPacket.getAddress().toString(),
                    requestPacket.getPort(), request, response);

4.写一个mian方法来执行Udp回显服务器

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

 在进行 读取请求并解析  和  把响应写回到客户端 时都分别创建了新的DatagramPacket对象

里面传的值有什么区别?

网络编程套接字之二【UDP】

 

 在进行 把响应写回到客户端 时,如何确定发给谁的问题?

在给DataGramPacket对象传参时,传入客户端地址和端口(用getSocketAddress方法)

网络编程套接字之二【UDP】

5. 回显服务器代码 

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 28463
 * Date: 2022—10—12
 * Time: 15:17
 */
public class UdpEchoServer {
    private DatagramSocket socket = null;

    //参数的端口表示我们自己的服务器要绑定的端口
    public UdpEchoServer(int port) throws SocketException {
        socket = new DatagramSocket(port);
    }

    //通过这个服务器启动服务器
    public void start() throws IOException {
        System.out.println("服务器启动!");
        while(true) {
            //循环里面处理一次请求
            //1.读取请求并解析
            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.把响应写回到客户端
            // 以这个对象为单位进行发送。这就是面向数据报,发送和接收都是以这个DatagramPacket为基本单位
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),
                    response.getBytes().length,
                    requestPacket.getSocketAddress());
            //getSockAddress() 此时只是有数据了,还需要知道数据发给谁,要写上客户端的地址的端口
            //getBytes() 得到字符串里面包含的字节数组

            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();
    }

}

6. 写回显—客户端 

UdpEchoClient

1. 写一个DatagramSocket对象

    DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

2. 再写回显客户端的构造方法

    // 两个参数一会会在发送数据的时候用到,暂时先将这两个参数存起来,以备后用
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //这里不是说没有端口,而是让系统自动指定一个空闲的端口
        socket = new DatagramSocket();
        // 假设serverIP 是形如 1.2.3.4 这种点分十进制的表示方式
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

3. .写一个启动客户端的方法start(),在start中写上while循环来执行具体逻辑

    public void start() throws IOException {
        Scanner scan = new Scanner(System.in);
        while(true) {

        }
    }

   a)从控制台读取用户输入的内容

            System.out.println("-> ");
            String request = scan.next();

   b)构造一个UDP请求,发送给服务器

            DatagramPacket requestpacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length,
                    InetAddress.getByName(this.serverIP),
                    this.serverPort);
            //把数据报通过网络发送出去
            socket.send(requestpacket);

   c)从服务器读取UDP响应数据,并解析

            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            //用receive针对空的responsePacket进行填充
            socket.receive(responsePacket);
            String response = new 
            String(responsePacket.getData(),0,responsePacket.getLength());

   d)把服务器的响应提示到控制台上

            System.out.println(response);

4. 最后再写个main方法,进行回显客户端的执行

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

给服务器发送请求,给DatagramPacket对象传入IP和端口来描述发送给谁 

网络编程套接字之二【UDP】 

在写客户端构造的时候,不用写客户端IP

网络编程套接字之二【UDP】

 再看一个DatagramPacket对象的传值,注意里面传的是字节网络编程套接字之二【UDP】

客户端给服务器发送一个数据:

客户端自己的主机IP:源IP

客户端没有手动的绑定一个端口,操作系统就会自动分配一个空闲的端口

服务器的主机IP:目的IP

服务器绑定的端口:目的端口

服务器需要手动指定指定端口,是为了方便客户端找到服务器在哪。

客户端不需要手动指定端口,是因为操作系统会自动分配一个空闲的端口,如果你自己手动指定一个端口,可能这个端口别的程序正在使用,就会导致程序无法正确运行了

总之,服务器是运行在程序员自己的服务器上,这个主机是可控的,而客户端是运行在用户自己的电脑上,是不可控的

7. 回显—客户端代码

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

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 28463
 * Date: 2022—10—12
 * Time: 15:16
 */
public class UdpEchoClient {
    DatagramSocket socket = null;
    private String serverIP;
    private int serverPort;

    // 两个参数一会会在发送数据的时候用到,暂时先将这两个参数存起来,以备后用
    public UdpEchoClient(String serverIP, int serverPort) throws SocketException {
        //这里不是说没有端口,而是让系统自动指定一个空闲的端口
        socket = new DatagramSocket();
        // 假设serverIP 是形如 1.2.3.4 这种点分十进制的表示方式
        this.serverIP = serverIP;
        this.serverPort = serverPort;
    }

    public void start() throws IOException {
        Scanner scan = new Scanner(System.in);
        while(true) {
            // 1.从控制台读取用户输入的内容
            System.out.println("-> ");
            String request = scan.next();

            // 2.构造一个 UDP 请求,发送给服务器
            DatagramPacket requestpacket = new DatagramPacket(request.getBytes(),
                    request.getBytes().length,
                    InetAddress.getByName(this.serverIP),
                    this.serverPort);
            //把数据报通过网络发送出去
            socket.send(requestpacket);
            
            // 3.从服务器读取 UDP 响应数据,并解析
            DatagramPacket responsePacket = new DatagramPacket(new byte[4096],4096);
            //用receive针对空的responsePacket进行填充
            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);
        client.start();
    }
}

8. 回显服务器客户端的,执行流程步骤 

网络编程套接字之二【UDP】

网络编程套接字之二【UDP】

9. 运行服务器 再运行客户端

网络编程套接字之二【UDP】网络编程套接字之二【UDP】

 网络编程套接字之二【UDP】

也可以在idea中设置,允许运行多个客户端

网络编程套接字之二【UDP】

 网络编程套接字之二【UDP】

 网络编程套接字之二【UDP】

10. 写翻译—服务器

英译汉;

请求输入 hello,响应返回一个 你好

请求输入 car,响应返回一个 汽车

import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: 28463
 * Date: 2022—10—14
 * Time: 16:08
 */
public class UdpTranslateServer extends UdpEchoServer{
    //翻译本质上是 key -> value
    private Map<String, String> dict = new HashMap<>();

    public UdpTranslateServer(int port) throws SocketException {
        super(port);

        dict.put("hello", "你好");
        dict.put("car", "汽车");
        dict.put("dog", "小狗");
        dict.put("cat", "小猫");
        //在这里就可以输入很多的内容。
        //现在的翻译程序基本都是这样,只不过用一个很大的哈希表,包含了很多单词和翻译
    }

    //重写 process 方法,实现查询哈希表的操作
    @Override
    public String process(String request) {
        return dict.getOrDefault(request,"词在词典中未找到!");
    }

    //start 和父类完全一样
    public static void main(String[] args) throws IOException {
        UdpTranslateServer server = new UdpTranslateServer(9090);
        server.start();
    }
}

 启动服务器,在客户端输入看看(这里用前面写的回显—客户端就可以)

网络编程套接字之二【UDP】

 一个服务器程序的基本流程,都是和上面一样的

但是最核心的区别就是“根据请求计算响应”,这个取决于服务器的业务逻辑

qq服务器,根据请求计算响应:根据用户发送的消息,返回一个“发送成功还是失败”

淘宝服务器,根据请求计算响应:支付一个订单,根据你的订单信息,完成支付操作

(扣款,修改订单状态)返回支付是否成功等待状态

百度服务器,根据请求计算响应:根据用户输入的查询词,找到匹配的网页

抖音服务器,根据请求计算响应:根据用户的身份特征(用户会话),返回可能感兴趣的视频

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/91221.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!