1. 网络编程
1.1 什么是网络编程
网络编程:在一定的协议下,实现两台或者以上计算机之间的通信。
1.2 什么是计算机网络
计算机网络:是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
1.3 网络编程的目的
传播交流信息;数据交换;通信。
1.4 网络通信协议
-
网络通信协议: 通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。
-
TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。
上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。
链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
1.5 协议分类
通信的协议还是比较复杂的,java.net
包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net
包中提供了两种常见的网络协议的支持:
-
UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。
由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。
特点:数据被限制在64kb以内,超出这个范围就不能发送了。
数据报(Datagram):网络传输的基本单位
-
TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
- 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。
- 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。
- 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。
- 三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
总结TCP与UDP的区别:
分开总结
TCP:打电话
- 连接,稳定
- 三次握手,四次挥手
A:你瞅啥?
B:瞅你炸地?
A:干一场。
A:我要走了。
B:你真的要走了吗?
B:你真的真的要走了吗?
A:我真的要走了。
- 客户端,服务端
- 传输完成,释放连接,效率低。
UDP:发1短信
- 不连接,不稳定
- 客户端、服务端:没有明确的接线
- 不管有没有准备好,都可以发给你
- 导弹
- DDOS攻击:洪水攻击
一起总结
1、基于连接与无连接;
2、对系统资源的要求(TCP较多,UDP少);
3、UDP程序结构较简单;
4、流模式与数据报模式 ;
5、TCP保证数据正确性,UDP可能丢包;
6、TCP保证数据顺序,UDP不保证。
1.6网络编程三要素
协议
- 协议: 计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。
IP地址
- IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。
IP地址分类
-
IPv4:是一个32位的二进制数,通常被分为4个字节,表示成
a.b.c.d
的形式,例如192.168.65.100
。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。 -
IPv6:由于互联网的蓬勃发展,IP地址的需求量愈来愈大,但是网络地址资源有限,使得IP的分配越发紧张。
为了扩大地址空间,拟通过IPv6重新定义地址空间,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成
ABCD:EF01:2345:6789:ABCD:EF01:2345:6789
,号称可以为全世界的每一粒沙子编上一个网址,这样就解决了网络地址资源数量不够的问题。
常用命令
- 查看本机IP地址,在控制台输入:
ipconfig
- 检查网络是否连通,在控制台输入:
ping 空格 IP地址
ping 220.181.57.216
特殊的IP地址
- 本机IP地址:
127.0.0.1
、localhost
。
IP测试
public class IPtest {
public static void main(String[] args) {
try {
// 查询本机地址
InetAddress inetAddress = InetAddress.getByName("127.0.0.1");
System.out.println(inetAddress); // /127.0.0.1
InetAddress inetAddress2 = InetAddress.getByName("localhost");
System.out.println(inetAddress2);// localhost/127.0.0.1
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost);// XXX/192.168.X.X
// 查询网址IP地址
InetAddress inetAddress3 = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress3);// www.baidu.com/14.215.177.38
System.out.println(inetAddress3.getCanonicalHostName());// 14.215.177.38
System.out.println(inetAddress3.getHostAddress());// 14.215.177.38
System.out.println(inetAddress3.getHostName());// www.baidu.com
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
端口号
IP地址:用来唯一标识网络中的设备,端口号:用来唯一标识设备中的进程(应用程序)。
- 用两个字节表示的整数,它的取值范围是0~65535
- TCP,UDP: 65535 * 2(每个协议都有65535个端口号) tcp: 80,udp: 80,(不同协议可以使用同一个端口号),单个协议下,端口号不能冲突。
- 端口分类
-
共有端口 0~1023
HTTP: 80
HTTPS: 443
FTB: 21
Telent: 23 -
程序注册端口 1024~49151,分配给用户或者程序
Tomcat:8080
MySQL:3306
Oracle: 1521 -
动态/私有端口 49152~65535
netstat -ano #查看所有端口
netstat -ano | findstr “端口号” #根据指定端口号
tasklist | findstr “端口号” #查看指定端口号进程 -
端口号测试
public class SocketTest {
public static void main(String[] args) {
InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9999);
System.out.println(inetSocketAddress);// /127.0.0.1:9999
InetSocketAddress inetSocketAddress2 = new InetSocketAddress("localhost", 9999);
System.out.println(inetSocketAddress2);// /127.0.0.1:9999
System.out.println(inetSocketAddress.getAddress());// /127.0.0.1
System.out.println(inetSocketAddress.getHostName()); // 在hosts文件 IP地址进行映射到的域名
System.out.println(inetSocketAddress.getPort());// 9999
}
}
利用协议
+IP地址
+端口号
三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。
1.7 TCP模拟文件上传
模拟客户端
public class TcpClientDemo02 {
public static void main(String[] args) {
OutputStream os = null;
Socket socket = null;
FileInputStream fis = null;
InputStream is = null;
ByteArrayOutputStream baos = null;
try {
//1.要知道服务端的Ip地址和端口号
InetAddress ipAddress = InetAddress.getByName("127.0.0.1");
int post = 9999;
//2.与服务端建立连接
socket = new Socket(ipAddress,post);
//3.与服务端通信
os = socket.getOutputStream();
//4.向服务端发送文件
fis = new FileInputStream("1.png");
byte[] buffer = new byte[1024];
int len;
if ((len = fis.read(buffer)) != -1) {
os.write(buffer,0,len);
}
//5.向服务端告知,我已经传输完文件
socket.shutdownOutput();
//6.确定服务期接收完才断开连接
is =socket.getInputStream();
baos = new ByteArrayOutputStream();
byte[] buffer2 = new byte[1024];
int len2;
if ((len2 = is.read(buffer2)) != -1) {
baos.write(buffer2,0,len2);
}
System.out.println(baos.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (baos != null) {
try {
baos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (os != null) {
try {
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
模拟服务端
public class TcpServeDemo02 {
public static void main(String[] args) {
ServerSocket serverSocket = null;
Socket socket = null;
InputStream is = null;
FileOutputStream fos = null;
OutputStream os = null;
try {
// 1.建立服务端
serverSocket = new ServerSocket(9999);
// 2.等待接收客户端信息
socket = serverSocket.accept();
// 3.接收客户端信息
is = socket.getInputStream();
fos = new FileOutputStream("receive2.png");
byte[] buffer = new byte[1024];
int len;
if ((len = is.read(buffer)) != -1) {
fos.write(buffer,0,len);
}
//4.告知服务端我已经接收完
os = socket.getOutputStream();
os.write("服务端:我这边已经接收完了".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (os != null) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1.8 UCP模拟多线程在线咨询
模拟发送端
public class TalkSend implements Runnable {
DatagramSocket socket = null;
BufferedReader reader = null;
private int fromPort;
private String toIP;
private int toPort;
public TalkSend(int fromPort, String toIP, int toPort) {
this.fromPort = fromPort;
this.toIP = toIP;
this.toPort = toPort;
try {
// 1、建立自己的端口
socket = new DatagramSocket(this.fromPort);
reader = new BufferedReader(new InputStreamReader(System.in));
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
try {
String datas = reader.readLine();
byte[] data = datas.getBytes();
//2.建立包,把要传输的数据包起来
DatagramPacket packet = new DatagramPacket(data,0,data.length,new InetSocketAddress(this.toIP,this.toPort));
//3.发送数据
socket.send(packet);
//4.判断是否为bye,如果是则中断
if(data.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
模拟接收端
public class TalkReceive implements Runnable {
DatagramSocket socket = null;
BufferedReader reader = null;
private int port;
private String msgFrom;
public TalkReceive(int port,String msgFrom) {
this.port = port;
this.msgFrom = msgFrom;
try {
// 1、建立自己的端口
socket = new DatagramSocket(this.port);
} catch (SocketException e) {
e.printStackTrace();
}
}
@Override
public void run() {
while(true) {
try {
//2.准备接收客户端发送的消息
byte[] contains = new byte[1024];
DatagramPacket packet = new DatagramPacket(contains,0,contains.length);
//3.接收数据
socket.receive(packet);
//4.判断是否为bye,如果是则中断
byte[] data = packet.getData();
String receiveStr = new String(data,0,data.length);
System.out.println(this.msgFrom+":"+receiveStr);
if(receiveStr.equals("bye")){
break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
socket.close();
}
}
模拟学生聊天
public class TalkStudent {
public static void main(String[] args) {
new Thread(new TalkSend(7777,"127.0.0.1",9999)).start();
new Thread(new TalkReceive(8888,"老师")).start();
}
}
模拟老师聊天
public class TalkTeacher {
public static void main(String[] args) {
new Thread(new TalkSend(6666,"127.0.0.1",8888)).start();
new Thread(new TalkReceive(9999,"学生")).start();
}
}
1.9 URL下载网络资源
public class UrlDown {
public static void main(String[] args) throws Exception {
URL url = new URL("https://m701.music.126.net/20210315221547/ce2c134f9cecb54ac2fbecf21d83358d/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/7867709801/359c/dc90/817b/3a43f120555dc59dbb80e24221cf0f60.m4a");
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
InputStream is = urlConnection.getInputStream();
FileOutputStream fos = new FileOutputStream("还是会想你.m4a");
byte[] buffer = new byte[1024];
int len;
while((len = is.read(buffer)) != -1) {
fos.write(buffer,0,len);
}
fos.close();
is.close();
urlConnection.disconnect();
}
}