当前位置:首页 » 《休闲阅读》 » 正文

【JavaSE】【网络编程】UDP数据报套接字编程

9 人参与  2024年12月04日 16:01  分类 : 《休闲阅读》  评论

点击全文阅读


目录

一、网络编程简介二、Socket套接字三、TCP/UDP简介3.1 有连接 vs 无连接3.2 可靠传输 vs 不可靠传输3.3 面向字节流 vs 面向数据报3.4 双向工 vs 单行工 四、UDP数据报套接字编程4.1 API介绍4.1.1 DatagramSocket类4.1.1.1 构造方法4.1.1.2 主要方法 4.1.2 DatagramPocket类4.1.2.1 构造方法4.1.2.2 主要方法 4.1.3 使用上述类创建回显服务器-客户端4.1.3.1 回显服务器UdpEchoServer4.1.3.2 回显客户端UdpEchoClient

一、网络编程简介

网络编程:

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

网络编程涉及基本概念:

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

请求和响应 :
一般来说,获取一个网络资源,涉及到两次网络数据传输:
第一次:请求数据的发送。
第二次:响应数据的发送。

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

二、Socket套接字

Socket套接字:Socket套接字,是由系统提供用于网络通信的技术,是基于TCP/IP协议的网络通信的基本操作单元。基于Socket套接字的网络程序开发就是网络编程。

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

流套接字:使用传输层TCP协议。数据报套接字:使用传输层UDP协议。原始套接字:用于自定义传输层协议。

三、TCP/UDP简介

socket api :传输层提供给应用层的API。
传输层的两个核心协议是:TCP与UDP协议。

TCP协议:有连接,可靠传输,面向字节流,全双工的协议。UDP协议:无连接,不可靠传输,面向数据报,全双工的协议。

3.1 有连接 vs 无连接

有/无连接:是一个抽象的概念,虚拟的逻辑上的连接。就像前面的链表的学习一样,物理上不连续,但通过节点之间的关联使其逻辑上连续。

TCP协议中:保存了对端的信息,让两个通信之间的设备有连接。就如AB通信,A保存B信息,B保存A信息。

UDP协议中:UDP本身不保存对端信息,但可以自己使用代码保存。

3.2 可靠传输 vs 不可靠传输

丢包: 网络上数据发生丢失的情况就是丢包。

传输中丢包情况造成原因:

当光/电/电磁信号,受到外界的干扰。网络世界是通过交换机,路由器组建起来的。而交换机,路由器就像十字路口一样,当某个时间点,实际转发的数据超过设备能转发的上限的时候,就会“堵车”,造成丢包。

可靠传输:不是保证数据报100%到达,不造成一点丢失。而是尽可能提高传输成功的概率,如果丢包还会进行记录。

不可靠传输:在发出数据后就不管了。

3.3 面向字节流 vs 面向数据报

面向数据流:读写数据(读–>接收数据,写–> 发送数据)的时候,以字节为单位。支持读写任意长度,有粘包问题。
面向数据报:读写数据(读–>接收数据,写–> 发送数据)的时候,以一个数据报为单位。有长度限制。

3.4 双向工 vs 单行工

全双工:读写数据(读–>接收数据,写–> 发送数据),支持双向通信,能读能写。
单双工:面向数据流:读写数据(读–>接收数据,写–> 发送数据),只支持单向通信,要么读要么写。

四、UDP数据报套接字编程

计算机中的文件通常是一个广义的概念,文件除了指代一些我们常说的文件外,还能指代一些硬件设备。操作系统管理硬件设备也是抽象成文件。

4.1 API介绍

4.1.1 DatagramSocket类

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

