[1.JavaSE] 7 网络编程

7. 网络编程

7.1 网络模型

OSI模型

为了使不同计算机厂家生产的计算机能够相互通信,以便在更大的范围内建立计算机网络,国际标准化组织(ISO)在1978年提出了"开放系统互联参考模型",即著名的OSI/RM模型(Open System Interconnection/Reference Model)。它将计算机网络体系结构的通信协议划分为七层,自下而上依次为:物理层(Physics Layer)、数据链路层(Data Link Layer)、网络层(Network Layer)、传输层(Transport Layer)、会话层(Session Layer)、表示层(Presentation Layer)、应用层(Application Layer)。其中第四层完成数据传送服务,上面三层面向用户。

除了标准的OSI七层模型以外,常见的网络层次划分还有TCP/IP四层协议以及TCP/IP五层协议,它们之间的对应关系如下图所示:
在这里插入图片描述

  • 物理层(Physical Layer)

在OSI参考模型中,物理层(Physical Layer)是参考模型的最低层,也是OSI模型的第一层。

物理层的主要功能是:利用传输介质为数据链路层提供物理连接,实现比特流的透明传输。

物理层的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。使其上面的数据链路层不必考虑网络的具体传输介质是什么。

  • 数据链路层(Data Link Layer)

数据链路层(Data Link Layer)是OSI模型的第二层,负责建立和管理节点间的链路。该层的主要功能是:通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。

在计算机网络中由于各种干扰的存在,物理链路是不可靠的。因此,这一层的主要功能是在物理层提供的比特流的基础上,通过差错控制、流量控制方法,使有差错的物理线路变为无差错的数据链路,即提供可靠的通过物理介质传输数据的方法。

  • 网络层(Network Layer)

网络层(Network Layer)是OSI模型的第三层,它是OSI参考模型中最复杂的一层,也是通信子网的最高一层。它在下两层的基础上向资源子网提供服务。其主要任务是:通过路由选择算法,为报文或分组通过通信子网选择最适当的路径。该层控制数据链路层与传输层之间的信息转发,建立、维持和终止网络的连接。具体地说,数据链路层的数据在这一层被转换为数据包,然后通过路径选择、分段组合、顺序、进/出路由等控制,将信息从一个网络设备传送到另一个网络设备。

  • 传输层

OSI下3层的主要任务是数据通信,上3层的任务是数据处理。而传输层(Transport Layer)是OSI模型的第4层。因此该层是通信子网和资源子网的接口和桥梁,起到承上启下的作用。

该层的主要任务是:向用户提供可靠的端到端的差错和流量控制,保证报文的正确传输。传输层的作用是向高层屏蔽下层数据通信的细节,即向用户透明地传送报文。该层常见的协议:TCP/IP中的TCP协议、Novell网络中的SPX协议和微软的NetBIOS/NetBEUI协议。

传输层提供会话层和网络层之间的传输服务,这种服务从会话层获得数据,并在必要时,对数据进行分割。然后,传输层将数据传递到网络层,并确保数据能正确无误地传送到网络层。因此,传输层负责提供两节点之间数据的可靠传送,当两节点的联系确定之后,传输层则负责监督工作。综上,传输层的主要功能如下:

传输连接管理:提供建立、维护和拆除传输连接的功能。传输层在网络层的基础上为高层提供“面向连接”和“面向无接连”的两种服务。

处理传输差错:提供可靠的“面向连接”和不太可靠的“面向无连接”的数据传输服务、差错控制和流量控制。在提供“面向连接”服务时,通过这一层传输的数据将由目标设备确认,如果在指定的时间内未收到确认信息,数据将被重发。

  • 会话层

会话层(Session Layer)是OSI模型的第5层,是用户应用程序和网络之间的接口,主要任务是:向两个实体的表示层提供建立和使用连接的方法。将不同实体之间的表示层的连接称为会话。因此会话层的任务就是组织和协调两个会话进程之间的通信,并对数据交换进行管理。

  • 表示层

表示层(Presentation Layer)是OSI模型的第六层,它对来自应用层的命令和数据进行解释,对各种语法赋予相应的含义,并按照一定的格式传送给会话层。其主要功能是“处理用户信息的表示问题,如编码、数据格式转换和加密解密”等。

  • 应用层

