🎇🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!
人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习!
欢迎志同道合的朋友一起加油喔 💪💪💪
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心
目录
- 1、什么是网络编程
- 2、Socket套接字
- 2.1、概念
- 2.2、分类
- 3、UDP数据报套接字编程
- 3.1、API 介绍
- 📚DatagramSocket
- 📚DatagramPacket
- 📚InetSocketAddress
- 3.2、基于UDP的Socket通信
1、什么是网络编程
- ⽹络编程 ,指⽹络上的主机 ,通过不同的进程 ,以编程的⽅式实现⽹络通信(或称为⽹络数据传输)。
当然 ,我们只要满⾜进程不同就⾏;所以即便是同⼀个主机 ,只要是不同进程 ,基于⽹络来传输数 据 ,也属于⽹络编程。
特殊的 ,对于开发来说 ,在条件有限的情况下 ,⼀般也都是在⼀个主机中运⾏多个进程来完成⽹络编 程。
但是 ,我们⼀定要明确 ,我们的⽬的是提供⽹络上不同主机 ,基于⽹络来传输数据资源:
进程A:编程来获取⽹络资源
进程B:编程来提供⽹络资源
2、Socket套接字
2.1、概念
Socket套接字 ,是由系统提供⽤于⽹络通信的技术 ,是基于TCP/IP协议的⽹络通信的基本操作单元。 基于Socket套接字的⽹络程序开发就是⽹络编程。
操作系统提供的网络编程的API就称为 “ socket api ”
2.2、分类
- Socket套接字主要针对传输层协议划分为如下三类:
- 流套接字:使⽤传输层-----------TCP协议
TCP ,即Transmission Control Protocol(传输控制协议),传输层协议。 以下为TCP的特点(细节后续再学习):
- 有连接
- 可靠传输
- ⾯向字节流
- 全双工
- 有接收缓冲区 ,也有发送缓冲区
- ⼤⼩不限
对于字节流来说 ,可以简单的理解为 ,传输数据是基于IO流 ,流式数据的特征就是在IO流没有关闭的 情况下 ,是⽆边界的数据 ,可以多次发送 ,也可以分开多次接收。
- 数据报套接字:使⽤传输层UDP协议
UDP ,即User Datagram Protocol( ⽤户数据报协议),传输层协议。 以下为UDP的特点(细节后续再学习):
- ⽆连接
- 不可靠传输
- ⾯向数据报
- 全双工
- 有接收缓冲区 ,⽆发送缓冲区
- ⼤⼩受限:⼀次最多传输64k
对于数据报来说 ,可以简单的理解为 ,传输数据是⼀块⼀块的 ,发送⼀块数据假如100个字节 ,必须⼀ 次发送 ,接收也必须⼀次接收100个字节 ,⽽不能分100次 ,每次接收1个字节。
- Unix 域套接字 -------不能跨主机通信,只能本机上的进程和进程之间的 通信
3、UDP数据报套接字编程
3.1、API 介绍
📚DatagramSocket
-
系统中,本身就有socket这样的概念,DatagramSocket就是对于操作系统的socket概念的封装
-
系统中的socket,可以理解成是一种文件
-
文件是一个广义的概念~~
-
socket文件,就可以视为是"网卡"这种硬件设备的抽象表现形式.
-
针对socket文件的读写操作就相当于针对网卡这个硬件设备进行读写.
-
此处,DatagramSocket就可以视为是"操作网卡"的遥控器.
针对这个对象进行读写操作,就是在针对网卡进行读写操作.
DatagramSocket是UDP Socket ,⽤于发送和接收UDP数据报
- DatagramSocket构造方法:
方法签名 | 方法说明 |
---|---|
DatagramSocket() | 创建⼀个UDP数据报套接字的Socket ,绑定到本机任 意⼀个随机端⼝ ( ⼀般⽤于客户端) |
DatagramSocket(int port) | 创建⼀个UDP数据报套接字的Socket ,绑定到本机指 定的端⼝ ( ⼀般⽤于服务端) |
- DatagramSocket 方法:
方法签名 | 方法说明 |
---|---|
void receive(DatagramPacket p) | 从此套接字接收数据报(如果没有接收到数据报 ,该 ⽅法会阻塞等待) |
voidsend(DatagramPacket p) | 从此套接字发送数据报包(不会阻塞等待 ,直接发 送) |
void close() | 关闭此数据报套接字 |
📚DatagramPacket
DatagramPacket是UDP Socket发送和接收的数据报。
Datagram Packet 构造方法:
方法签名 | 方法说明 |
---|---|
DatagramPacket(byte[] buf, int length) | 构造⼀个DatagramPacket以⽤来接收数据报 ,接收的 数据保存在字节数组(第⼀个参数buf) 中 ,接收指定 ⻓度(第⼆个参数length) |
DatagramPacket(byte[] buf, int offset, int length, SocketAddress address) | 构造⼀个DatagramPacket以⽤来发送数据报 ,发送的 数据为字节数组(第⼀个参数buf) 中 ,从0到指定⻓ 度(第⼆个参数length)。address指定⽬的主机的IP 和端⼝号 |
- Datagram Packet 方法:
方法签名 | 方法说明 |
---|---|
InetAddress getAddress() | 从接收的数据报中 ,获取发送端主机IP地址;或从发 送的数据报中 ,获取接收端主机IP地址 |
int getPort() | 从接收的数据报中 ,获取发送端主机的端⼝号;或从 发送的数据报中 ,获取接收端主机端⼝号 |
byte[] getData() | 获取数据报中的数据 |
构造UDP发送的数据报时 ,需要传⼊SocketAddress, 该对象可以使⽤InetSocketAddress来创建。
📚InetSocketAddress
InetSocketAddress(SocketAddress的⼦类 )构造⽅法:
⽅法签名 | ⽅法说明 |
---|---|
InetSocketAddress(InetAddress addr, int port) | 创建⼀个Socket地址 ,包含IP地址和端⼝号 |
3.2、基于UDP的Socket通信
步骤分析:
- 服务器端先启动,创建DatagramSocket对象,监听端口,用于接收
- 服务器端创建DatagramPacket对象,打包用于接收的数据包
- 服务器阻塞等待接收
- 客户端启动,创建DatagramSocket对象,监听端口,用于接收
- 客户端创建DatagramPacket对象,打包用于发送的数据包
- 客户端发送数据,服务端接收
- 服务端接收数据后,创建DatagramPacket对象,打包用于发送的数据包,发送数据
- 客户端创建DatagramPacket对象,打包用于接收的数据包,阻塞等待接收
- 客户端接收服务端数据,断开连接,释放资源
- java中使⽤UDP协议通信 ,主要基于DatagramSocket类来创建数据报套接字 ,并使⽤DatagramPacket作为发送或接收的UDP数据报。对于⼀次发送及接收UDP数据报的流程如下:
- 以上只是⼀次发送端的UDP数据报发送 ,及接收端的数据报接收 ,并没有返回的数据。也就是只有请求 ,没有响应。对于⼀个服务端来说 ,重要的是提供多个客户端的请求处理及响应 ,流程如下:
服务器端:
package network;
//其他的网络编程的类 都在 net 这个包中
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;//服务器程序,需要在程序启动的时候,将端口号确定下来
//原因:
//因为客户端是主动的一方,服务器是被动的一方
//所以呢?
//客户端要能知道服务器在哪,才能 “主动的了”
//需要知道哪些东西呢?
// 1、ip地址(服务器所在主机的ip)
// 2、端口号(一个主机上,有多个程序都要进行网络通信,就需要把哪个程序用哪个端口给明确下来)
//而且一个端口不能同时被两个或者多个进程来关联~~(不能重复)
public class UdpEchoServer {//对于一个服务器,一上来我们需要做什么工作呢?//我们要进行网络通信,呢就需要操纵网卡//而操纵网卡就是基于 socket 对象进行展开的//接下来实例化一个类作为 UdpEchoServer 这个服务器的成员变量private DatagramSocket socket = null;//SocketException异常继承于IOException//说明网络编程也属于 io 操作//io 是一个很广泛的概念public UdpEchoServer(int port) throws SocketException {//在构造方法里面进行实例化//一方面可以处理异常,另一方面还需要传入一个参数 (端口号)this.socket = new DatagramSocket(port);}//服务器还需要不停的去处理请求,不停的返回响应 期限(7 * 24)public void start() throws IOException {System.out.println("服务器启动!!!");while (true){//1)读取请求并解析byte[] bytes = new byte[4096];//“空盘子”//接受的数据保存在 数组里面//requestPacket 这个对象是一个UDP数据报//里面就包含两个部分://1)报头(通过类的属性来表示)//2)载荷(通过构造方法传递的字符数组,作为持有载荷的空间)DatagramPacket requestPacket = new DatagramPacket(bytes,4096);/*括号里面需要填写输出型参数从此套接字接收数据报(如果没有接收到数据报 ,该⽅法会阻塞等待)*/socket.receive(requestPacket);//此时requestPacket里面存储的是二进制数据,为了方便Java代码中处理,可以把上述数据报中的二进制数据,拿出来,构造为StringString requst = new String(requestPacket.getData(),0,requestPacket.getLength());//2)根据请求计算响应//可以写成一个单独的方法String response = process(requst);//3)把响应写会到客户端//要想发送,也需要实例化一个 DatagramPacket 对象//括号里面需要放一个有数据的字节数组DatagramPacket responsePacket = new DatagramPacket(response.getBytes(),0,response.getBytes().length,requestPacket.getSocketAddress());/*requestPacket.getSocketAddress()这个方法返回的对象里面就包含了 IP 和 端口号*/socket.send(responsePacket);//打印个日志//IP地址、端口号、请求、响应System.out.printf("[%s:%d] req = %s,resp = %s\n",requestPacket.getAddress(),requestPacket.getPort(),requst,response);}}//由于当前写的是“回显服务器”//请求和响应是一样的//所以 请求是什么,响应就返回什么private String process(String requst) {return requst;}public static void main(String[] args) throws IOException {UdpEchoServer server = new UdpEchoServer(9090);server.start();}
}
UdpEchoServer(UDP 服务器):
- 首先,创建一个 DatagramSocket 对象,并绑定到指定的端口。这个端口用于服务器与客户端之间的通信。
- 在服务器的主循环中,首先创建一个空的 DatagramPacket 对象,用于接收客户端发来的请求数据。
- 调用 socket.receive(requestPacket) 方法接收客户端发来的数据包。
- 将收到的数据包中的数据转换成字符串形式,并调用 process() 方法生成响应。在这个例子中,响应就是原请求。
- 创建一个新的 DatagramPacket 对象,包含响应数据和客户端的地址信息。
- 使用 socket.send(responsePacket) 方法将响应数据包发送回客户端。
- 打印请求和响应信息。
客户端:
package network;import java.io.IOException;
import java.net.*;
import java.util.Scanner;public class UdpEchoClient {//客户端和服务器一样//刚开始也需要创建 DatagramSocket 对象//接下来要对网卡进行操作//就需要借助 socket 这个 “遥控器”private DatagramSocket socket = null;private String severIp;private int severPort;//需要只要服务器的ip和端口号//通过额外的途径进行获取//利用String severIp,int severPort 来获取//因此服务器的IP和端口号不能老变public UdpEchoClient(String severIp,int severPort) throws SocketException {this.socket = new DatagramSocket();this.severIp = severIp;this.severPort = severPort;}public void start() throws IOException {System.out.println("客户端启动!!!");Scanner scanner = new Scanner(System.in);while (true){//1.让用户从控制台进行输入,发送给服务器System.out.println("请输入要发送的请求:");String request = scanner.next();//2.构造请求并发送//先实例化一个 DatagramPacket 对象 (数据报)// 不光要有数据 还要有目标 就是你要发给哪个服务器的哪个端口 也需要在这写出来// severIp = 127.0.0.1// 但是 InetAddress.getByName(severIp) 需要把 severIp 转换成一个Java能识别的对象// 转换成 InetAddress 这个对象 (系统能识别的IP地址)DatagramPacket requestPacket = new DatagramPacket(request.getBytes(),0,request.getBytes().length,InetAddress.getByName(severIp),severPort);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 {// 使用后 127.0.0.1 是因为服务器和客户端在同一个主机上// 此时就固定写这个ip// 它又叫做 环回ip 系统提供的特殊ipUdpEchoClient client = new UdpEchoClient("127.0.0.1",9090);//客户端的端口号 是操作系统自动进行分配的client.start();}
}
UdpEchoClient(UDP 客户端):
- 创建一个 DatagramSocket 对象,不需要显式地关联端口,系统会自动分配一个空闲的端口。
- 在客户端的主循环中,从控制台读取用户输入的字符串作为请求。
- 创建一个 DatagramPacket 对象,包含请求数据、服务器的 IP 地址和端口信息。
- 调用 socket.send(requestPacket) 方法将请求数据包发送给服务器。
- 创建一个空的 DatagramPacket 对象,用于接收服务器返回的响应数据。
- 调用 socket.receive(responsePacket) 方法接收服务器发来的响应数据包。
- 将收到的响应数据包中的数据转换成字符串形式,并打印请求和响应信息。
总结:这两段代码实现了一个简单的 UDP 回显服务器和客户端。客户端将用户输入的请求数据通过 UDP 协议发送给服务器,服务器接收到请求后原样返回响应,客户端接收响应并打印信息。整个过程使用无连接的 UDP 协议进行通信。