UDP传输
一.发送端
1.建立udp的socket对象
2.将要发送的数据封装成数据包
3.通过udp的socket对象,将数据包发送出
4.释放资源
public class Sender {
public static void main(String[] args) throws IOException {
// 应用层: 根据应用层的业务逻辑,产生要发送的数据
String data = "hello, udp";
//-------------------------------------------------------------
// 数据传输
//1. 创建Socket对象
DatagramSocket datagramSocket = new DatagramSocket(10086);//本机端口
//2.将要发送的数据封装成数据包
byte[] dataBytes = data.getBytes();
// 将要发送的字节数据,封装到数据报包中
InetAddress targetIp = InetAddress.getByName("192.168.56.1");
int tartPort = 9998;
DatagramPacket senderPacket = new DatagramPacket(dataBytes, 0,
dataBytes.length, targetIp, tartPort);
// 3. 利用DatagramSocket对象的send方法,将数据报包,发送出去
datagramSocket.send(senderPacket);
// 4. 释放Socket套接字
datagramSocket.close();
}
}
a. DatagramSocket(基于UDP)
此类表示用来 发送 和 接收 数据报包 的套接字。
构造方法
DatagramSocket(int port) 该Socket对象,本机ip + 指定端口号 port
创建数据报套接字并将其绑定到本地主机上的指定端口。
b. 发送数据的数据报包
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
参数:
buf - (要发送的)包数据。
offset - 包数据偏移量。
length - 包数据长度。
address - 目的地址。
port - 目的端口号。
c. 利用DatagramSocket对象的send方法
public void send(DatagramPacket p)
1)从此套接字发送数据报包。
2)DatagramPacket 包含的信息指示:将要发送的数据、其长度、远程主机的 IP 地址和远程主机的端口号。
发现,即使还没有接收端(接收端根本就没运行), 基于UDP协议的发送端,仍然可以正常向接收端发送数据。所以,再次证明,UDP协议是一种无连接的 不可靠 的协议。
二:接收端
1.建立udp的socket对象.
2.创建用于接收数据的数据报包,
3.通过socket对象的receive方法接收数据
4.可以对资源进行释放
public class Receiver {
public static void main(String[] args) throws IOException {
// 1.创建接收端,用来发送和接收数据的DatagramSocket对象
DatagramSocket datagramSocket = new DatagramSocket(9998);
// 2. 创建用来接收数据的数据报包
byte[] buf = new byte[1024];
//演示offset
int off = 5;
//创建用来接收数据的数据报包
DatagramPacket receiverPacket = new DatagramPacket(buf, off, buf.length - off);
//3.调用datagramSocket的receive方法,接收发送端发送的数据
datagramSocket.receive(receiverPacket);
//4. 释放资源
datagramSocket.close();
// -----------------------------------------------------------------
// 应用层,解析并处理接收到数据(字节)
// 接收到的多个字节数据
byte[] data = receiverPacket.getData();
// 获取,实际接收到的字节数据的起始位置
int offset = receiverPacket.getOffset();
System.out.println("应用层: " + offset);
//获取,本次实际接收到的字节数
int length = receiverPacket.getLength();
System.out.println("应用层: " + length);
// 解析接收到的字节数据
String s = new String(data, offset, length);
// 在接受数据的数据报包上调用以下方法:
// 1. InetAddress receiverPacket.getAddress() 获取发送方ip
// 2. int receiverPacket.getPort() 发送方的端口号
System.out.println(receiverPacket.getAddress() + ":" + receiverPacket.getPort() + ", " +s);
}
}
用来接受数据的数据报包:
a.DatagramPacket(byte[] buf, int offset, int length)
构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
参数:
buf - 保存传入数据报的缓冲区。
offset - 缓冲区的偏移量
length - 读取的字节数。
b.
public void receive(DatagramPacket p)
a.从此套接字接收数据报包。
b.当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。
c. 数据报包也包含发送方的 IP地址和发送方机器上的端口号。
d. 此方法在接收到数据报前一直阻塞
注意事项:
1. receive方法是一个阻塞方法
2. 当我们两次运行Receiver接收端,BindException: Address already in use (ip + port)一个端口号只能同时绑定一个进程,所以当两次启动Receiver端的时候,相当于要让两个进程绑定同一个端口号。
TCP传输
一:发送端
1.建立客户端的Socket对象,并明确要连接的对端的(服务器端)地址。
2.如果Socket对象建立成功,就表明客户端和服务器端已经建立好了连接。此时如果要发送数据,就可以在Socket对象中获取用来发送数据的流
3.向流中读取或写入数据
4.释放资源
a. public class Socket
此类实现 客户端 套接字(也可以就叫“套接字”)。
Socket(String host, int port)
创建一个流套接字 并将其连接到 指定主机上的指定端口号。
host:客户端要连接的,服务器端主机的目标ip地址
port: 客户端要连接的, 服务器端主机中目标进程的端口
注意:我们创建出来的Socket对象,它本身绑定的是本机ip地址 + 绑定一个系统随机分配的端口号。
注意事项:当服务器端还未运行的时候,直接运行客户端,会报错ConnectException: Connection refused, 因为,基于tcp协议的数据传输,要先建立好连接,才能传输数据
所以tcp协议是一种有连接的 可靠 的协议
public class Client {
public static void main(String[] args) throws IOException {
// 1.创建基于TCP协议的Socket对象
Socket socket = new Socket("127.0.0.1", 9999);
// 2. 从Socket对象中获取用来发送数据的流
OutputStream out = socket.getOutputStream();
//3. 利用流发送数据
out.write("hello, tcp".getBytes());
//4. 关闭Socket,释放资源
socket.close(); // socket会负责关闭,并释放其底层流
}
}
二:接收端
1.创建ServerSocket对象,在指定端口监听客户端连接请求
2.收到客户端连接请求后,建立Socket连接
3.如果连接建立成功,就表明已经建立了数据传输的通道.就可以在该通道通过IO进行数据的读取和写入
从socket中根据需要获取输入或输出流
4.根据需要向流中写入数据或从流中读数据
5.释放资源
public class ServerSocket
此类实现服务器套接字。服务器套接字等待(连接)请求通过网络传入(ServerSocket接收并处理连接请求)
ServerSocket(int port)
1.侦听并接受到此套接字的连接。
2.此方法在连接传入之前一直阻塞。
注意事项:
1. accept方法是阻塞方法
2. 服务器端,不能同时启动两次 BindException: Address already in use
public class Server {
public static void main(String[] args) throws IOException {
//1. 创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(9999);
//2.收到客户端连接请求后,建立Socket连接
Socket socket = serverSocket.accept();
//根据需要从服务器端的Socket对象,获取流
InputStream in = socket.getInputStream();
byte[] buf = new byte[1024];
//4.根据需要向流中写入数据或从流中读数据
int len = in.read(buf);
String s = new String(buf, 0, len);
// 通过Socket对象的getAnetAddress() 以及 getPort(), 可以分别获取到发送端额ip地址和端口号
System.out.println("接收到来自," + socket.getInetAddress() + ":" + socket.getPort()+ " " + s);
//5.释放资源
socket.close();
serverSocket.close();
}
}