软件架构
C/S架构: 即Client/Server(客户端/服务器端)架构,QQ、迅雷。
B/S架构: 即Browser/Server(浏览器端/服务器端)架构,简化了系统的开发,维护和使用。谷歌、火狐。
B/S架构与C/S架构的区别:
C/S架构基于局域网的,而B/S架构基于广域网的基础上。硬件环境不同,
C/S建立在专用网络上,小范围的网络,可以专门的服务器提供数据连接和数据交换
C/S一般面向的固定用户群体,对信息的安全控制较高较高一点。
对程序架构不同用户接口不同:
C/S架构大多建立在Windows平台上,B/S建立在浏览器上,不仅可以应用在Windows平台上,也可以应用在Unix/Linux等平台上。
两种架构各有优劣,但是无论使用哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机通信的程序。
网络通信协议
网络通信协议:通信协议是计算机通信必须遵从的一种规则,协议中对数据的传输格式、传输的速率、传输的步骤等都做了统一的规定,通信双方必须同时遵守,最终实现数据的正常传输和交换。
TCP/IP:传输控制协议(TCP)/因特网互联协议(IP),他们俩定义了计算机如何联网、数据如何交换和传输的标准。它的内部包含了一系列用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫下一层所提供的协议来完成自己的请求。
协议分类
TCP协议:传输控制协议,该协议是面向连接的通信协议,即数据传输前,在发送端和接收端先建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠的无差错的数据传输。
三次握手:在数据发送的准备阶段,客户端和服务器之间通过三次交互,保证连接的可靠性。
通过三次握手,建立连接后,客户端和服务器端就可以进行数据传输了。由于这种面相连接的特性,TCP协议能够数据传输的安全。所以应用挺广泛的,下载文件等。
UDP:用户数据报协议(User DataProgram Protocal),他是一个面相无连接的协议。在进行数据传输时,不需要建立连接。不管对方在不在,直接将数据、数据源和目的地封装到数据包中,发送过去。每次发送的数据不能很大,限制在64K以内,数据有可能丢失,数据传输时不安全,速度快,QQ聊天
网络编程三要素【了解】
协议:计算机通信必须遵守的规则。
IP地址:互联网协议地址,俗称IP,IP地址用来给网络中的计算机编订一个唯一的编号。
端口号:端口号就是唯一标识设备中的进程(应用程序)。IP地址是唯一标识网络中的设备。
网络编程三要素
协议:计算机中网络通信必须遵守的规则。
IP地址:互联网协议地址(Internet Protocol Address) ,俗称IP,给网络中的计算机设备编订一个唯一的编号,好比人的身份证号。
IP地址分类:
IPv4:是一个32位的二进制数,通常被分为4个字节,表示形式:a.b.c.d ,例如:192.168.53.245 ,其中a.b.c.d都是一个0~255之间的十进制的整数,那么最多能够表示42亿个。
IPv6:为了扩大地址空间,通过IPv6重新定义地址空间,采用128位地址长度,每16个字节分为一组,分成8组十六进制数 ,表示形式:ABCD:EF01:2345:6789:ABCD:EF01:2345:6789,号称互联网中的任何一粒沙子能够编写进去,解决了地址不够的问题。
通过DOS命名查看本机的IP地址:ipconfig
检查网络连接是否正常:ping ip
地址特殊的IP地址:
本机的IP地址:127.0.0.1 localhost 通过hosts文件,可以自定义自己的域名
端口号:如何区分在网络通信中打开的这些进程(应用程序)?通过端口号可以准确的找到彼此。
端口号:用两个字节表示的整数,它的取值范围是0~65535。 其中,0~1023之间的的端口号被一些知名的应用和网络服务占用了。普通的常规的应用程序只能使用从1024以上的端口号, 如果端口号被另外一个服务或者应用程序占用了,会导致当前程序启动失败。
常用的端口号:
Tomcat应用服务器:8080
HTTP服务器:80 www.baidu.com:80 80端口可以选择省略不写
数据库服务器:MYSQL:3306 Oracle:1521
TCP通信
目的:实现两台计算机之间的交互,通信两端要严格区分客户端和服务器端口
两端通信的步骤:
- 服务端需要启动,等待客户端链接
- 客户端需要主动地链接服务器端,链接成功才能通信,服务器端口不可以主动链接客户端
在java中,提供两个类,实现TCP通信:
- 客户端:创建Socket对象,创建之后就相当于创建了客户端,向服务器端发送链接请求,服务器端回响一个请求表示已经接受,两者就建立了通信
- 服务器端:创建ServerSocket对象,创建之后就相当于,开启了一个服务,等待客户端链接
Socket类
Socket类,实现客户端套接字,套接字指的是两台设备之间通讯的端点,
构造方法:
public Socket(String host,int port);—>创建套接字对象并连接到指定主机的指定端口号,如果指定了host是一个null,那么就相当于与指定地址为回送地址【127.x.x.x】,是本机的回送地址,主要用于网络软件测试使用,本机上进程间的通信。就不会进行网络传输
成员方法: - public InputStream getInputStream();---->获取套接字的输入流
- public OutputStream getOutputStream();—>获取套接字输出流
- public void close();—>关闭套接字
- public void shoutdownOutput();—>禁用套接字输出流
- 任何先前写入的数据都将会被发送,最后终止输出流
ServerSocket类
ServerSocket类实现了服务器端套接字,该对象等待通过网络的请求
构造方法:
ServerSocket(int port);—>使用该构造方法子创建ServerSocket创建对象时,就可以将其绑定到一个指定的端口号上面,参数port就是端口号
成员方法:
public Socket accept();—>监听并接受连接,返回一个新的Socket对象,用于和用户端实现通信。
代码示例
服务器端:
/*
服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
服务器端的实现步骤:
1. 创建ServerSocket服务器端对象和系统指定的端口号
2. 使用ServerSocket对象中的方法accept,获取客户端请求的连接,拿到Socket对象
3. 使用Socket对象中的方法getInputStream(),可以获取网络字节输入流对象
4. 使用网络字节输入流对象中的方法read,读取客户端发送的数据
5. 使用Socket对象中的方法getOutputStream(),获取网络字节输出流对象
6. 使用网络字节输出流对象中的方法write,给客户端回写数据
7. 释放资源(ServerSocket,Socket)
服务器端必须明确到底是哪一个客户端给你发送的请求
可以使用accept方法来获取到客户端对象Socket
通过accept方法来监听接收此套接字的连接。
*/
public static void main(String[] args) throws IOException {
//1. 创建ServerSocket服务器端对象和系统指定的端口号
ServerSocket server = new ServerSocket(6666);
//2. 使用ServerSocket对象中的方法accept,获取客户端请求的连接,拿到Socket对象
Socket socket = server.accept();
//3. 使用Socket对象中的方法getInputStream(),可以获取网络字节输入流对象
InputStream is = socket.getInputStream();
//4. 使用网络字节输入流对象中的方法read,读取客户端发送的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);//
// 打印接收客户端发送的数据
System.out.println(new String(bytes,0,len));
//5. 使用Socket对象中的方法getOutputStream(),获取网络字节输出流对象
OutputStream os = socket.getOutputStream();
//6. 使用网络字节输出流对象中的方法write,给客户端回写数据
os.write("我收到了你的请求消息".getBytes());
//7. 释放资源(ServerSocket,Socket)
socket.close();
server.close();
}
客户端:
/*
实现步骤:
1. 创建一个Socket客户端对象,构造方法中绑定服务器的ip地址和端口号
2. 使用Socket对象中的方法getOutputStream获取网络字节输出流对象
3. 使用网络字节输出流对象中的方法write,给服务器发送数据
4. 使用Socket对象中的方法getInputStream()获取网络字节输入流对象
5. 使用网络字节输入流对象调用read方法,读取服务器回写的数据
6. 释放资源(Socket)
java.net.ConnectException: Connection refused: connect
注意事项:
1. 客户端和服务端进行交互的时候,必须使用Socket中提供的网络流,不能使用创建的流对象
2. 当我们创建客户端对象Socket的时候,就会去请求服务器和服务器进行三次握手建立网络连接
这时如果服务器端没有启动,那么程序就会抛出 java.net.ConnectException: Connection refused: connect
如果服务器启动了,那么就可以进行交互。
*/
public static void main(String[] args) throws IOException {
// 1. 创建一个Socket客户端对象,构造方法中绑定服务器的ip地址和端口号
Socket socket = new Socket("127.0.0.1", 6666);
// 2. 使用Socket对象中的方法getOutputStream获取网络字节输出流对象
OutputStream os = socket.getOutputStream();
// 3. 使用网络字节输出流对象中的方法write,给服务器发送数据
os.write("你在干什么".getBytes());
// 4. 使用Socket对象中的方法getInputStream()获取网络字节输入流对象
InputStream is = socket.getInputStream();
// 5. 使用网络字节输入流对象调用read方法,读取服务器回写的数据
byte[] bytes = new byte[1024];
int len = is.read(bytes);// 有效字节个数
// 打印下服务器回传的数据
System.out.println(new String(bytes,0,len));
// 6. 释放资源
socket.close();
}
文件上传案例:
步骤:
1.在客户端当中,通过输入流,从硬盘当中读取文件数据到内存
2.在客户端当中,通过输出流,写入文件数据到服务器端中
3.在服务器端中,通过输入流,读取文件数据到服务器端中
4.在服务器端中,通过输出流,写入文件数据到服务器硬盘当中
服务器端:
public static void main(String[] args) throws IOException {
// 1. 创建一个服务器端ServerSocket
ServerSocket server = new ServerSocket(9999);
// 2. 使用serverSocket对象中的方法accept,获取客户端Socket对象
Socket socket = server.accept();
// 3. 通过socket对象获取网络字节输入流
InputStream is = socket.getInputStream();
// 4. 判断文件夹是否存在
File file = new File("E:\\documents\\day31_Net");
if (!file.exists()) {
file.mkdirs();
}
// 5. 创建一个本地的文件字节输出流对象,构造方法绑定输出的目的地
FileOutputStream fos = new FileOutputStream(file + "\\3.gif");
// 6. 通过网络字节输入流读取客户端发送的数据,再次通过本地的字节输出流写入文件中
int len = 0;// 记录读取的有效的字节个数
byte[] bytes = new byte[1024];
while ((len = is.read(bytes)) != -1) {
//通过write方法写入本地的文件中
fos.write(bytes,0,len);
}
// 7. 释放资源
fos.close();
socket.close();
server.close();
}
客户端:
public static void main(String[] args) throws IOException {
// 1. 先创建一个本地的字节输入流,绑定读取的数据源
FileInputStream fis = new FileInputStream("C:\\Users\\admin\\Desktop\\3.gif");
// 2. 创建一个网络字节输出流 往服务器发送数据
// 2.1 构建一个Socket客户端对象
Socket socket = new Socket("127.0.0.1", 9999);
// 2.2 从socket对象中获取网络字节输出流
OutputStream os = socket.getOutputStream();
// 2.3 通过本地字节输入流读取数据,再通过网络字节数输出流发送给服务器端
byte[] bytes = new byte[1024];
int len = 0;// 记录读取的有效字节个数
while ((len = fis.read(bytes)) != -1) {
// 调用write方法发送数据
os.write(bytes, 0, len);
}
// 释放资源
socket.close();
}
文件下载案例
服务器端:
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("E:\\3.gif\\文件上传.png");
ServerSocket server = new ServerSocket(9999);
Socket socket = server.accept();
OutputStream os = socket.getOutputStream();
byte[] bytes = new byte[1024];
int len=0;
while ((len=fis.read(bytes))!=-1){
os.write(bytes,0,len);
}
os.close();
socket.close();
fis.close();
}
客户端:
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1",9999);
InputStream is = socket.getInputStream();
File file = new File("C:\\Users\\99213\\Desktop\\下载地址");
if(!file.exists()){
file.mkdirs();
}
FileOutputStream fos = new FileOutputStream(file + "\\下载副本.png");
int len=0;
byte[] bytes = new byte[1024];
while ((len=is.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.close();
is.close();
socket.close();
}
文件上传优化
文件名称需要优化
服务端,保存文件时如果名称固定,那么最终会导致服务器硬盘,只会保留一个文件,对上传的文件名称优化。
//文件名称定义规则:
"beautiful" + System.currentTimeMillis() + new Random().nextInt(1000000) + ".jpg";
服务器端接收文件的优化
服务器端,接收一个文件之后就关闭了,后面的其他客户端无法继续上传文件,使用循环进行改进,可以不断的接收不同的客户端传输过来的文件。
// 使用循环
while(true) {
Socket socket = serverSocket.accept();
....
....
}
服务器端接收客户端文件的效率的优化
服务器端,在接收文件的时候,加入某个客户端给你传输一个大文件,此时就不能再接收其他用户的文件,所以可以用多线程技术优化接收效率。
while(true) {
Socket socket = serverSocket.accept();
// 使用多线程技术,提高程序的效率
// 有一个客户端上传文件,就开启一个线程,完成文件的上传
new Thread(new Runnable() {
// 重写run方法,设定线程的任务
@Override
public void run() {
// 使用网络字节输入流对象
InputStream is = socket.getInputStream();
// 指定一个目录
File file = new File("D:\\upload");
if(!file.exits()) {
file.mkdirs();
}
// 防止同名的文件被覆盖
String filename = "beautiful" + System.currentTimeMillis() + new Random().nextInt(1000000) + ".jpg";
// 构建一个本地的文件字节输出流对象
new FileOutputStream(file + "\\" + filename);
// 一读一写完成文件的上传,最终把文件写入到服务器端的硬盘当中。
.....
}
}).start();
}
// 服务端不关闭
// serverSocket.close();