前言:
概述:在网络通信协议下,不同计算机上运行的程序,进行数据传输
比如:通信,视频通话,网游,邮件等
只要是计算机之间通过网络进行数据传输,就有网络编程的存在
(下面单纯是在Java基础中了解了一下网络编程,感觉理解比较浅显,深入的知识可能得学Netty)
软件结构:
C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、红蜘蛛、飞秋等软件。
B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有IE、谷歌、火狐等。
简单了解一下即可,知道在Java开发中大多情况都是B/S结构
通信三要素
第一个要素:[IP地址]:计算机的唯一标识,用于两台计算机之间的连接
互联网协议地址(Internet Protocol Address),俗称IP
计算机的唯一标识
这个ip地址其实是会动态变化得,当你在一个局域网中连上网得时候,自动会被分配一个ip地址
特殊的网址:代表的是本机地址,到了哪里都不会变,代表自己
127.0.0.1 -> 固定不变
localhost注意这两个ip地址都是指向自己的主机的,不过这两个还是不同的原,如果处理不好可能也会发送跨域问题。
第二个要素:[协议]
TCP:面向连接协议
需要先确认连接,才能进行数据交互
三次握手:
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
好处:数据安全,能给数据的传输提供一个安全的传输环境
坏处:效率低
UDP:面向无连接协议
好处:效率高
坏处:传输的数据不安全,容易丢失数据包
第三个要素: [端口号]
每一个应用程序的唯一标识
举个例子来理解这句话,比如我们电脑开了两个应用程序,qq,微信,
这两个应用程序会各自占用一个端口号,别的主机也开了这两个程序,别的主机想和我们发消息的话,微信发的信息,就会通过我们电脑微信占用的端口号传给上层应用
用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
TCP协议中的三次握手和四次挥手:
三次握手:
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。
经过三次握手,客户端就可以和服务端建立连接
为什么要三次握手?不是两次?
防止已失效的连接请求导致资源浪费和网络拥堵 GPT是这么说的,感觉讲的不是很明白
我来举一个例子
假设现在只有两次握手
客户端发送了一个连接请求给服务端,这个请求报文并没有丢失,只是因为网络延迟在某个网络结点滞留了很久,导致过了连接释放以后的时间,然后这个时候服务端才收到请求,服务端以为这是一个新的请求,就开始等待客户端的数据并且给客户端发送了一个确认报文,可是在客户端这边连接释放的时间已经过了,自然不会理会服务端发生的确认报文,这个时候,服务端就会等待,导致浪费资源。
采用“三次握手”的办法可以防止上述现象发生。例如刚才那种情况,client不会向server的确认发出确认。server由于收不到确认,就知道client并没有要求建立连接。"
这就很明白了,防止了服务器端的一直等待而浪费资源。
四次挥手:
- 第一次挥手:客户端向服务器端提出结束连接,让服务器做最后的准备工作。此时,客户端处于半关闭状态,即表示不再向服务器发送数据了,但是还可以接受数据。
- 第二次挥手:服务器接收到客户端释放连接的请求后,会将最后的数据发给客户端。并告知上层的应用进程不再接收数据。
- 第三次挥手:服务器发送完数据后,会给客户端发送一个释放连接的报文。那么客户端接收后就知道可以正式释放连接了。
- 第四次挥手:客户端接收到服务器最后的释放连接报文后,要回复一个彻底断开的报文。这样服务器收到后才会彻底释放连接。这里客户端,发送完最后的报文后,会等待2MSL,因为有可能服务器没有收到最后的报文,那么服务器迟迟没收到,就会再次给客户端发送释放连接的报文,此时客户端在等待时间范围内接收到,会重新发送最后的报文,并重新计时。如果等待2MSL后,没有收到,那么彻底断开。
TCP协议编程:
在了解TCP协议编程的之前,需要先了解两个对象:Socket和ServerSocket对象
秉承着在Java中万物皆是对象的理解方式:
-
Socket
:在TCP协议编程中,Socket
类代表客户端的套接字。通过Socket
对象,客户端可以与服务器建立连接,发送和接收数据。客户端通过Socket
与服务器进行通信,发送请求并接收响应。Socket
类提供了许多方法来管理连接、发送和接收数据等操作。 -
ServerSocket
:ServerSocket
类代表服务器端的套接字。服务器端通过ServerSocket
对象监听客户端的连接请求,一旦有客户端连接,ServerSocket
会创建一个新的Socket
对象来处理和该客户端的通信。通过ServerSocket
,服务器端可以接受客户端的连接,并处理客户端发送的请求。
模拟客户端和服务端的交互:
编写客户端:
步骤:
1:创建Socket对象
2:调用Socket对象的getOutputStream想服务端发请求
3:调用socket中的getInputStream来读取服务端返回的数据
4:关流
public class Send {
public static void main(String[] args) throws Exception {
//1.创建Socket对象,指明服务端的ip以及端口号
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 6666;
Socket socket = new Socket(ip,port);
//2.调用socket中的getOutputStream,往服务端发送请求
byte[] bytes = "我需要资源".getBytes();
OutputStream outputStream = socket.getOutputStream();
outputStream.write(bytes);
//3.调用socket中的getInputStream,读取服务端响应回来的数据
InputStream inputStream = socket.getInputStream();
byte[] bytes1 = new byte[1024];
int len = inputStream.read(bytes1);
System.out.println(new String(bytes1,0,len));
//4.关流
socket.close();
outputStream.close();
inputStream.close();
}
}
编写服务端:
步骤:
1:创建ServerSocket对象,设置端口号
2:调用ServerSocket中的accept方法创建Socket对象,用于接收客户端发送的数据
3:调用Socket对象的getOutputStream想服务端发请求
4:调用socket中的getInputStream来读取服务端返回的数据
5:关流
public class Server {
public static void main(String[] args) throws Exception{
//1:创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(6666);
//2:等待客户端的连接
Socket socket = serverSocket.accept();
//3: 获取客户端发过来的图片
InputStream inputStream = socket.getInputStream();
//4: UUID
String s = UUID.randomUUID().toString();
//5:将客户端发过来的图片读到磁盘中
FileOutputStream fileOutputStream = new FileOutputStream("E:\\java基础\\imgResult\\"+s+".jpg");
//6:边读边写
int len;
byte[] bytes = new byte[1024];
while((len = inputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
System.out.println("======以下代码是给客户端的响应结果======");
//7:接收客户端的响应结果
OutputStream outputStream = socket.getOutputStream();
outputStream.write("上传成功".getBytes());
//8:关流
serverSocket.close();
socket.close();
inputStream.close();
fileOutputStream.close();
outputStream.close();
}
}
文件上传操作:
这一个部分主要有两块内容
第一个主要通过模拟客户端发送文件给服务端,服务端接收文件写入磁盘加深对TCP协议编程的理解
第二个理解阿里云文件上传中upload方法
第一个部分:
首先要完成上面那个功能,也是需要去写客户端和服务端的代码的。
客户端代码:
步骤:
- 创建Socket对象
- 创建文件对象,读取文件
- 调用Socket对象的getOutputStream想服务端发请求
- 边读文件边写文件
- 调用socket中的getInputStream来读取服务端返回的数据
- 关流
public class Client {
public static void main(String[] args) throws Exception{
//1:创建Socket对象,指明服务端的ip以及端口号
InetAddress ip = InetAddress.getByName("127.0.0.1");
int port = 6666;
Socket socket = new Socket(ip,port);
//2:从磁盘文件中读取图片资源
FileInputStream file = new FileInputStream("E:\\java基础\\img\\1.jpg");
//3: 调用socket中的getOutputStream,往服务端发送请求
OutputStream outputStream = socket.getOutputStream();
//4: 边读边写
int len;
byte[] bytes = new byte[1024];
while((len = file.read(bytes))!=-1){
outputStream.write(bytes,0,len);
}
socket.shutdownOutput();
System.out.println("======以下代码是读取响应的结果======");
//5:调用socket中的getInputStream,读取服务端响应回来的数据
InputStream inputStream = socket.getInputStream();
byte[] bytes1 = new byte[1024];
int len1 = inputStream.read(bytes1);
System.out.println(new String(bytes1,0,len1));
//6:关流
socket.close();
outputStream.close();
inputStream.close();
file.close();
}
}
这里可以稍微说一下:socket.shutdownOutput();
用于关闭客户端并释放资源,如果你不主动关闭的话,服务端不知道你的文件已经传输完了,会一直等待。
服务端代码:
步骤:
- 创建ServerSocket对象,设置端口号
- 调用ServerSocket中的accept方法创建Socket对象,用于接收客户端发送的数据
- UUID
- 创建文件对象,将图片读到磁盘里
- 调用Socket对象的getOutputStream想服务端发请求
- 调用socket中的getInputStream来读取服务端返回的数据
- 关流
public class Server {
public static void main(String[] args) throws Exception{
//1:创建ServerSocket对象
ServerSocket serverSocket = new ServerSocket(6666);
//2:等待客户端的连接
Socket socket = serverSocket.accept();
//3: 获取客户端发过来的图片
InputStream inputStream = socket.getInputStream();
//4: UUID
String s = UUID.randomUUID().toString();
//5:将客户端发过来的图片读到磁盘中
FileOutputStream fileOutputStream = new FileOutputStream("E:\\java基础\\imgResult\\"+s+".jpg");
//6:边读边写
int len;
byte[] bytes = new byte[1024];
while((len = inputStream.read(bytes))!=-1){
fileOutputStream.write(bytes,0,len);
}
System.out.println("======以下代码是给客户端的响应结果======");
//7:接收客户端的响应结果
OutputStream outputStream = socket.getOutputStream();
outputStream.write("上传成功".getBytes());
//8:关流
serverSocket.close();
socket.close();
inputStream.close();
fileOutputStream.close();
outputStream.close();
}
}
第二个部分:
下面是阿里云OSS的客户端上传文件的代码(我第一次接触的时候看不懂,只会用)
@Component
public class AliOSSUtils {
@Autowired
private AliOSSproperties aliOSSproperties;
/**
* 实现上传图片到OSS
*/
public String upload(MultipartFile file) throws IOException {
String endpoint = aliOSSproperties.getEndpoint();
String accessKeyId = aliOSSproperties.getAccessKeyId();
String accessKeySecret = aliOSSproperties.getAccessKeySecret();
String bucketName = aliOSSproperties.getBucketName();
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
//文件访问路径
String url = endpoint.split("//")[0] + "//" + bucketName + "." + endpoint.split("//")[1] + "/" + fileName;
// 关闭ossClient
ossClient.shutdown();
return url;// 把上传到oss的路径返回
}
}
虽然这上面步骤和第一部分的客户端代码有点不同
不过本质差不多
第一步:也是获取OSS客户端对象(前面需要一点配置信息):
String endpoint = aliOSSproperties.getEndpoint();
String accessKeyId = aliOSSproperties.getAccessKeyId();
String accessKeySecret = aliOSSproperties.getAccessKeySecret();
String bucketName = aliOSSproperties.getBucketName();
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
第二步:获取文件对象:
// 获取上传的文件的输入流
InputStream inputStream = file.getInputStream();
第三步:UUID防止文件被覆盖:
// 避免文件覆盖
String originalFilename = file.getOriginalFilename();
String fileName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
首先说一下,我刚刚也尝试在第一部分的客户端代码中进行UUID的处理,发现不行,仔细看了一下,阿里云的文件对象是MultipartFile
MultipartFile
是Spring框架中的一个接口,主要用于接收上传的文件数据,并提供了一些方法用于操作上传的文件。
这个文件提供了一些api,所以能在客户端就UUID
第四步:上传文件:
//上传文件到 OSS
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
ossClient.putObject(bucketName, fileName, inputStream);
第五步:关闭ossClient:
ossClient.shutdown();