概述
-
计算机网络
计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路(网线)连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
-
网络编程(语言)
我们所编写的程序具备在网络间进性信息传递, java支持网络通信 , 将底层实现细节封装起来
-
网络通信中的问题
如何找到计算机网络中的某台主机 (IP)
找到主机后如何可靠安全的传输 (通信协议)
网络通信协议
网络通信协议
计算机网络中实现通信必须有一些约定,即通信协议;包括对速率,传输代码,代码结构,传输控制步骤,出错控制等制定的标准。常见的网络通信协议有:TCP/IP协议、IPX/SPX协议、NetBEUI协议等。
网络通信接口
为了使两个节点之间能进行对话,必须在他们之间建立通信工具(即接口),使彼此之间,能进行信息交换。接口包括两部分:
硬件装置:实现结点之间的信息传送
软件装置:规定双方进行通信的约定协议
通信协议分层思想
可以各层互不影响,利于系统的开发和扩展。
分层规定
把用户应用程序作为最高层,把物理通信线路作为最底层,将其间的协议处理分为若干层,规定每层处理的任务,也规定每层的接口标准。
应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。
传输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。
网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。
数据链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。
协议分类
java.net包中提供了两种常见的网络协议的支持:TCP和UDP
TCP是可靠的连接,TCP就像打电话,需要先打通对方电话,等待对方有回应后才会跟对方继续说话,也就是一定要确认可以发信息以后才会把信息发出去。TCP上传任何东西都是可靠的,只要两台机器上建立起了连接,在本机上发送的数据就一定能传到对方的机器上。
UDP就好比发电报,发出去就完事了,对方有没有接收到它都不管,所以UDP是不可靠的。
TCP传送数据虽然可靠,但传送得比较慢;UDP传送数据不可靠,但是传送得快。
1.UDP
用户数据报协议(User Datagram Protocol)。
- UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。
- 由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。
- 但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时,不建议使用UDP协议。
- 数据被限制在64kb以内,超出这个范围就不能发送了。
2.TCP
传输控制协议(Transmission Control Protocol)。
TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
- 使用TCP协议前,须先建立TCP连接,形成传输数据通道
- 传输前,采用“三次握手”方式,是可靠的
- TCP协议进行通信的两个应用进程:客户端、服务端
- 在连接中可进行大数据量的传输
- 传输完毕,需释放已建立的连接,效率低
- 在断开时要进行**“四次挥手”**
三次握手
TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。
第一次握手,客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
(客户端向服务器端发出连接请求,等待服务器确认。)
第二次握手,服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。(服务器端向客户端回送一个响应,通知客户端收到了连接请求。)
第三次握手,客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。(客户端再次向服务器端发送确认信息,确认连接。)
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。整个交互过程如下图所示。
四次挥手
TCP的客户端和服务端断开连接,需要四次挥手
客户端打算关闭连接,此时会发送一个 TCP 首部 FIN 标志位被置为 1 的报文,也即 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
客户端收到服务端的 ACK 应答报文后,之后进入 FIN_WAIT_2 状态。
等待服务端处理完数据后,也向客户端发送 FIN 报文,之后服务端进入 LAST_ACK 状态。
客户端收到服务端的 FIN 报文后,回一个 ACK 应答报文,之后进入 TIME_WAIT 状态
服务器收到了 ACK 应答报文后,就进入了 CLOSE 状态,至此服务端已经完成连接的关闭。
客户端在经过 2MSL 一段时间后,自动进入 CLOSE 状态,至此客户端也完成连接的关闭。
你可以看到,每个方向都需要一个 FIN 和一个 ACK,因此通常被称为四次挥手。这里一点需要注意是:主动关闭连接的,才有 TIME_WAIT 状态。
为什么挥手需要四次?
关闭连接时,客户端向服务端发送 FIN 时,仅仅表示客户端不再发送数据了但是还能接收数据。服务器收到客户端的 FIN 报文时,先回一个 ACK 应答报文,而服务端可能还有数据需要处理和发送,等服务端不再发送数据时,才发送 FIN 报文给客户端来表示同意现在关闭连接。从上面过程可知,服务端通常需要等待完成数据的发送和处理,所以服务端的 ACK 和 FIN 一般都会分开发送,从而比三次握手导致多了一次。参考
通俗理解
三次握手A:我要过来了!B:我知道你要过来了!A:我现在过来!
四次挥手A:我们分手吧!B:真的分手吗?B:真的真的要分手吗?A:是的!
由此,可以可靠地进行连接和断开。
IP协议
InetAddress 网络互连协议
给每个电脑提供了独一无二的IP地址,这样互相通信时就不会传错信息了
可以通过域名解析到IP地址
public static void main(String[] args) throws UnknownHostException {
InetAddress ip = InetAddress.getLocalHost(); //获取本机IP:LAPTOP-66VPV0MB/127.0.0.1
System.out.println(ip); //输出地址:LAPTOP-66VPV0MB/127.0.0.1
System.out.println(ip.getHostName());//主机名
System.out.println(ip.getHostAddress());//IP地址
System.out.println(InetAddress.getByName("localhost"));//本机地址localhost/127.0.0.1
InetAddress inetAddress =InetAddress.getByName("www.taobao.com");
System.out.println(inetAddress);//www.taobao.com/219.144.108.233
}
端口
标识正在计算机上运行的进程(程序)
不同的进程有不同的端口号
端口号与IP地址的组合得出一个网络套接字。
端口分类:
-
公认端口:0-1023,被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23)
-
注册端口:1024-49151,分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。
-
动态/私有端口:49152-65535.
public static void main(String[] args) {
InetSocketAddress inetSocketAddress =new InetSocketAddress("127.0.0.1",8802);
System.out.println(inetSocketAddress);// /127.0.0.1:8802
System.out.println(inetSocketAddress.getHostName()); //返回主机名
System.out.println(inetSocketAddress.getPort());//获得InetSocketAddress的端口
InetAddress address = inetSocketAddress.getAddress();//返回一个InetAddress对象(IP对象)
System.out.println(address);
}
java实现网络编程
服务器 : 电脑中为我们提供服务的软件 , 只是用电脑做硬件支撑
Socket类 (套接字 : 包含服务器IP+端口号)
ServerSocket (提供服务)
基于TCP / IP协议的进行连接传输
//服务器
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(7777); //设定程序端口号
System.out.println("服务器已启动");
while (true){
Socket socket =serverSocket.accept(); //循环监听
System.out.println("客户端连接成功");
InputStream in =socket.getInputStream();
DataInputStream din = new DataInputStream(in); //处理流包装
while (true){
String accMsg=din.readUTF(); //接收
System.out.println("客户端说:"+accMsg);
}
}
}
}
//客户端
public class Client extends Thread{
public static void main(String[] args) throws IOException {
Socket socket = new Socket("192.168.1.3",7777); //确定要连接的程序的IP和端口
System.out.println("服务器连接成功");
while (true){
OutputStream out =socket.getOutputStream();
DataOutputStream dout =new DataOutputStream(out);
Scanner scanner =new Scanner(System.in);
while (true){
System.out.println("客户端请输入:");
String sendMsg=scanner.next();
dout.writeUTF(sendMsg); //发送
}
}
}
}
UDP通信
● UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
● DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
● UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接
//发送方
public class Client {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket = new DatagramSocket();
for (int i = 0; i < 1000; i++) {
String msg ="hello\t"+i;
byte[]b =msg.getBytes();
//数据报
DatagramPacket datagramPacket =new DatagramPacket(b,b.length, InetAddress.getByName("192.168.1.3"),7777);
datagramSocket.send(datagramPacket);
}
}
}
//接收方
public class Server {
public static void main(String[] args) throws IOException {
DatagramSocket datagramSocket =new DatagramSocket(7777);
byte[]b =new byte[1024];
while (true){
DatagramPacket datagramPacket =new DatagramPacket(b,b.length);//把内容封装到一个数据报中
datagramSocket.receive(datagramPacket); //读取接收到的内容
String msg =new String(b,0,b.length,"utf-8");
System.out.println(msg);
}
}
}