应用层(Application Layer)是OSI参考模型的最高层,它是计算机用户,以及各种应用程序和网络之间的接口,其功能是直接向用户提供服务,完成用户希望在网络上完成的各种工作。它在其他6层工作的基础上,负责完成网络中应用程序与网络操作系统之间的联系,建立与结束使用者之间的联系,并完成网络用户提出的各种网络服务及应用所需的监督、管理和服务等各种协议。此外,该层还负责协调各个应用程序间的工作。

OSI是一个理想的模型,一般的网络系统只涉及其中的几层,在七层模型中,每一层都提供一个特殊 的网络功能,从网络功能角度观察:

  • 下面4层(物理层、数据链路层、网络层和传输层)主要提供数据传输和交换功能, 即以节点到节点之间的通信为主
  • 第4层作为上下两部分的桥梁,是整个网络体系结构中最关键的部分;
  • 上3层(会话层、表示层和应用层)则以提供用户与应用程序之间的信息和数据处理功能为主。

简言之,下4层主要完成通信子网的功能,上3层主要完成资源子网的功能。

TCP/IP模型

┌────------────┐┌─┬─ ┬─ ┬─┬─ ┬─–┬─-┬─┬─-┬─┬─-┐

│        ││D│F│W│F│H│G│T│I│S│U│ │

│        ││N│I│H│T│T│O│E│R│M│S│其│

│第四层,应用层 ││S│N│O│P│T│P│L│C│T│E│ │

│        ││ │G│I│ │P│H│N│ │P│N│ │

│        ││ │E│S│ │ │E│E│ │ │E│它│

│        ││ │R│ │ │ │R│T│ │ │T│ │

└───────------─┘└─┴─┴─-┴─┴─-┴─┴─-┴─┴─-┴─┴-─–┘

┌───────-----─┐┌─────────-------┬──--------─────────┐

│第三层,传输层 ││   TCP   │    UDP    │

└───────-----─┘└────────-------─┴──────────--------─┘

┌───────-----─┐┌───----──┬───—─┬────────-------──┐

│        ││     │ICMP│         │

│第二层,网间层 ││     └──—──┘         │

│        ││       IP           │

└────────-----┘└────────────────────-------------─-┘

┌────────-----┐┌─────────-------┬──────--------─────┐

│第一层,网络接口││ARP/RARP │    其它     │

└────────------┘└─────────------┴─────--------──────┘

TCP/IP四层参考模型

TCP/IP协议被组织成四个概念层,其中有三层对应于ISO参考模型中的相应层。ICP/IP协议族并不包含物理层和数据链路层,因此它不能独立完成整个计算机网络系统的功能,必须与许多其他的协议协同工作。

TCP/IP分层模型的四个协议层分别完成以下的功能:

第一层:网络接口层

包括用于协作IP数据在已有网络介质上传输的协议。实际上TCP/IP标准并不定义与ISO数据链路层和物理层相对应的功能。相反,它定义像地址解析协议(Address Resolution Protocol,ARP)这样的协议,提供TCP/IP协议的数据结构和实际物理硬件之间的接口。

第二层:网间层

对应于OSI七层参考模型的网络层。本层包含IP协议、RIP协议(Routing Information Protocol,路由信息协议),负责数据的包装、寻址和路由。同时还包含网间控制报文协议(Internet Control Message Protocol,ICMP)用来提供网络诊断信息。

第三层:传输层

对应于OSI七层参考模型的传输层,它提供两种端到端的通信服务。其中TCP协议(Transmission Control Protocol)提供可靠的数据流运输服务,UDP协议(Use Datagram Protocol)提供不可靠的用户数据报服务。

第四层:应用层

对应于OSI七层参考模型的应用层和表达层。因特网的应用层协议包括Finger、Whois、FTP(文件传输协议)、Gopher、HTTP(超文本传输协议)、Telent(远程终端协议)、SMTP(简单邮件传送协议)、IRC(因特网中继会话)、NNTP(网络新闻传输协议)等。

7.2 相关概念

IP地址

要想使网络中的计算机能够进行通信,必须为每台计算机指定一个标识号,通过这个标识号来指定接受数据的计算机或者发送数据的计算机。