4.1.1.1 构造方法
方法签名方法说明
public DatagramSocket() throws SocketException创建⼀个UDP数据报套接字的Socket,绑定到本机任意⼀个随机端⼝(⼀般⽤于客⼾端)
public DatagramSocket(int port) throws SocketException创建⼀个UDP数据报套接字的Socket,绑定到本机指定的端⼝(⼀般⽤于服务端)
public DatagramSocket(int port, InetAddress laddr) throws SocketException创建⼀个UDP数据报套接字的Socket,绑定到指定的IP即laddr和端⼝port
public DatagramSocket(SocketAddress bindaddr) throws SocketException创建⼀个UDP数据报套接字的Socket,绑定到bindaddr指定的IP和端⼝
4.1.1.2 主要方法
方法签名作用
public void send(DatagramPacket p) throws IOException从此套接字发送数据报包(不会阻塞等待,直接发送)
public synchronized void receive(DatagramPacket p) throws IOException从此套接字接收数据报(如果没有接收到数据报,该⽅法会阻塞等待)
public void close()关闭此数据报套接字

4.1.2 DatagramPocket类

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

4.1.2.1 构造方法
方法签名作用
public DatagramPacket(byte buf[ ], int length)构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length)
public DatagramPacket(byte buf[ ], int offset, int length)构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length)从buf的下标offset位置开始接收
public DatagramPacket(byte buf[], int offset, int length, SocketAddress address)构造⼀个DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从offset到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号
public DatagramPacket(byte buf[ ], int offset, int length, InetAddress address, int port)构造⼀DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP,port指定⽬的主机的端⼝号
public DatagramPacket(byte buf[ ], int length, SocketAddress address)构造⼀个DatagramPacket以⽤来发送数据报,发送的数据为字节数组(第⼀个参数buf)中,从0到指定⻓度(第⼆个参数length)。address指定⽬的主机的IP和端⼝号
public DatagramPacket(byte buf[ ], int length, InetAddress address, int port)构造⼀个DatagramPacket以⽤来接收数据报,接收的数据保存在字节数组(第⼀个参数buf)中,接收指定⻓度(第⼆个参数length)address指定⽬的主机的IP,port指定⽬的主机的端⼝号
4.1.2.2 主要方法
方法签名作用
public synchronized byte[ ] getData()获取数据报中的数据
public synchronized int getPort()从接收的数据报中,获取发送端主机的端⼝号;或从发送的数据报中,获取接收端主机端⼝号
public synchronized InetAddress getAddress()从接收的数据报中,获取发送端主机IP地址;或从发送的数据报中,获取接收端主机IP地址

4.1.3 使用上述类创建回显服务器-客户端

回显:请求是啥,相应就是啥。

