文章目录
- 网络数据传输的基本原理
- UDP
- 发送端步骤
- 接收端步骤
- DatagramSocket
- DatagramPacket
- 举例
- 版本1:发送端发送消息,接收端接收并打印
- 版本2:创建一个NetworkUtils工具类优化版本1
- 版本3:发送端接收端相互发送
- 版本4:使用多线程
- TCP
- 客户端步骤
- 服务端步骤
- Socket
- ServerSocket
- 举例
- 版本1:客户端发送消息,服务端接收并打印
- 版本2:多个客户端发送,服务端接收(多线程处理)
- 版本3:客户端发送对象(序列化),服务端接收
- 版本4:客户端上传文件到服务端
网络数据传输的基本原理
- ip地址
- 唯一确定目的主机
- port 端口号
- 唯一确定进程
- 协议
- UDP协议:无连接的不可靠的协议
- TCP协议:面向连接的可靠的协议
UDP
发送端步骤
- 创建发送端的socket对象
- 把要发送的数据封装成数据报包
send
方法发送数据报包- 释放资源
close
接收端步骤
- 创建接收端的socket对象
- 创建用于接收的数据报包
receive
方法接收数据- 解析数据报包
- 释放资源
close
DatagramSocket
此类表示用来发送和接收数据报包的套接字。
构造方法:
DatagramSocket(int port) // 创建数据报套接字并将其绑定到本地主机上的指定端口。
成员方法:
DatagramPacket
此类表示数据报包。
构造方法
1. 用于发送的
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
// 构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号
// InetAddress address ---> ip值
2. 用于接受的
DatagramPacket(byte[] buf, int offset, int length)
// 构造DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
成员方法:
举例
版本1:发送端发送消息,接收端接收并打印
// 发送端sender
public class Sender {
public static void main(String[] args) throws IOException {
// 1. 创建发送端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 2. 把要发送的数据封装成数据报包
String s = "hello";
// 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);
// 3. send方法发送数据报包
datagramSocket.send(sendPacket);
// 4. 释放资源close
datagramSocket.close();
}
}
———————————————————————————————————————————————————————————————————————————————————————————
// 接收端receiver
public class Receiver {
public static void main(String[] args) throws IOException {
// 1. 创建接收端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 2. 创建用于接收的数据报包
// DatagramPacket(byte[] buf, int offset, int length)
// 构造DatagramPacket,用来接收长度为 length 的包,在缓冲区中指定了偏移量。
byte[] bytes = new byte[1024];
DatagramPacket receivePacket = new DatagramPacket(bytes, 0, bytes.length);
// 3. receive方法接收数据
System.out.println("receiver before");
datagramSocket.receive(receivePacket);
System.out.println("receiver after");
// 4. 解析数据报包
byte[] data = receivePacket.getData();
int length = receivePacket.getLength();
int offset = receivePacket.getOffset();
System.out.println(new String(data, offset, length));
// 5. 释放资源close
datagramSocket.close();
}
}
版本2:创建一个NetworkUtils工具类优化版本1
NetworkUtil.java
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 parseMsg(DatagramPacket receivePacket) {
byte[] bytes = receivePacket.getData();
int length = receivePacket.getLength();
int offset = receivePacket.getOffset();
return new String(bytes,offset,length);
}
}
————————————————————————————————————————————————————————————————————————————————————————————
// sender 发送端
public class Sender {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(8888);
String s = "hello hi";
DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 9999);
datagramSocket.send(sendPacket);
datagramSocket.close();
}
}
————————————————————————————————————————————————————————————————————————————————————————————
// 接收端receiver
public class Receiver {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket(9999);
DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
datagramSocket.receive(receivePacket);
String s = NetworkUtils.parseMsg(receivePacket);
System.out.println(s);
datagramSocket.close();
}
}
版本3:发送端接收端相互发送
// 发送端sender
public class Sender {
public static void main(String[] args) throws IOException {
// 1. 创建发送端的socket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 2. 创建Scannner对象
Scanner scanner = new Scanner(System.in);
// while循环
while (true) {
// 3. 发送逻辑
// 键盘接收数据
String s = scanner.nextLine();
// 把数据封装成数据包
DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 9999);
// send
datagramSocket.send(sendPacket);
// 4. 接受逻辑
// 创建用于接受的数据报包
DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
// receive方法接收
datagramSocket.receive(receivePacket);
// 解析
String s1 = NetworkUtils.parseMsg(receivePacket);
System.out.println("接收到了" + receivePacket.getSocketAddress() + "的消息" + s1);
}
}
}
// 接收端 receiver
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);
// parse解析
String msg = NetworkUtils.parseMsg(receivePacket);
System.out.println("接收到了" + receivePacket.getSocketAddress() + "的消息" + msg);
// 发送逻辑
// 键盘接收数据
String s = scanner.nextLine();
// 把数据封装成包
DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, "127.0.0.1", 8888);
// send
datagramSocket.send(sendPacket);
}
}
}
版本4:使用多线程
- 定义发送任务 SendTask 专门用来发送消息
- 定义接收任务 receiveTask 专门用来接收消息
eg:
SendTask:
public class SenderTask implements Runnable {
// DatagramSocket datagramSocket
DatagramSocket datagramSocket;
// String ip
String ip;
// int port
int port;
public SenderTask(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
while(true){
// 键盘输入消息
String s = scanner.nextLine();
// 把消息封装成数据报包
try {
DatagramPacket sendPacket = NetworkUtils.getSendPacket(s, ip, port);
// send
datagramSocket.send(sendPacket);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
ReceiveTask:
public class ReceiveTask implements Runnable{
// 成员变量
DatagramSocket datagramSocket;
public ReceiveTask(DatagramSocket datagramSocket) {
this.datagramSocket = datagramSocket;
}
@Override
public void run() {
// 只是接收数据
while(true){
// 创建用于接收的数据报包
DatagramPacket receivePacket = NetworkUtils.getReceivePacket();
// receive接收
try {
datagramSocket.receive(receivePacket);
// 解析
String s = NetworkUtils.parseMsg(receivePacket);
// 打印
System.out.println("s = " + s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
OnePerson:
public class OnePerson {
public static void main(String[] args) throws IOException {
// 创建DatagramSocket对象
DatagramSocket datagramSocket = new DatagramSocket(8888);
// 创建发送线程 接收线程并启动
new Thread(new SenderTask(datagramSocket,"127.0.0.1",9999)).start();
// 创建接收线程
new Thread(new ReceiveTask(datagramSocket)).start();
}
}
AnotherPerson:
public class AnotherPerson {
public static void main(String[] args) throws IOException {
// 创建DatagramSocket对象
DatagramSocket datagramSocket = new DatagramSocket(9999);
// 创建发送线程 接收线程并启动
new Thread(new SenderTask(datagramSocket, "127.0.0.1", 8888)).start();
// 创建接收线程
new Thread(new ReceiveTask(datagramSocket)).start();
}
}
TCP
客户端步骤
-
创建客户端Socket对象(
Socket
) -
从socket中获取输入输出流
-
利用输出输出流进行读写操作
-
释放资源
close
服务端步骤
-
创建服务端的socket对象(
ServerSocket
) -
通过
accept
建立连接, 得到socket对象 -
从socket中得到输入输出流
-
利用输入输出流进行读写操作
-
释放资源
Socket
此类实现客户端套接字
构造方法:
Socket(String host, int port)
// 创建一个流套接字并将其连接到指定主机上的指定端口号。
注:这里的host
指的是目的主机的指定端口
成员方法:
ServerSocket
此类实现服务器套接字
构造方法:
ServerSocket(int port)
// 创建绑定到特定端口的服务器套接字。
成员方法:
举例
版本1:客户端发送消息,服务端接收并打印
注意事项:
- 先启动服务端,后启动客户端
- 端口号不能重复,如果重复占用的话,换一个端口号
eg:
客户端Client:
public class Client {
public static void main(String[] args) throws IOException {
// 1. 创建客户端Socket对象
// Socket(String host, int port)
Socket socket = new Socket("127.0.0.1", 9999);
// 2. 从socket中获取输入输出流
OutputStream out = socket.getOutputStream();
// 3. 利用输出输出流进行读写操作
out.write("hello".getBytes());
// 4. 释放资源close
socket.close();
}
}
服务端Server:
public class Server {
public static void main(String[] args) throws IOException {
// 1. 创建服务端的socket对象(ServerSocket)
// ServerSocket(int port)
ServerSocket serverSocket = new ServerSocket(9999);
// 2. 通过accept建立连接, 得到socket对象
Socket socket = serverSocket.accept();
// 3. 从socket中得到输入输出流
InputStream inputStream = socket.getInputStream();
// 4. 利用输入输出流进行读写操作
byte[] bytes = new byte[1024];
int readCount = inputStream.read(bytes);
System.out.println(new String(bytes, 0, readCount));
// 5. 释放资源
serverSocket.close();
socket.close();
}
}
版本2:多个客户端发送,服务端接收(多线程处理)
eg:
客户端Client:
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端的socket对象
Socket socket = new Socket("127.0.0.1",9999);
// 创建scanner对象
Scanner scanner = new Scanner(System.in);
// while循环
while(true){
String s = scanner.nextLine();
// 从socket对象中获取输入输出流
// 字节流
OutputStream out = socket.getOutputStream();
// 转换成字符流
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(out);
// write
// out.write(s.getBytes());
outputStreamWriter.write(s);
// flush
outputStreamWriter.flush();
}
}
}
服务端Server:
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端的socket对象
ServerSocket serverSocket = new ServerSocket(9999);
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
// while循环
while (true){
// 通过accept方法建立连接
Socket socket = serverSocket.accept();
// 向线程池中提交任务
pool.submit(new ConnectTask(socket));
// 使用多线程
// new Thread(new ConnectTask(socket)).start();
}
}
}
// 多线程改进
class ConnectTask implements Runnable{
// 定义成员变量
Socket socket;
public ConnectTask(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
// 读取数据
while (true){
try {
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
int readCount = in.read(bytes);
String s = new String(bytes, 0, readCount);
System.out.println("接收到了来自" + socket.getLocalAddress()
+ "端口号是:" + socket.getPort()
+ "线程名是:" + Thread.currentThread().getName()
+ ",内容是:" + s);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
版本3:客户端发送对象(序列化),服务端接收
ObjectOutputStream
将 Java 对象的基本数据类型和图形写入 OutputStream
。
可以使用 ObjectInputStream
读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。
eg:
客户端Client:
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端socket对象
Socket socket = new Socket("127.0.0.1", 12300);
// 从socket中获取输出流
OutputStream out = socket.getOutputStream();
// 把输出流进行包装,包装成序列化流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(out);
// 创建学生对象
Student student = new Student("zs", 20);
// writeObject
objectOutputStream.writeObject(student);
// close
objectOutputStream.close();
socket.close();
}
}
服务端Server:
public class Server {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 创建客户端的socket对象
ServerSocket serverSocket = new ServerSocket(12300);
// accept方法建立连接,得到socket对象
Socket socket = serverSocket.accept();
// 得到输入流
InputStream in = socket.getInputStream();
// 把输入流进行包装 ObjectInputStream
ObjectInputStream objectInputStream = new ObjectInputStream(in);
// readObject
Object o = objectInputStream.readObject();
// 打印
System.out.println(o);
// close
objectInputStream.close();
socket.close();
serverSocket.close();
}
}
版本4:客户端上传文件到服务端
eg:
客户端Client:
// 客户端上传文件到服务端
public class Client {
public static void main(String[] args) throws IOException {
// 创建客户端的socket对象
Socket socket = new Socket("127.0.0.1", 12345);
// 创建自己的输入流对象
FileInputStream fileInputStream =
new FileInputStream("D:\\Java_test\\testPhoto.jpg");
// 通过自己的输入流读取文件信息
OutputStream outputStream = socket.getOutputStream();
// 通过socket获取输出流对象
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = fileInputStream.read(bytes)) != -1) {
// 边读边写
outputStream.write(bytes, 0, readCount);
}
// 释放资源
fileInputStream.close();
outputStream.close();
socket.close();
}
}
服务端Server:
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端的socket对象
ServerSocket serverSocket = new ServerSocket(12345);
// 创建自己的输出流对象
FileOutputStream fileOutputStream =
new FileOutputStream("D:\\darkNight.jpg");
// accept方法建立连接 得到socket对象
Socket socket = serverSocket.accept();
// 从socket中获取输入流
InputStream in = socket.getInputStream();
// 边读边写
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = in.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, readCount);
}
// 释放
serverSocket.close();
socket.close();
fileOutputStream.close();
in.close();
}
}
Socket的半关闭
版本4plus:
eg:
客户端Client:
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端的socket对象
ServerSocket serverSocket = new ServerSocket(12345);
// 创建自己的输出流对象
FileOutputStream fileOutputStream =
new FileOutputStream("D:\\darkNight.jpg");
// accept方法建立连接 得到socket对象
Socket socket = serverSocket.accept();
// 从socket中获取输入流
InputStream in = socket.getInputStream();
// 边读边写
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = in.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, readCount);
}
OutputStream outputStream = socket.getOutputStream();
outputStream.write("文件上传成功".getBytes());
// 释放
serverSocket.close();
socket.close();
fileOutputStream.close();
in.close();
}
}
服务端Server:
public class Server {
public static void main(String[] args) throws IOException {
// 创建服务端的socket对象
ServerSocket serverSocket = new ServerSocket(12345);
// 创建自己的输出流对象
FileOutputStream fileOutputStream =
new FileOutputStream("D:\\darkNight.jpg");
// accept方法建立连接 得到socket对象
Socket socket = serverSocket.accept();
// 从socket中获取输入流
InputStream in = socket.getInputStream();
// 边读边写
int readCount;
byte[] bytes = new byte[1024];
while ((readCount = in.read(bytes)) != -1) {
fileOutputStream.write(bytes, 0, readCount);
}
OutputStream outputStream = socket.getOutputStream();
outputStream.write("文件上传成功".getBytes());
// 释放
serverSocket.close();
socket.close();
fileOutputStream.close();
in.close();
}
}