基本原理
UDP编程
传输原理
发送端步骤
-
创建socket对象(DatagramSocket)
-
把要发送的数据封装成数据报包
-
通过send(数据报包)方法把数据报包发送出去
-
close释放资源
接收端步骤
-
创建接收端的socket对象(DatagramSocket)
-
创建用于接收的数据报包
-
通过receive(数据报包)方法进行接收
-
解析数据报包, 把数据从包里取出来
-
close释放资源
DatagramSocket
此类表示用来 发送 和 接收 数据报包的套接字
构造方法
DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。
成员方法
void | receive(DatagramPacket p) 从此套接字接收数据报包。 |
---|---|
void | send(DatagramPacket p) 从此套接字发送数据报包。 |
DatagramPacket
此类表示数据报包
构造方法
用于发送的
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
用于接收的
DatagramPacket(byte[] buf, int offset, int length) 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
成员方法
byte[] | getData() 返回数据缓冲区。 |
---|---|
int | getLength() 返回将要发送或接收到的数据的长度。 |
int | getOffset() 返回将要发送或接收到的数据的偏移量。 |
案例
v1 发送端发送消息,接收端接收并打印
package _22network.com.cskaoyan.udp.v1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @description: 接收端
* @author: 景天
* @date: 2022/6/30 16:27
**/
public class Receiver {
public static void main(String[] args) throws IOException {
// - 创建接收端的socket对象(DatagramSocket)
DatagramSocket datagramSocket = new DatagramSocket(9999);
//- 创建用于接收的数据报包
// DatagramPacket(byte[] buf, int offset, int length)
// 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
byte[] bytes = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
//- 通过receive(数据报包)方法进行接收
System.out.println("receive before");
datagramSocket.receive(receivePacket);
System.out.println("receive after");
//- 解析数据报包, 把数据从包里取出来
byte[] data = receivePacket.getData();
int offset = receivePacket.getOffset();
int length = receivePacket.getLength();
String s = new String(data, offset, length);
System.out.println("接收到了来自"+receivePacket.getSocketAddress()+
"的消息: " +s);
//- close释放资源
datagramSocket.close();
}
}
package _22network.com.cskaoyan.udp.v1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @description: 发送端
* @author: 景天
* @date: 2022/6/30 16:16
**/
/*
v1 发送端发送消息,接收端接收并打印
*/
public class Sender {
public static void main(String[] args) throws IOException {
// - 创建socket对象(DatagramSocket)
DatagramSocket datagramSocket = new DatagramSocket(8888);
//- 把要发送的数据封装成数据报包
String s = "hello udp";
// DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
// 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。
byte[] bytes = s.getBytes();
InetAddress targetIp = InetAddress.getByName("127.0.0.1");
int port = 9999;
DatagramPacket sendPacket =
new DatagramPacket(bytes, 0, bytes.length, targetIp, port);
//- 通过send(数据报包)方法把数据报包发送出去
datagramSocket.send(sendPacket);
//- close释放资源
datagramSocket.close();
}
}
我的
package com._24_network._01_udp.ver1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* 发送端
*
* @author zxcsjf
* @since 2022/06/30 19:18
*/
public class Sender {
public static void main(String[] args) throws IOException {
// 1.创建发送端的socket对象,port绑定本机端口
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 2.把要发送的数据变成字节数组
String s = "lalalalal~~~~";
byte[] bytes = s.getBytes();
// 3.得到目标ip
InetAddress targetIP = InetAddress.getByName("192.168.13.255");
// InetAddress localHost = InetAddress.getLocalHost();
// 4.目标端口号
int port = 9999;
// 6.封装成数据报包
DatagramPacket datagramPacket = new DatagramPacket(bytes, 0, bytes.length, targetIP, port);
// 7.通过send(包) 发送出去
datagramSocket.send(datagramPacket);
// 8.close
datagramSocket.close();
}
}
——————————————————————————————————————————————————————————————————————————————
package com._24_network._01_udp.ver1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author zxcsjf
* @since 2022/06/30 19:29
*/
public class Receiver {
public static void main(String[] args) throws IOException {
// 1.创建接收端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 2.创建用于存放数据的字符数组
byte[] bytes = new byte[1024];
// 3.创建用于接收数据的数据报包
DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
// 4.调用socket的receive方法进行接收
datagramSocket.receive(receivePacket);
// 5.解析数据报包,把数据从包中取出,放入字符串
byte[] data = receivePacket.getData();
int offSet = receivePacket.getOffset();
int length = receivePacket.getLength();
String s = new String(data, offSet, length);
System.out.println("接受到了来自" + receivePacket.getSocketAddress() + "的消息:" + s);
// 6.释放资源,
datagramSocket.close();
}
}
v2 使用工具类优化v1
package _22network.com.cskaoyan.udp.v1;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @description: 接收端
* @author: 景天
* @date: 2022/6/30 16:27
**/
public class Receiver {
public static void main(String[] args) throws IOException {
// - 创建接收端的socket对象(DatagramSocket)
DatagramSocket datagramSocket = new DatagramSocket(9999);
//- 创建用于接收的数据报包
// DatagramPacket(byte[] buf, int offset, int length)
// 构造 DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
byte[] bytes = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
//- 通过receive(数据报包)方法进行接收
System.out.println("receive before");
datagramSocket.receive(receivePacket);
System.out.println("receive after");
//- 解析数据报包, 把数据从包里取出来
byte[] data = receivePacket.getData();
int offset = receivePacket.getOffset();
int length = receivePacket.getLength();
String s = new String(data, offset, length);
System.out.println("接收到了来自"+receivePacket.getSocketAddress()+
"的消息: " +s);
//- close释放资源
datagramSocket.close();
}
}
package _22network.com.cskaoyan.udp.v2;
import utils.NetWorkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @description: 接收端
* @author: 景天
* @date: 2022/6/30 16:48
**/
public class Receiver {
public static void main(String[] args) throws IOException {
// 创建接收端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 创建用于接收的数据报包
DatagramPacket receivePacket = NetWorkUtils.getReceivePacket();
// receive
datagramSocket.receive(receivePacket);
// 解析
String msg = NetWorkUtils.parse(receivePacket);
System.out.println(receivePacket.getSocketAddress()+": " + msg);
// close
datagramSocket.close();
}
}
————————————————————————————————————————————————————————————————————————————————————————
package utils;
import java.io.Reader;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @description:
* @author: 景天
* @date: 2022/6/30 16:38
**/
public class NetWorkUtils {
// 获取发送的数据报包
public static DatagramPacket getSendPacket(String msg,String ip,int port)
throws UnknownHostException {
// 把数据封装成包
byte[] bytes = msg.getBytes();
InetAddress targetIp = InetAddress.getByName(ip);
DatagramPacket sendPacket =
new DatagramPacket(bytes, 0,
bytes.length, targetIp, port);
// 最终要返回装满了数据的包
return sendPacket;
}
// 获取接收的数据报包
public static DatagramPacket getReceivePacket() {
// 构建用于接收的包
byte[] bytes = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(bytes, 0,
bytes.length);
return receivePacket;
}
// 解析数据的方法
public static String parse(DatagramPacket packet) {
// 解析数据
byte[] data = packet.getData();
int offset = packet.getOffset();
int length = packet.getLength();
String s = new String(data, offset, length);
return s;
}
}
v3 发送端接收端相互发送
package _22network.com.cskaoyan.udp.v3;
import utils.NetWorkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;
/**
* @description: 接收端
* @author: 景天
* @date: 2022/6/30 17:17
**/
public class Receiver {
public static void main(String[] args) throws IOException {
// 创建接收端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 创建scanner对象
Scanner scanner = new Scanner(System.in);
// while循环
while (true) {
// 接收逻辑
// 创建用于接收的数据报包
DatagramPacket receivePacket = NetWorkUtils.getReceivePacket();
// receive接收
datagramSocket.receive(receivePacket);
// 解析
String msg = NetWorkUtils.parse(receivePacket);
System.out.println(msg);
// 发送逻辑
// 键盘接收数据
String s = scanner.nextLine();
// 把数据封装成数据报包
DatagramPacket sendPacket =
NetWorkUtils.getSendPacket(s, "127.0.0.1", 8888);
// send发送
datagramSocket.send(sendPacket);
}
}
}
package _22network.com.cskaoyan.udp.v3;
import utils.NetWorkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Scanner;
/**
* @description: 发送端
* @author: 景天
* @date: 2022/6/30 17:12
**/
/*
发送端接收端相互发送
*/
public class Sender {
public static void main(String[] args) throws IOException {
// 创建发送端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 创建Scanner对象
Scanner scanner = new Scanner(System.in);
// while循环
while (true) {
// 发送逻辑
// 键盘接收数据
String s = scanner.nextLine();
// 把数据封装成数据报包
DatagramPacket sendPacket =
NetWorkUtils.getSendPacket(s, "127.0.0.1", 9999);
// send发送
datagramSocket.send(sendPacket);
// 接收逻辑
// 创建用于接收的数据报包
DatagramPacket receivePacket = NetWorkUtils.getReceivePacket();
// receive方法接收
datagramSocket.receive(receivePacket);
// 解析
String msg = NetWorkUtils.parse(receivePacket);
System.out.println(msg);
}
}
}
v4 使用多线程优化v3
思路:
OnePerson
AnotherPerson
定义2个任务
发送任务 只发送消息SendTask
-
定义一个成员变量 DatagramSocket
-
ip
-
port
接收任务 只接收消息ReceiveTask
-
定义一个成员变量 DatagramSocket
package _22network.com.cskaoyan.udp.v4;
import java.io.IOException;
import java.net.DatagramSocket;
/**
* @description:
* @author: 景天
* @date: 2022/6/30 17:42
**/
public class AnotherPerson {
public static void main(String[] args) throws IOException {
// 创建socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 创建接收,发送任务
// 运行在线程中
new Thread(new SendTask(datagramSocket,"127.0.0.1",8888)).start();
new Thread(new ReceiveTask(datagramSocket)).start();
}
}
package _22network.com.cskaoyan.udp.v4;
import java.io.IOException;
import java.net.DatagramSocket;
/**
* @description:
* @author: 景天
* @date: 2022/6/30 17:42
**/
public class OnePerson {
public static void main(String[] args) throws IOException {
// 创建socket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 创建接收,发送任务
// 运行在线程中
new Thread(new SendTask(datagramSocket,"127.0.0.1",9999)).start();
new Thread(new ReceiveTask(datagramSocket)).start();
}
}
package _22network.com.cskaoyan.udp.v4;
import utils.NetWorkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @description: 接收消息的任务
* @author: 景天
* @date: 2022/6/30 17:39
**/
public class ReceiveTask implements Runnable{
// 定义一个成员变量 DatagramSocket
DatagramSocket datagramSocket;
public ReceiveTask(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
// 只接收消息
while (true) {
// 创建用于接收的数据报包
DatagramPacket receivePacket = NetWorkUtils.getReceivePacket();
// receive方法接收数据
try {
datagramSocket.receive(receivePacket);
// parse
String msg = NetWorkUtils.parse(receivePacket);
// 打印
System.out.println("接收到了来自" +
receivePacket.getSocketAddress() +
"的消息: " + msg);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package _22network.com.cskaoyan.udp.v4;
import utils.NetWorkUtils;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.UnknownHostException;
import java.util.Scanner;
/**
* @description: 发送任务
* @author: 景天
* @date: 2022/6/30 17:35
**/
/*
- 定义一个成员变量 DatagramSocket
- ip
- port
*/
public class SendTask implements Runnable{
// 定义成员变量
DatagramSocket datagramSocket;
String ip;
int port;
public SendTask(DatagramSocket datagramSocket, String ip, int port) {
this.datagramSocket = datagramSocket;
this.ip = ip;
this.port = port;
}
@Override
public void run() {
// 创建Scanner对象
Scanner scanner = new Scanner(System.in);
// 只发送消息
while (true) {
// 键盘接收数据
String msg = scanner.nextLine();
// 把数据封装成数据报包
try {
DatagramPacket sendPacket =
NetWorkUtils.getSendPacket(msg, ip, port);
// send
datagramSocket.send(sendPacket);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
TCP编程
传输原理
客户端步骤(Client)
-
创建客户端socket对象
-
从socket对象中获取输入输出流
-
利用输入输出流进行读写操作
-
close释放资源
服务端步骤(Server)
-
创建服务端socket对象(ServerSocket)
-
利用accept方法建立连接,得到socket对象
-
从socket对象中获取输入输出流
-
利用输入输出流进行读写操作
-
close释放资源
Socket
此类实现客户端套接字
构造方法
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号
成员方法
InputStream | getInputStream() 返回此套接字的输入流。 |
---|---|
OutputStream | getOutputStream() 返回此套接字的输出流。 |
---|---|
Socket半关闭
void | shutdownOutput() 禁用此套接字的输出流。 |
---|---|
ServerSocket
此类实现服务器套接字
构造方法
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
成员方法
Socket | accept() 侦听并接受到此套接字的连接。 |
---|---|
案例
v1 客户端发送消息,服务端接收并打印
package _22network.com.cskaoyan.tcp.v1;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* @description: 客户端
* @author: 景天
* @date: 2022/7/1 9:46
**/
/*
客户端发送消息,服务端接收并打印
*/
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端socket对象
Socket socket = new Socket("127.0.0.1", 9999);
// 获取输出流
OutputStream out = socket.getOutputStream();
// write数据
out.write("hello tcp".getBytes());
// close
out.close();
}
}
package _22network.com.cskaoyan.tcp.v1;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @description: 服务端
* @author: 景天
* @date: 2022/7/1 9:49
**/
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端socket对象
ServerSocket serverSocket = new ServerSocket(9999);
// 建立连接 accept方法 --> socket对象
System.out.println("accept before");
Socket socket = serverSocket.accept();
System.out.println("accept after");
// 从socket对象中获取输入流
InputStream in = socket.getInputStream();
// read读取数据
byte[] bytes = new byte[1024];
int readCount = in.read(bytes);
String s = new String(bytes, 0, readCount);
System.out.println(socket.getInetAddress() +"--"+socket.getPort()+ "--" + s);
// close
socket.close();
serverSocket.close();
}
}
v2 多个客户端发送,服务端接收(多线程处理)
package _22network.com.cskaoyan.tcp.v2;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @description: 服务端
* @author: 景天
* @date: 2022/7/1 10:06
**/
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端socket对象
ServerSocket serverSocket = new ServerSocket(8888);
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
// 循环
while (true) {
// 建立连接 得到Socket对象
Socket socket = serverSocket.accept();
//new Thread(new ConnectTask(socket)).start();
// 使用线程池
pool.submit(new ConnectTask(socket));
}
}
}
class ConnectTask implements Runnable{
// 定义成员变量
Socket socket;
public ConnectTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
while (true) {
// 读取来自客户端的数据
try {
// 获取输入流
InputStream in = socket.getInputStream();
// read读一下
byte[] bytes = new byte[1024];
int readCount = in.read(bytes);
String s = new String(bytes, 0, readCount);
System.out.println(socket.getInetAddress() + ":" + socket.getPort() +
"--" + Thread.currentThread().getName() + s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
package _22network.com.cskaoyan.tcp.v2;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* @description:
* @author: 景天
* @date: 2022/7/1 9:58
**/
/*
多个客户端发送,服务端接收(多线程处理)
*/
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端socket对象
Socket socket = new Socket("127.0.0.1", 8888);
// 创建Scanner对象
Scanner scanner = new Scanner(System.in);
// while循环
while (true) {
// 键盘接收数据
String s = scanner.nextLine();
// 从socket中获取输出流
OutputStream out = socket.getOutputStream();
out.write(s.getBytes());
}
}
}
v3 客户端发送对象(序列化),服务端接收
package _22network.com.cskaoyan.tcp.v3;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @description:
* @author: 景天
* @date: 2022/7/1 10:24
**/
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建服务端Socket对象
ServerSocket serverSocket = new ServerSocket(12306);
// 跟客户端建立连接
Socket socket = serverSocket.accept();
// 得到socket对象
// 从socket中获取输入流
InputStream in = socket.getInputStream();
// 利用反序列流进行包装
ObjectInputStream objectInputStream = new ObjectInputStream(in);
// readObject读取对象
Object o = objectInputStream.readObject();
// 打印
System.out.println(o);
// close
socket.close();
serverSocket.close();
}
}
package _22network.com.cskaoyan.tcp.v3;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* @description:
* @author: 景天
* @date: 2022/7/1 10:20
**/
/*
v3 客户端发送对象(序列化),服务端接收
*/
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端Socket对象
Socket socket = new Socket("127.0.0.1", 12306);
// 创建学生对象
Student student = new Student("zs", 20);
// 从Socket中获取输出流
OutputStream out = socket.getOutputStream();
// 利用序列化流进行包装
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
// writeObject(学生对象)
objectOutputStream.writeObject(student);
// close
socket.close();
}
}
v4 客户端上传文件到服务端
思路:
package _22network.com.cskaoyan.tcp.v4;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* @description:
* @author: 景天
* @date: 2022/7/1 10:59
**/
/*
客户端上传文件到服务端
*/
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端的socket对象
Socket socket = new Socket("127.0.0.1", 11111);
// 创建自己的输入流对象
FileInputStream in = new FileInputStream("D:\\b.txt");
// 从socket中获取输出流
OutputStream out = socket.getOutputStream();
// 边读边写
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = in.read(bytes)) != -1) {
out.write(bytes,0,readCount);
}
// 循环结束意味着文件已经上传到服务器
System.out.println("客户端已经上传完毕");
// 禁用此套接字的输出流 shutdownOutput()
socket.shutdownOutput();
// 接收来自服务端的反馈消息
// 从socket中获取输入流
InputStream inputStream = socket.getInputStream();
// 读取数据
byte[] bytes1 = new byte[1024];
System.out.println("read before");
int readCount2 = inputStream.read(bytes1);
System.out.println("read after");
System.out.println(new String(bytes1,0,readCount2));
// close
in.close();
socket.close();
}
}
package _22network.com.cskaoyan.tcp.v4;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @description: 服务端
* @author: 景天
* @date: 2022/7/1 11:03
**/
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端socket对象
ServerSocket serverSocket = new ServerSocket(11111);
// 创建自己的输出流对象
FileOutputStream out = new FileOutputStream("copy_b.txt");
// accept建立连接 得到socket对象
Socket socket = serverSocket.accept();
// 从socket获取输入流
InputStream in = socket.getInputStream();
// 边读边写
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = in.read(bytes)) != -1) {
out.write(bytes,0,readCount);
}
System.out.println("end while");
// 保存成功
// 给客户端一个反馈消息
// 从socket中获取输出流
OutputStream outputStream = socket.getOutputStream();
// write
outputStream.write("文件上传成功!".getBytes());
// close
out.close();
socket.close();
serverSocket.close();
}
}
异常
java.net.ConnectException: Connection refused: connect 先启动了Client
java.net.BindException: Address already in use: JVM_Bind port重复了