在TCP/IP协议中,这个标识号就是IP地址,它可以唯一标识一台计算机,目前,IP地址广泛使用的版本是IPv4,它是由4个字节大小的二进制数来表示,如:00001010000000000000000000000001。由于二进制形式表示的IP地址非常不便记忆和处理,因此通常会将IP地址写成十进制的形式,每个字节用一个十进制数字(0-255)表示,数字间用符号“.”分开,如 “192.168.1.100”。

随着计算机网络规模的不断扩大,对IP地址的需求也越来越多,IPV4这种用4个字节表示的IP地址面临枯竭,因此IPv6 便应运而生了,IPv6使用16个字节表示IP地址,它所拥有的地址容量约是IPv4的8×1028倍,达到2128个(算上全零的),这样就解决了网络地址资源数量不够的问题。

端口号

通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号。在计算机中,不同的应用程序是通过端口号区分的。端口号是用两个字节(16位的二进制数)表示的,它的取值范围是065535,其中,01023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一个应用或服务所占用。

端口: 数据的发送与接收都需要通过端口。可以理解为一个计算机的门。同一个计算机中两个应用程序不允许占用同一个端口。

端口号:0 ~ 65535

0~1023: 公共端口

1025 ~ 49151: 注册端口

1024 ~ 65535: 动态或私有端口

常见的端口:

8080:tomcat

3306:mysql

1521:oracle

InetAddress类

是一个用来描述IP的一个类,有两个常用的子类:Inet4Address、Inet6Address

IPv4: 是用四个字节来描述IP地址,IP地址中的每一位都是一个字节,范围[0, 255]

192.168.1.1

IPv6: 是用六个字节来描述IP地址

A类:1.0.0.1 ~ 126.255.255.254 保留给政府机构

B类:128.0.0.1 ~ 191.255.255.254 分配给大中型企业

C类:192.0.0.1 ~ 223.255.255.254 分配给任何有需要的个人

D类:224.0.0.1 ~ 239.255.255.254 用于组播

E类:240.0.0.1 ~ 255.255.255.254 用于实验

    public static void main(String[] args) {
        try {
            // 获取主机
            InetAddress local = InetAddress.getLocalHost();
            System.out.println(local);
    
            // 获取主机名字字符串
            String name = local.getHostName();
            System.out.println(name);
    
            // 获取主机地址字符串
            String address = local.getHostAddress();
            System.out.println(address);
    
            // 通过主机名获取
            InetAddress localhost = InetAddress.getByName("localhost");
            System.out.println(localhost);
    
            // 通过一个域名获取
            InetAddress kaikeba = InetAddreshosttByName("www.kaikeba.com");
            System.out.println(kaikeba);
    
            InetAddress[] addresses = InetAddress.getAllByName("www.kaikeba.com");
            for (InetAddress addr : addresses) {
                System.out.println(addr);
            }
    
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }

7.3 TCP

TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。

三次握手: 建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立, 在Socket编程中,这一过程由客户端执行connect来触发,具体流程图如下:
在这里插入图片描述

  • **第一次握手:**Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server, Client进入SYN_SENT状态,等待Server确认。
  • **第二次握手:**Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位 SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求 ,Server进入SYN_RCVD状态。
  • **第三次握手:**Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK 置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则 连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以 开始传输数据了。

四次挥手: 终止TCP连接,就是指断开一个TCP连接时,需要客户端和服务端总共发送4个包以确认连接的断开。 在Socket编程中,这一过程由客户端或服务端任一方执行close来触发,具体流程图如下:
在这里插入图片描述

  • 第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入 FIN_WAIT_1状态
  • 第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同, 一个FIN占用一个序号),Server进入CLOSE_WAIT状态。
  • 第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK 状态。
  • 第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。 另外也可能是同时发起主动关闭的情况:
    在这里插入图片描述

另外还可能有一个常见的问题就是:为什么建立连接是三次握手,而关闭连接却是四次挥手呢? 答:因为服务端在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里 发送给客户端。而关闭连接时,当收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还 能接收数据,己方也未必全部数据都发送给对方了,所以己方可以立即close,也可以发送一些 数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会 分开发送。

