软件结构
常见的软件结构有C/S和B/S
Client/Server(C/S结构),表示 客户端/服务器 的软件结构,例如QQ、微信、网盘客户端等,只要是需要我们下载安装,并且和服务器通信的这一类软件,都属于C/S的软件结构。
Browser/Server(B/S结构),表示 浏览器/服务器 的软件结构,例如淘宝网、京东商城等,只要是需要使用浏览器,并且和服务器通信的这一类软件,都属于B/S的软件结构
C/S和B/S各有优势:
C/S在图形的表现能力上以及运行的速度上肯定是强于B/S的
C/S/需要运行专门的客户端,并且它不能跨平台,用c++在windows下写的程序肯定是不能在linux下运行
B/S需要专门的客户端,只要系统中安装了浏览器即可访问,方便用户的使用。
B/S是基于网页语言的、与操作系统无关,所以跨平台也是它的优势随着网页技术以及浏览器的进步,B/S在表现能力上的处理以及运行的速度上会越来越快,所以现在越来越多的C/S结构的软件,也推出了对应B/S的版本,例如webQQ,在线文档工具、在线画图工具等。
同时也包括很多网页版的游戏,也是随着前端技术的发展,慢慢出现的。
无论是C/S结构的软件,还是B/S结构的软件,都离不开网络的支持。
网络编程,就是在一定的协议下,实现两台计算机的通信的程序。
通信协议
通过计算机网络可以使多台计算机实现相互连接,位于同一个网络中的两台计算机,在进行连接和通信时,需要遵守一定的规则,这些连接和通信的规则就是网络通信协议
通信协议,对俩台计算机之间所传输数据的传输格式、传输步骤等做了统一规定要求,通信双方必须同 时遵守才能完成数据交换。
TCP/IP协议栈,传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),它是一系列网络协议的总和,是构成网络通信的核心骨架。它是互联网中最基本、使用最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在计算机之间进行传输。
TCP/IP协议栈采用4层结构,分别是应用层、传输层、网络层、链路层,并且在每一层的内部,都包含了一系列用于处理数据通信的协议,分别负责不同的通信功能。
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
例如,当通过http发起一个请求时,应用层、传输层、网络层和链路层的相关协议依次对该请求进行包装并携带对应的首部,最终在链路层生成以太网数据包,以太网数据包通过物理介质传输给对方主机,对方接收到数据包以后,然后再一层一层采用对应的协议进行拆包,最后把应用层数据交给应用程序处理。 如图
TCP和UDP
虽然完整的通信过程比较复杂,但是JavaAPI中把这些通信实现的细节进行了封装,使得我们可以直接使用相应的类和接口,来进行网络程序开发,而不用考虑通信的细节。
java.net 包中对常见的俩种通信协议进行了封装和支持:UDP和TCP
UDP,用户数据报协议(User Datagram Protocol) (了解)
UDP是无连接通信协议,在数据传输时,数据的发送端和接收端不建立连接,也不能保证对方能接收成功。
例如,当一台计算机向另外一台计算机发送数据时(UDP),发送端不会确认接收端是否存在,就会直接发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在传输重要数据时,不建议使用UDP协议,因为它不能保证数据传输的完整性。
TCP,传输控制协议 (Transmission Control Protocol) (重要)
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立连接,然后再传输数据,它提供了两台计算机之间可靠的、无差错的数据传输。
在TCP连接中,将计算机明确划分为客户端与服务器端,并且由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”的过程。
TCP的三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠
完成上述的三次握手后,客户端和服务器端的连接就已经建立了,在这个安全的、可靠的连接基础之上,就可以开始进行数据传输了。
TCP协议应用的十分广泛,例如下载文件、浏览网页、远程登录等。
IP和端口号
IP地址,互联网协议地址(Internet Protocol Address)。IP地址用来给一个网络中的计算机设备做唯一的编号查看本机IP地址的命令:
ipconfig
查看本机与另一台主机是否连通:
ping ip地址
ping 10.0.1.129
表示本机IP地址:
127.0.0.1
或者
localhost
端口号:网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,每个需要网络通信的进程(应用程序),都会占用系统的一个端口号。
在网络中,通过IP可以找到对应的主机设备,而通过端口号,可以找到这个主机中正在运行的一个应用程序
端口号的取值范围是0 ~ 65535。其中,0 ~ 1023之间的端口号用于系统内部的使用,我们自己普通的应用
程序要使用1024以上的端口号即可,同时也要避免和一些知名应用程序默认的端口冲突:
例如,oracle启动后默认占用端口号1521,mysql启动后默认占用端口号3306,redis启动后默认占用端口号6379,tomcat启动后默认占用端口号8080
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lis3gUwB-1629159045907)(D:\新桌面\jp\新建文件夹\笔记\image-20210630101933206.png)]
程序1,在客户端电脑中的内存中运行着,并且占用了端口号8899
程序2,在服务器端电脑中的内存中运行着,并且占用了端口号8888
俩个程序,通过IP+端口号的方式,找到对方进行通信,传输信息
如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。
如果我们要使用的一个端口号8888,被其他程序给占用了,该怎么办?可以找到这个占用8888端口的进程PID是多少,然后给它强制关闭(杀死)。
Windows系统中执行命令:
//查看占用8888端口号的进程PID
netstat -aon|findstr 8888
//显示结果,找到占用8888端口号的PID
TCP 0.0.0.0:8888 0.0.0.0:0 LISTENING 2696
//强制关闭(杀死)
taskkill /F /pid 2696
注意,需要使用管理员的权限来运行命令窗口
Ubuntu系统中执行命令:
//查看占用8888端口号的进程PID
netstat -anp | grep :8888
//显示结果,找到占用8888端口号的PID
tcp 0 0 :::8888 :::* LISTEN 3626/java
//强制关闭(杀死)
kill -9 3626
如果权限不足,可以使用root账号操作,或者使用sudo命令
TCP网络编程
概述
在TCP通信协议下,能实现两台计算机之间的数据交互,并且它们要严格区分客户端(Client)与服务(Server)
客户端和服务端通信的步骤:
服务端先进行启动,并占用一个指定的端口号,等待客户端的连接
客户端主动发起服务端的连接,在连接成功之后,就可以进行数据发送了
注意,在整个过程中,服务端不能主动连接客户端,必须由客户端先行发起连接才行
在java中,对于这样基于TCP协议下连接通信的客户端和服务端,分别进行了抽象:
java.net.Socket 类表示客户端
java.net.ServerSocket 类表示服务端
使用 Socket 和 ServerSocket 进行的编程,也称为套接字编程。
TcpServerTest.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServerTest {
public static void main(String[] args) {
int port = 8888;
ServerSocket server = null;
Socket socket = null;
BufferedReader in = null;
PrintWriter out = null;
String encoding = "UTF-8";
try {
server = new ServerSocket(port);
System.out.println("服务器启动,监听端口"+port+",等待客户端的连接");
socket = server.accept();
System.out.println("服务器接收到客户端的连接:"+socket);
//获取输入流,读取客户端发送过来的内容
// InputStream is = socket.getInputStream();
// in = new BufferedReader(new InputStreamReader(is,"UTF-8"));
in = new BufferedReader(new
InputStreamReader(socket.getInputStream(),encoding));
//获取输出流,给客户端写回内容
// OutputStream os = socket.getOutputStream();
// out = new PrintWriter(new OutputStreamWriter(os,"UTF-8"));
out = new PrintWriter(new
OutputStreamWriter(socket.getOutputStream(),encoding));
String line = null;
line = in.readLine();
System.out.println("服务器端接收到客户端发送的信息:"+line);
out.println("欢迎访问xxx服务器!");
out.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭资源
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null){
out.close();
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(server!=null){
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
TcpClientTest.java
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
public class TcpClientTest {
public static void main(String[] args) {
String host = "127.0.0.1";
int port = 8888;
Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
BufferedReader inFormConsole = null;
String encoding = "UTF-8";
try {
//对象创建成功,就表示连接服务器端成功
socket = new Socket(host,port);
//获取输出流,给服务器写内容
// OutputStream os = socket.getOutputStream();
// out = new PrintWriter(new OutputStreamWriter(os,"UTF-8"));
out = new PrintWriter(new
OutputStreamWriter(socket.getOutputStream(),encoding));
//获取输入流,读取服务器端写回的内容
// InputStream is = socket.getInputStream();
// in = new BufferedReader(new InputStreamReader(is,"UTF-8"));
in = new BufferedReader(new
InputStreamReader(socket.getInputStream(),encoding));
//创建新的输入流,负责读取从控制台中读取用户输入的内容
inFormConsole = new BufferedReader(new
InputStreamReader(System.in,encoding));
String lineFromConsole = null;
lineFromConsole = inFormConsole.readLine();
out.println(lineFromConsole);
out.flush();
String lineFromServer = null;
lineFromServer = in.readLine();
System.out.println("客户端接收到服务器写回的是内容为:"+lineFromServer);
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭资源
if(inFormConsole!=null){
try {
inFormConsole.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out!=null){
out.close();
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
UDP网络编程
概述
在UDP通信协议下,两台计算机之间进行数据交互,并不需要先建立连接,客户端直接往指定的IP和端口号上发送数据即可,但是它并不能保证数据一定能让对方收到。
java.net.DatagramSocket 和 java.net.DatagramPacket 是UDP编程中使用到的俩个类,客户端和服务器端都使用这个俩类
java.net.DatagramSocket 负责接收和发送数据
java.net.DatagramPacket 负责封装要发送的数据和接收到的数据
UdpServerTest.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
public class UdpServerTest {
public static void main(String[] args) {
byte[] buf = new byte[1024];
int port = 10232;
DatagramSocket ds = null;
DatagramPacket dp = null;
try {
//创建socket,并指定监听的端口号
ds = new DatagramSocket(port);
//创建packet,用于接收数据
//指定buf从下标0开始,最多接收length长度的数据
dp = new DatagramPacket(buf, 0,buf.length);
System.out.println("服务器启动,等待客户端发送数据过来");
//receive方法会阻塞,等待客户端发送数据过来
//使用packet接收数据,数据存放在packet中的buf数组中
ds.receive(dp);
//把收的数据转为字符串输出,数据从buf的下标0开始,长度为packet.getLength()
System.out.println("服务器接收的数据为:"+new
String(buf,0,dp.getLength()));
} catch (IOException e) {
e.printStackTrace();
}finally {
if(ds!=null){
ds.close();
}
}
}
}
UdpClientTest.java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class UdpClientTest {
public static void main(String[] args) {
String ip = "127.0.0.1";
int port = 10232;
DatagramSocket ds = null;
DatagramPacket dp = null;
try {
byte[] buf = "hello world 中国".getBytes();
//socket负责发送数据
ds = new DatagramSocket();
//打好数据报,并指定要发生到的ip和端口号
//buf中从0开始,全部数据都发送
dp = new DatagramPacket(buf,
0,buf.length,InetAddress.getByName(ip), port);
//发送数据
ds.send(dp);
System.out.println("客户端发送数据完毕");
} catch (SocketException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(ds!=null){
ds.close();
}
}
}
}
URI和URL
URI(uniform resource identifier),统一资源标识符,用来唯一的标识一个资源。
URL(uniform resource locator),统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何定位这个资源。
例如,http://127.0.0.1/hello ,这就是一个URL,它不仅标识了一个资源,还能定位这个资源。
例如,/hello ,这就是一个URI,它只是标识了一个资源。
java.net.URL 可以表示一个URL地址,使用URL对象打开一个连接后,可以获取这个网络资源返回的内容