4.1.3.1 回显服务器UdpEchoServer
先创建一个成员变量:Socket对象,用于接收发送数据报。private DatagramSocket socket = null;构造方法,指定一个固定端口号给服务器使用,将Socket使用这个端口号实例化。
public UdpEchoServer(int port) throws SocketException{        socket = new DatagramSocket(port);    }
接下来在start方法中编写服务器实现逻辑。由于服务器是要7*24小时工作的,所以在一个死循环中实现。读取请求并解析;
4.1 构造一个DatagramPocket对象,初始化为0(不是null),用于保存UDP的载荷部分。
DatagramPacket requestPacket = new DatagramPacket(new byte[2024],2024);
4.2 读取请求socket.receive(requestPacket);
4.3 把读取到有效数据的解析为字符串。
通过getData方法获取DatagramPacket 中的字节数组,getLength方法获取DatagramPacket 有效数据长度,根据字节数组构造字符串。
String request = new String(requestPacket.getData(), 0 ,requestPacket.getLength());根据请求计算响应,由于是回显服务器,直接请求就是响应。
String response = product(request);private String product(String request) {        return request;    }
把响应返回给客户端,通过getBytes方法拿到字符串中的字节数据,getBytes().length拿到字节长度,getSocketAddress方法拿到客户端的端口和IP。
socket.send(new DatagramPacket(request.getBytes(),request.getBytes().length,requestPacket.getSocketAddress()));
打印一个日志记录此次交互过程。
System.out.printf([%s:%d],request:%s,response:%s\n",requestPacket.getAddress().toString(),requestPacket.getPort(),request,response);
总代码
import java.io.IOException;import java.net.DatagramPacket;import java.net.DatagramSocket;import java.net.SocketException;public class UdpEchoServer {    //先创建一个成员变量:Socket对象,用于接收发送数据报    private DatagramSocket socket = null;    //构造方法,指定一个固定端口号给服务器使用,将Socket使用这个端口号实例化    public UdpEchoServer(int port) throws SocketException{        socket = new DatagramSocket(port);    }    public void start() throws IOException {        while (true) {            //构造一个DatagramPocket对象,初始化为0(不是null),用于保存UDP的载荷部分。            DatagramPacket requestPacket = new DatagramPacket(new byte[2024], 2024);            //接收请求            socket.receive(requestPacket);            //把读取到有效数据的解析为字符串            String request = new String(requestPacket.getData(),0,requestPacket.getLength());            //计算响应            String response = product(request);            //把响应返回给客户端            socket.send(new DatagramPacket(request.getBytes(),request.getBytes().length,requestPacket.getSocketAddress()));            //打印一个日志            System.out.printf("[%s : %d], request:%s , response:%s\n",requestPacket.getAddress().toString(),                    requestPacket.getPort(),request,response);        }    }    private String product(String request) {        return request;    }    public static void main(String[] args) throws IOException {        UdpEchoServer server = new UdpEchoServer(6666);        server.start();    }}
4.1.3.2 回显客户端UdpEchoClient
先创建一个成员变量:Socket对象,用于接收发送数据报private DatagramSocket socket = null;UDP 本身不保存对端的信息, 就自己的代码中保存一下.
private String serverIp;    private int port;
构造方法是要指定访问的服务器的地址。
public UdpEchoClient(String serverIp,int port) throws SocketException {        this.serverIp = serverIp;        this.port = port;        socket = new DatagramSocket();    }
从控制台读取用户输入的内容。
System.out.println("请输入要传输的内容");            Scanner scanner = new Scanner(System.in);            if (!scanner.hasNext()) {                    break;            }            String request =scanner.next();
发送数据报,把请求发送给服务器, 需要构造 DatagramPacket 对象。 构造过程中, 不光要构造载荷, 还要设置服务器的 IP 和端口号。
socket.send(new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIp),port));
接收服务器的响应
DatagramPacket responsePacket = new DatagramPacket(new byte[2024],2024);socket.receive(responsePacket);
从服务器读取的数据进行解析, 打印出来.
String response = new String(responsePacket.getData(),0,responsePacket.getData().length);            System.out.println(response);
总代码
import java.io.IOException;import java.net.*;import java.util.Scanner;public class UdpEchoClient {    //先创建一个成员变量:Socket对象,用于接收发送数据报    private DatagramSocket socket = null;    // UDP 本身不保存对端的信息, 自己的代码中保存一下    private String serverIp;    private int port;    public UdpEchoClient(String serverIp,int port) throws SocketException {        this.serverIp = serverIp;        this.port = port;        socket = new DatagramSocket();    }    public void start() throws IOException {        while (true) {            // 1. 从控制台读取用户输入的内容.            System.out.println("请输入要传输的内容");            Scanner scanner = new Scanner(System.in);            if (!scanner.hasNext()) {                    break;            }            String request =scanner.next();            //  发送数据报.把请求发送给服务器, 需要构造 DatagramPacket 对象。 构造过程中, 不光要构造载荷, 还要设置服务器的 IP 和端口号。            socket.send(new DatagramPacket(request.getBytes(),request.getBytes().length, InetAddress.getByName(serverIp),port));            //接收服务器的响应            DatagramPacket responsePacket = new DatagramPacket(new byte[2024],2024);            socket.receive(responsePacket);            //从服务器读取的数据进行解析, 打印出来.            String response = new String(responsePacket.getData(),0,responsePacket.getData().length);            System.out.println(response);        }    }    public static void main(String[] args) throws IOException {        UdpEchoClient client = new UdpEchoClient("127.0.0.1",6666);        client.start();    }}

点击全文阅读


本文链接:http://zhangshiyu.com/post/196561.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1