由于TCP协议的面向连接特性,它可以保证传输数据的安全性,所以是一个被广泛采用的协议,例如在下载文件时,如果数据接收不完整,将会导致文件数据丢失而不能被打开,因此,下载文件时必须采用TCP协议。

TCP特点:

  • 面向连接。
  • 安全的。(不会存在数据丢失)
  • 传输的数据大小是有限制的。

三次握手:

  • 客户端向服务端发送一个请求。
  • 服务端收到请求后,返回给客户端一个响应。
  • 客户端接收到服务端的响应后,再回服务端一个确认信息。

重点涉及到的类:

Socket类:客户端

ServerSocket类:服务端

Socket用来描述IP地址和端口,是通信链的句柄,应用程序通过Socket向网络发送请求或者应答网络请求,socket是支持TCP/IP协议的网络通信的基本操作单元

Socket
在这里插入图片描述

    /* TCP 服务器端
     * 
     * 1,创建服务器ServerSocket对象(指定服务器端口号)
     * 2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象
     * 3,给客户端反馈信息
     * 4,关闭流资源
     */
    public class TCPServer {
    	public static void main(String[] args) throws IOException {
    		//1,创建服务器ServerSocket对象(指定服务器端口号)
    		ServerSocket ss = new ServerSocket(8888);
    		//2,开启服务器了,等待客户端的连接,当客户端连接后,可以获取到连接服务器的客户端Socket对象
    		Socket s = ss.accept();
    		//3,给客户端反馈信息
    		/*
    		 * a,获取客户端的输出流
    		 * b,在服务端端,通过客户端的输出流写数据给客户端
    		 */
    		//a,获取客户端的输出流
    		OutputStream out = s.getOutputStream();
    		//b,在服务端端,通过客户端的输出流写数据给客户端
    		out.write("你已经连接上了服务器".getBytes());
    		//4,关闭流资源
    		out.close();
    		s.close();
    		//ss.close();  服务器流 通常都是不关闭的
    	}
    }
    
    //完成了服务器端程序的编写,接下来编写客户端程序。
    /*
     * TCP 客户端
     * 
     * 1,创建客户端Socket对象,(指定要连接的服务器地址与端口号)
     * 2,获取服务器端的反馈回来的信息
     * 3,关闭流资源
     */
    public class TCPClient {
    	public static void main(String[] args) throws IOException {
    		//1,创建客户端Socket对象,(指定要连接的服务器地址与端口号)
    		Socket s = new Socket("127.0.0.1", 8888);
    		//2,获取服务器端的反馈回来的信息
    		InputStream in = s.getInputStream();
    		//获取获取流中的数据
    		byte[] buffer = new byte[1024];
    		//把流中的数据存储到数组中,并记录读取字节的个数
    		int length = in.read(buffer);
    		//显示数据
    		System.out.println( new String(buffer, 0 , length) );
    		//3,关闭流资源
    		in.close();
    		s.close();
    	}
    }

7.4 UDP

UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。

由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。UDP的交换过程如下图所示。

    //UDP完成数据的发送
    /*
    * 发送端
     * 1,创建DatagramSocket对象
     * 2,创建DatagramPacket对象,并封装数据
     * 3,发送数据
     * 4,释放流资源
     */
    public class UDPSend {
    	public static void main(String[] args) throws IOException {
    		//1,创建DatagramSocket对象
    		DatagramSocket sendSocket = new DatagramSocket();
    		//2,创建DatagramPacket对象,并封装数据
    		//public DatagramPacket(byte[] buf, int length, InetAddress address,  int port)
    		//构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
    		byte[] buffer = "hello,UDP".getBytes();
    		DatagramPacket dp = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("127.0.0.1"), 6666);
    		//3,发送数据
    		//public void send(DatagramPacket p) 从此套接字发送数据报包
    		sendSocket.send(dp);
    		//4,释放流资源
    		sendSocket.close();
    	}
    }
    
    //UDP完成数据的接收
    /*
     * UDP接收端
     * 
     * 1,创建DatagramSocket对象
     * 2,创建DatagramPacket对象
     * 3,接收数据存储到DatagramPacket对象中
     * 4,获取DatagramPacket对象的内容
     * 5,释放流资源
     */
    public class UDPReceive {
    	public static void main(String[] args) throws IOException {
    		//1,创建DatagramSocket对象,并指定端口号
    		DatagramSocket receiveSocket = new DatagramSocket(6666);
    		//2,创建DatagramPacket对象, 创建一个空的仓库
    		byte[] buffer = new byte[1024];
    		DatagramPacket dp = new DatagramPacket(buffer, 1024);
    		//3,接收数据存储到DatagramPacket对象中
    		receiveSocket.receive(dp);
    		//4,获取DatagramPacket对象的内容
    		//谁发来的数据  getAddress()
    		InetAddress ipAddress = dp.getAddress();
    		String ip = ipAddress.getHostAddress();//获取到了IP地址
    		//发来了什么数据  getData()
    		byte[] data = dp.getData();
    		//发来了多少数据 getLenth()
    		int length = dp.getLength();
    		//显示收到的数据
    		String dataStr = new String(data,0,length);
    		System.out.println("IP地址:"+ip+ "数据是"+ dataStr);
    		//5,释放流资源
    		receiveSocket.close();
    	}
    }

