网络编程
软件架构
常见的软件架构C/S和B/S
- C/S架构(Client/Server),表示客户端/服务器的软件架构,例如英雄联盟等游戏需要下载客户端而不是可以在浏览器打开的这一类软件都是C/S架构
- B/S架构(Browser/Server),表示浏览器/服务器的软件架构,例如百度等需要浏览器打开的软件,都是B/S架构
注意,我们这里所讲的都是PC端,而不是手机软件的移动端。
C/S和B/S的区别:
- C/S在图形界面的优化以及运行速度强于B/S
- C/S需要运行的专门的客户端,所以并不支持跨平台
- B/S不需要专门的客户端,只要有浏览器就可以访问
- B/S是基于网页语言,与操作系统无关,所以可以跨平台
随着网页技术的不断发展以及浏览器的进步,许多C/S架构的软件都推出了相应的B/S架构软件
无论是C/S架构,还是B/S架构的软件,都离不开网络的支持
网络编程,就是在一定协议之下,实现两台计算机的通信的程序。
通信协议
通信协议,对两台计算机之间所传输数据的传输格式,传输步骤等做了统一的规定要求,通信双方必须同时遵守该协议
TCP/IP协议栈,传输控制协议/因特网互联协议,它是一系列网络协议的总和,是 构造网络通信的核心骨架。它是互联网最基本,使用最广泛的协议,它定义了计算机如何连入因特网,以及数据如何在计算机之间进行传输。
TCP/IP协议栈采用4层结构
-
应用层
主要负责应用程序的协议(HTTP、FTP)
-
传输层
主要使用网络程序进行通信,在进行网络通信时采用的协议是TCP或UDP
-
网络层
网络层是整个TCP/IP协议的核心,它主要用来将传输的数据进行分组,将分组的数据发送到目标计算机或网络(简单来说就是将传输的数据分批传输到指定主机)
-
链路层
链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议(光纤,网线)
各层都包含不同的通信协议,分别负责不同的通信功能
当通过http发起一个请求时,应用层、传输层、网络层和链路层的相关协议依次对该请求进行包装并携带对应的首部,最终在链路层生成以太网数据包,以太网数据包通过物理介质(网线,光纤)传输给对方主机,对方接收到数据包以后,然后再一层一层采用对应的协议进行拆包,最后把应用层数据交给应用程序处理
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7o8x37aB-1599134706784)(C:\Users\石原里美\AppData\Roaming\Typora\typora-user-images\image-20200903190858496.png)]
TCP和UDP
在Java中java.net包对常见的两种通信协议(TCP UDP)进行了封装和支持。以便我们可以直接使用相应的类和接口来进行网络开发。
-
UDP,用户数据报协议
UDP是无连接协议,在进行数据传输的时候,发送端与接受端不需要建立连接,这就导致无法保证接收端一定可以接收成功。简单来说就是发送端只管不停发送数据,至于谁接收,接收多少都不会影响发送端。
正因为如此,通常都会用于音频,视频和普通数据的传输。不适用于传输重要数据
在Java种我们主要使用的就是TCP协议
-
TCP、传输控制协议
TCP协议是面向连接的通信协议,传输数据时需要发送端与接收端建立连接之后才会传输数据
保证了两台计算机之间数据传输可靠,无差错
在TCP连接中,要经历“三次握手”的过程才可以传输数据
TCP的三次握手
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认
- 第二次握手,服务器向客户端会送一个相应,通知客户端收到了连接请求,如果客户端在等待一段时间后迟迟得不到相应,那么就会认定这次连接请求失败。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接
在完成三次握手之后,就说明客户端与服务器已经成功的建立了连接,可以安全,可靠的,无差错的传输数据
IP和端口号
IP地址,互联网协议地址(Internet Protocol Address)。IP地址用来给一个网络中的计算机设备做唯一
的编号
每一台计算机都有一个默认的本机IP地址----127.0.0.1(localhost)
为了方便连接和记忆,ip地址通常都有相对应的域名,例如百度www.baidu.com的ip地址180.101.49.11
端口号:网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,每个需要
网络通信的进程(应用程序),都会占用系统的一个端口号(服务器是计算机上的一个软件,所以同时也有相对应的进程)
在网络中,通过IP可以找到对应的主机设备,而通过端口号,可以找到这个主机中正在运行的一个应用
程序
在网络中,通过IP可以找到对应的主机设备,而通过端口号,可以找到这个主机中正在运行的一个应用
程序
我们下载的一些软件,在启动的时候,会默认占用特定的端口号,如果一个端口号被占用当前程序就会启动失败,要么关闭占用当前端口号的程序,要么就修改占用另外的端口号(建议使用这种方法)
TCP网络编程
概述
TCP通信协议下,能实现两台计算机之间的数据交互,并且它们要严格区分客户端(Client)与服务端(Server)
客户端和服务端通信的步骤·:
- 服务端先启动,并占用一个端口号,等待客户端的连接
- 客户端主动连接服务端,在连接成功后就可以进行数据的发送(服务端不会主动连接客户端)
在java中,对于这样基于TCP协议下连接通信的客户端和服务端,分别进行了抽象:
- java.net.Socket 类表示客户端
- java.net.ServerSocket 类表示服务端
使用 Socket 和 ServerSocket 进行的编程,也称为套接字编程。(翻译过来就是套接字的意思)
简单练习
1、传输视频
//客户端
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
//创建客户端 并且指定IP地址和端口号
Socket socket = new Socket("127.0.0.1",8887);
//创建一个字节输出流来进行数据输出
OutputStream out = socket.getOutputStream();
//创建一个文件输入流将对应视频读取
FileInputStream inputStream = new FileInputStream("D:\\Program Files\\JiJiDown\\Download\\青春.mp4");
//给输出流套一个缓冲区
BufferedOutputStream outputStream = new BufferedOutputStream(out);
@SuppressWarnings("unused")
int len = -1;
byte[] buf = new byte[1024];
//将数据读取进来
while((len = inputStream.read(buf))!=-1) {
//将读取进来的数据发送到服务端
outputStream.write(buf);
}
//刷新缓冲区和关闭流,客户端
outputStream.flush();
outputStream.close();
out.close();
socket.close();
}
}
//服务端
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
@SuppressWarnings("unused")
public static void main(String[] args) throws Exception {
//创建服务端
ServerSocket socket = new ServerSocket(8887);
//接收客户端发过来的数据,这个是阻塞的
//只有客户端发送数据过来以后才会往下执行,否则一直阻塞在这
//直到客户端发送数据过来
Socket socket2 = socket.accept();
//创建输入流
InputStream stream = socket2.getInputStream();
//套一个缓冲输入流
BufferedInputStream in = new BufferedInputStream(stream);
//创建一个文件输出流 并且共套一个缓冲输出流
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream("D:\\拷贝.mp4"));
int len = -1;
byte[] buf = new byte[1024];
//将接收的数据读取到数组中
while((len = in.read(buf))!= -1) {
//将数组中的数据写入到对应的文件中
out.write(buf);
}
//关闭流和服务端
out.close();
in.close();
stream.close();
socket.close();
}
2、传输对象
Light对象
import java.io.Serializable;
public class Light implements Serializable{
private static final long serialVersionUID = -5954880065668661351L;
private String name = "石原里美";
private int age = 33;
private String desc = "如今你依旧是我的光";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age + ", desc=" + desc ;
}
}
客户端
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Client {
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
//创建客户端 并且指定IP地址和端口号
Socket socket = new Socket("127.0.0.1",8887);
//创建一个字节输出流来进行数据输出
OutputStream out = socket.getOutputStream();
//创建一个文件输入流将对应视频读取
FileInputStream inputStream = new FileInputStream("D:\\Program Files\\JiJiDown\\Download\\青春.mp4");
//给输出流套一个缓冲区
BufferedOutputStream outputStream = new BufferedOutputStream(out);
@SuppressWarnings("unused")
int len = -1;
byte[] buf = new byte[1024];
//将数据读取进来
while((len = inputStream.read(buf))!=-1) {
//将读取进来的数据发送到服务端
outputStream.write(buf);
}
//刷新缓冲区和关闭流,客户端
outputStream.flush();
outputStream.close();
out.close();
socket.close();
}
}
服务端
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerLight {
@SuppressWarnings("resource")
public static void main(String[] args) throws Exception {
//创建服务端 端口号为8888
ServerSocket socket = new ServerSocket(8888);
//用来接收传输客户端传输过来的数据
Socket accept = socket.accept();
//创建字节输入流
InputStream in = accept.getInputStream();
//转化为对象流
ObjectInputStream inputStream = new ObjectInputStream(in);
//接收传输过来的对象并且转化为相应的数据类型
Light light = (Light) inputStream.readObject();
//创建一个文件输出流,并转化为对象输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("A.txt"));
//将对象写入到相应的文件中
objectOutputStream.writeObject(light);
objectOutputStream.flush();
//打印输出对象
System.out.println(light);
//关闭流和服务端
inputStream.close();
in.close();
socket.close();
}
}
对象输出流
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(“A.txt”));
//将对象写入到相应的文件中
objectOutputStream.writeObject(light);
objectOutputStream.flush();
//打印输出对象
System.out.println(light);
//关闭流和服务端
inputStream.close();
in.close();
socket.close();
}
}