文章目录
- 前言
- 网络编程
- 网络编程三要素
- IP
- InetAddress
- 端口号
- 协议
- OSI七层模型
- TCP/IP模型
- UDP协议
- TCP协议
- 综合练习
- 多发多收
- 接收和反馈
- 上传文件
- 上传文件重名问题
- 上传文件多线程问题
- 上传文件线程池问题
- BS架构
- 总结
前言
博客仅记录个人学习进度和一些查缺补漏。
学习内容:BV17F411T7Ao
网络编程
详见408《计算机网络》
在网络通讯协议下,不同的计算机上运行的程序进行数据传输。
网络编程三要素
IP
C类私有地址192.168.0.0
InetAddress
JAVA中提供的ip类,但是没有对外的构造方法,只有先获取自己的IP再更改。
例如:
也可以根据计算机名字获取:
例如:获取InetAddress对象
端口号
一个端口号只能被一个应用程序使用
协议
计算机网络中,连接和通信的规则被称为网络通信协议
OSI七层模型
TCP/IP模型
UDP协议
发送数据:
使用套接字DatagramSocket来发送信息,新建对象的时候需要绑定端口
例如:套接字就是快递小哥,具体的消息内容是将字符数组打包到DatagramPacket,并指定长度、地址和端口号然后,就像填写快递单一样。
接收数据:
例如:接收的时候一定要使用对应端口的快递小哥
接收数据也需要使用字符数组
注意,要先运行接收端,再运行发送端,接收方法receive会自动轮询阻塞等待接收消息,发送端口是随机指定的,并不需要手动指定。
例如:持续发送直到特殊信息出现
例如:持续接收
例如:重复运行多个当前类,形成聊天室
单播:1对1
组播:1对多
广播:1对全
例如:组播的实现,需要使用MulticastSocket组播套接字,并且发送的时候需要使用组播地址(组播地址详见计算机网络)
例如:组播的实现,需要让接收端的主机加入组播中
TCP协议
TCP再发送数据之前一定要建立连接
例如:客户端,发出数据(output)释放资源的时候要先关闭链接,再关闭客户端
例如:服务端,接收数据,会一直轮询accept方法等待链接。
注意,因为是通过字节流传输,没有指明编码表,所以接受中文的时候会出现乱码
需要在服务器中作出改动,将字节流变成字符流,即转换流
为增加阅读效率,可以再加入缓冲流
三次握手,四次挥手,详见408计算机网络。
综合练习
多发多收
package com.itheima.demo7;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientTest {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
Socket socket = new Socket("127.0.0.1", 10001);
OutputStream os = socket.getOutputStream();
// 发送数据
while (true) {
System.out.println("发送数据:");
//注意这个\r\n只有在客户端读取整行时添加,第一种方法不用添加。
String str = sc.nextLine() + "\r\n";
if (str.equals("886")) {
break;
}
os.write(str.getBytes());
}
os.close();
socket.close();
}
}
package com.itheima.demo7;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
//本机IP无需指定,只需要指定端口
//ServerSocket就是本机专属快递员,Socket相当于管道,管道谁都能用,但管道送过来了还得指定的快递员来送达指定端口
ServerSocket serverSocket = new ServerSocket(10001);
Socket socket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//第一种单字读取
/*int b;
while ((b = bufferedReader.read()) != -1) {
System.out.print((char) b);
}*/
//第二种整行读取
String str = "";
while ((str = bufferedReader.readLine()) != null){
System.out.println(str);
}
socket.close();
}
}
接收和反馈
package com.itheima.demo7;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class ClientTest {
public static void main(String[] args) throws IOException {
Scanner sc = new Scanner(System.in);
Socket socket = new Socket("127.0.0.1", 10001);
OutputStream os = socket.getOutputStream();
// 发送数据
while (true) {
System.out.println("发送数据:");
String str = sc.nextLine() + "\r\n";
if (str.equals("886\r\n")) {
break;
}
os.write(str.getBytes());
}
//结束标记
socket.shutdownOutput();
//接收反馈并输出
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String res = "";
while ((res = bufferedReader.readLine()) != null) {
System.out.println(res);
}
os.close();
socket.close();
}
}
package com.itheima.demo7;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
//本机IP无需指定,只需要指定端口
//ServerSocket就是本机专属快递员,Socket相当于管道,管道谁都能用,但管道送过来了还得指定的快递员来送达指定端口
ServerSocket serverSocket = new ServerSocket(10001);
Socket socket = serverSocket.accept();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String str = "";
while ((str = bufferedReader.readLine()) != null){
System.out.println(str);
}
System.out.println("接收已结束");
//发送反馈
String res = "服务已结束\r\n";
OutputStream outputStream = socket.getOutputStream();
outputStream.write(res.getBytes());
socket.close();
}
}
上传文件
package com.itheima.demo7;
import java.io.*;
import java.net.Socket;
public class ClientTest {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 10001);
// 发送文件数据
String ori_path = "F:\\abc\\def\\aaa.jpg";
File ori_file = new File(ori_path);
sentFile(ori_file, socket);
//接收反馈并输出
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String res = bufferedReader.readLine();
System.out.println(res);
socket.close();
}
public static void sentFile(File file, Socket socket) throws IOException {
if(file.isFile()) {
// 读取文件内容(buffer字节流)
BufferedInputStream bfi = new BufferedInputStream(new FileInputStream(file));
// 创建发送字节流
BufferedOutputStream bfo = new BufferedOutputStream(socket.getOutputStream());
int len;
byte[] bytes = new byte[1024];
while ((len = bfi.read(bytes)) != -1) {
bfo.write(bytes, 0, len);
}
// 记住一定要刷新
bfo.flush();
//文件结束标记
socket.shutdownOutput();
//bfo.close();
}
}
}
package com.itheima.demo7;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
//本机IP无需指定,只需要指定端口
//ServerSocket就是本机专属快递员,Socket相当于管道,管道谁都能用,但管道送过来了还得指定的快递员来送达指定端口
ServerSocket serverSocket = new ServerSocket(10001);
//持续监听,有人链接就把对方的套接字拿出来
Socket socket = serverSocket.accept();
//创建读者输入流
BufferedInputStream bfi = new BufferedInputStream(socket.getInputStream());
//创建写者输出流,指定位置
BufferedOutputStream bfo = new BufferedOutputStream(new FileOutputStream("F:\\abc\\bbb.jpg"));
int len;
byte[] bytes = new byte[1024];
while ((len = bfi.read(bytes)) != -1) {
bfo.write(bytes, 0, len);
}
//记住一定要刷新
bfo.flush();
System.out.println("接收已结束");
//发送反馈
String res = "服务已结束\r\n";
OutputStream outputStream = socket.getOutputStream();
outputStream.write(res.getBytes());
socket.close();
serverSocket.close();
}
}
上传文件重名问题
使用UUID实现
//创建写者输出流,指定位置
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bfo = new BufferedOutputStream(new FileOutputStream("F:\\abc\\" + name +".jpg"));
上传文件多线程问题
package com.itheima.demo7;
import java.io.*;
import java.net.Socket;
import java.util.UUID;
public class ServerRunnable implements Runnable{
private Socket socket;
public ServerRunnable(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 创建读者输入流
BufferedInputStream bfi = new BufferedInputStream(socket.getInputStream());
// 创建写者输出流,指定位置
String name = UUID.randomUUID().toString().replace("-", "");
BufferedOutputStream bfo = new BufferedOutputStream(new FileOutputStream("F:\\abc\\" + name +".jpg"));
int len;
byte[] bytes = new byte[1024];
while ((len = bfi.read(bytes)) != -1) {
bfo.write(bytes, 0, len);
}
// 记住一定要刷新
bfo.flush();
System.out.println("接收已结束");
// 发送反馈
String res = "服务已结束\r\n";
OutputStream outputStream = socket.getOutputStream();
outputStream.write(res.getBytes());
} catch (IOException e) {
throw new RuntimeException(e);
}finally {
try {
if (socket != null){
socket.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
package com.itheima.demo7;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTest {
public static void main(String[] args) throws IOException {
// 本机IP无需指定,只需要指定端口
// ServerSocket就是本机专属快递员,Socket相当于管道,管道谁都能用,但管道送过来了还得指定的快递员来送达指定端口
ServerSocket serverSocket = new ServerSocket(10001);
while (true) {
// 持续监听,有人链接就把对方的套接字拿出来
Socket socket = serverSocket.accept();
new Thread(new ServerRunnable(socket)).start();
}
// 服务器关闭
//serverSocket.close();
}
}
package com.itheima.demo7;
import java.io.*;
import java.net.Socket;
public class ClientTest {
public static void main(String[] args) throws IOException {
// 创建套接字
Socket socket = new Socket("127.0.0.1", 10001);
// 发送文件数据
String ori_path = "F:\\abc\\def\\aaa.jpg";
File ori_file = new File(ori_path);
sentFile(ori_file, socket);
// 接收反馈并输出
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String res = bufferedReader.readLine();
System.out.println(res);
socket.close();
}
public static void sentFile(File file, Socket socket) throws IOException {
if(file.isFile()) {
// 读取文件内容(buffer字节流)
BufferedInputStream bfi = new BufferedInputStream(new FileInputStream(file));
// 创建发送字节流
BufferedOutputStream bfo = new BufferedOutputStream(socket.getOutputStream());
int len;
byte[] bytes = new byte[1024];
while ((len = bfi.read(bytes)) != -1) {
bfo.write(bytes, 0, len);
}
// 记住一定要刷新
bfo.flush();
// 文件结束标记
socket.shutdownOutput();
// 注意bfo是通过套接字创建的,关闭bfo会导致套接字关闭
//bfo.close();
}
}
}
上传文件线程池问题
package com.itheima.demo7;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;
public class ServerTest {
public static void main(String[] args) throws IOException {
//本机IP无需指定,只需要指定端口
//ServerSocket就是本机专属快递员,Socket相当于管道,管道谁都能用,但管道送过来了还得指定的快递员来送达指定端口
ServerSocket serverSocket = new ServerSocket(10001);
// 定义线程池
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3 // 最大核心线程数
, 16 // 最大总线程数
, 60 // 临时线程存活时间
, TimeUnit.SECONDS // 时间单位
, new ArrayBlockingQueue<>(3) // 等待队列大小
, Executors.defaultThreadFactory() // 线程工厂
, new ThreadPoolExecutor.AbortPolicy()); // 阻塞队列处理方式
while (true) {
//持续监听,有人链接就把对方的套接字拿出来
Socket socket = serverSocket.accept();
//提交任务给线程队列(工会)
threadPoolExecutor.submit(new ServerRunnable(socket));
}
}
}
BS架构
package com.itheima.demo7;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class SocketTest {
public static void main(String[] args) throws IOException {
// 创建服务器套接字
ServerSocket ss = new ServerSocket(10001);
// 监听等待套接字
Socket s = ss.accept();
// 有套接字连接后,接收套接字的输出
BufferedInputStream bfi = new BufferedInputStream(s.getInputStream());
int b;
while ((b = bfi.read()) != -1) {
System.out.print((char)b);
}
s.close();
ss.close();
}
}
总结
以前学套接字的时候完全搞不懂,怎么都写不出来就想着赶紧搞完赶紧出去玩,最后网上抄了一个,还不会运行,现在全心全意写代码学习感觉就完全不一样的,心态真的很关键。