7.5 综合案例

    /*
     * 文件上传  服务器端
     *
     */
    public class TCPServer {
    	public static void main(String[] args) throws IOException {
    		//1,创建服务器,等待客户端连接
    		ServerSocket serverSocket = new ServerSocket(8888);
    		Socket clientSocket = serverSocket.accept();
    		//显示哪个客户端Socket连接上了服务器
    		InetAddress ipObject = clientSocket.getInetAddress();//得到IP地址对象
    		String ip = ipObject.getHostAddress(); //得到IP地址字符串
    		System.out.println("谁在传文件给我" + "IP:" + ip);
    		
    		//2,获取Socket的输入流
    		InputStream in = clientSocket.getInputStream();
    		//3,创建目的地的字节输出流  
    		BufferedOutputStream fileOut = new BufferedOutputStream(new FileOutputStream("myfileRecieved.txt"));
    		//4,把Socket输入流中的数据,写入目的地的字节输出流中
    		byte[] buffer = new byte[1024];
    		int len = -1;
    		while((len = in.read(buffer)) != -1){
    			//写入目的地的字节输出流中
    			fileOut.write(buffer, 0, len);
    		}
    		
    		//-----------------反馈信息---------------------
    		//5,获取Socket的输出流, 作用:写反馈信息给客户端
    		OutputStream out = clientSocket.getOutputStream();
    		//6,写反馈信息给客户端
    		out.write("文件上传成功".getBytes());
    		
    		out.close();
    		fileOut.close();
    		in.close();
    		clientSocket.close();
    		//serverSocket.close();
    	}
    }
    
    //编写客户端,完成上传文件
    /*
     * 文件上传 客户端
     * 
     * public void shutdownOutput()  禁用此Socket的输出流,间接的相当于告知了服务器数据写入完毕
     */
    public class TCPClient {
    	public static void main(String[] args) throws IOException {
    		//1,创建客户端Socket,连接服务器
    		Socket socket = new Socket("127.0.0.1", 8888);
    		//2,获取Socket流中的输出流,功能:用来把数据写到服务器
    		OutputStream out = socket.getOutputStream();
    		//3,创建字节输入流,功能:用来读取数据源(图片)的字节
    		BufferedInputStream fileIn = new BufferedInputStream(new FileInputStream("myfile.txt"));
    		//4,把图片数据写到Socket的输出流中(把数据传给服务器)
    		byte[] buffer = new byte[1024];
    		int len = -1;
    		while ((len = fileIn.read(buffer)) != -1){
    			//把数据写到Socket的输出流中
    			out.write(buffer, 0, len);
    		}
    		//5,客户端发送数据完毕,结束Socket输出流的写入操作,告知服务器端
    		socket.shutdownOutput();
    
    		//-----------------反馈信息---------------------
    		//6,获取Socket的输入流  作用: 读反馈信息
    		InputStream in = socket.getInputStream();
    		//7,读反馈信息
    		byte[] info = new byte[1024];
    		//把反馈信息存储到info数组中,并记录字节个数
    		int length = in.read(info);
    		//显示反馈结果
    		System.out.println( new String(info, 0, length) );
    		
    		//关闭流
    		in.close();
    		fileIn.close();
    		out.close();
    		socket.close();
    	}
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值