黑马毕向东Java课程笔记(day23-01——23-14)):网络编程:网络模型、IP地址、UDP/TCP协议及其应用、相关练习

1、网络编程简介(这一部分见视频23-01的介绍)
  首先,网络的模型如下:(这部分参考计算机网络)

  • OSI参考模型
  • TCP/IP参考模型
      其次,网络通讯要素包含:
  • IP地址
  • 端口号
  • 传输协议
      具体的传输过程如下:
1、找到对方IP地址;
2、数据要发送到对方指定的应用程序上。
为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。
为了方便称呼这些数字,我们把这些数字叫做“端口”,这些端口不是物理上的端口,而是逻辑端口。
我们的网络应用程序一般可以对应一到多个数字标识(就是逻辑端口)
3、定义通信规则,这个通信规则称之为协议。

  2个主机想进行通信,就必须使用相同的通信协议。
  IP地址一共分为A、B、C、D共4段(IPv4),每一段用8个位表示,最大值为255。有一个IP地址较为特殊——127.0.0.1,如果没有配置任何IP地址,这就是本机默认的IP地址。(该地址可以用于Ping测试网卡)另外,有一些地址保留作为局域网的地址使用。IPV6的IP地址不仅仅包含数字,还包含字母,数量很多足够使用!
  我们在用数字表示端口的时候,可以用0-65535(2^16),0-1024的端口一般被系统的程序所保留。常见端口:web:80;Tomcat服务器:8080;MySQL数据库:3306,这些默认的端口都可以改。凡是网络应用程序,都会有数字来标识端口,这样他们的数据才可以传输。
  常用的传输协议有UDP与TCP。
(以上这些具体参考计算机网络的内容)

2、网络模型
  开放系统互联参考模型(OSI,Open System Interconnection Reference Model),它包括七层的协议体系,从下到上分别是——物理层、数据链路层、网络层、运输层、会话层、表示层、应用层。
  传输控制协议/网际协议(TCP/IP,Transmission Control Protocol/Internet Protocol),它从下到上包括网络接口层、网际层IP、运输层(TCP/UDP)、应用层。
  我们在学习计算机网络的时候,通常采用折中的方法,学习5层协议的体系结构——从下到上分别是:物理层、数据链路层、网络层、运输层、应用层。
(参考《计算机网络》——谢希文,p28)
在这里插入图片描述
  具体各层之间如何通信,见《计算机网络》+视频23-03,2.00解析。
  我们学习网络编程,其实就是在TCP/IP模型中的网际层和运输层(传输层)进行操作。

3、IP地址
  网络通信相关要素如下:

IP地址
——网络中设备的标识
——不易记忆,可用主机名
——本地回环地址:127.0.0.1,主机名:localhost

端口号
——用于标识进程的逻辑地址,不同进程的标识
——有效端口:0~65535,其中0~1024系统使用或保留端口。

传输协议
——通讯的规则
——常见协议:TCP,UDP

  我们想要使用java来操作者3个要素,首先要找到与这3个要素相关的对象。
  由于主机的IP地址不便于记忆,我们使用主机名称来标识各台主机。首先,我们解析一下下面这段主机名称地址(23-04,2.00)

https://www.baidu.com
https:应用层中支持万维网应用的HTTP协议;
www:万维网组织,代表baidu这台主机在万维网中有注册
baidu:百度这台主机的名称
com:用于区分主机的所属组织(或者说所属用途),比如.com是商业组织、.org是教育组织。用不同的字段来组成主机名称,这样就会好记忆很多!

  下面我们说一下java中操作IP地址的类——类 InetAddress

InetAddress:此类表示互联网协议 (IP) 地址。 
IP 地址是 IP 使用的 32 位或 128 位无符号数字,它是一种低级协议,UDP 和 TCP 协议都是在它的基础上构建的。
InetAddress 的实例包含 IP 地址,还可能包含相应的主机名(取决于它是否用主机名构造或者是否已执行反向主机名解析)。 

InetAddress没有构造方法,它必然含有返回它本类对象的静态方法

  IP地址对象的操作代码如下:

package pack;
import java.net.*;

public class IPDemo
{
   	
	public static void main(String[] args) throws Exception
	{
   
		test_3();		
	}
	
	public static void test_1()throws Exception
	{
   
		//static InetAddress getLocalHost() 返回本地主机。 
		//首先我们使用getLocalHost()方法返回InetAddress的对象。这个方法会抛出异常UnknownHostException
		InetAddress iad = InetAddress.getLocalHost();
				
		// String toString() 将此IP地址转换为String。 
		System.out.println(iad.toString());
		/* 
		* DESKTOP-O5MEGBI/192.168.1.195:显示本机IP地址的主机名以及具体的IP地址字符串表示
		*/
		//可以使用下面2个方法获取IP地址的字符串表现形式以及相对应的主机名
		System.out.println("Address:"+iad.getHostAddress());//Address:192.168.1.195
		System.out.println("Name:"+iad.getHostName());//Name:DESKTOP-O5MEGBI
		
		//注意,我们所获取的地址都是本机解析获得的地址,如果是网络地址,我们不一定可以拿到网络上其他主机的名称
	}
	
	public static void test_2()throws Exception
	{
   
		//如果我们想获取任意一台主机的IP地址对象
		//static InetAddress getByName(String host) 在给定主机名的情况下确定主机的IP地址。 
		//主机名可以是机器名(如 "java.sun.com"),也可以是其 IP 地址的文本表示形式。如果提供字面值 IP 地址,则仅检查地址格式的有效性。
		
		InetAddress iad = InetAddress.getByName("192.168.1.195");
		System.out.println(iad.toString());// /192.168.1.195:主机地址没有解析出来
		System.out.println("Address:"+iad.getHostAddress());//Address:192.168.1.195
		System.out.println("Name:"+iad.getHostName());//Name:192.168.1.195
		//这里使用IP地址的文本形式寻找相应的主机名。如果IP地址与对应的主机名的映射关系没有在网络上,
		//我们的主机那IP地址去网络上寻找主机名,但是没有解析成功,那么返回的名字还是IP地址
		//也就是说,IP地址可以直接获得,但是主机名称还需要解析,有的时候可能解析不出来。
		   
		//如果我们使用主机地址来查询,就可以查询出来——经过试验,查询链接到宿舍wifi的其他主机也可以查询到!
		InetAddress ia = InetAddress.getByName("WINDOWS-LTOJAMN");
		System.out.println(ia.toString());// DESKTOP-O5MEGBI/192.168.1.195
		System.out.println("Address:"+ia.getHostAddress());//Address:192.168.1.195
		System.out.println("Name:"+ia.getHostName());//Name:DESKTOP-O5MEGBI
	}
	
	public static void test_3()throws Exception
	{
   
		//只要一个主机连上网络,我们就可以获取其主机地址,比如百度腾讯的地址我们都可以获取
		//但是百度主机可能有很多台,而且一个IP地址可能通过映射,对应多个服务器,这个时候返回的IP对象不唯一
		//static InetAddress[] getAllByName(String host) 在给定主机名的情况下,根据系统上配置的名称服务返回其IP地址所组成的数组。 
		InetAddress[] iads = InetAddress.getAllByName("www.baidu.com");
		for(InetAddress iad : iads)
		{
   
			System.out.println("Address:"+iad.getHostAddress());
			System.out.println("Name:"+iad.getHostName());
		}
		/*发现百度果然不止一台主机
		 Address:14.215.177.39
		Name:www.baidu.com
		Address:14.215.177.38
		Name:www.baidu.com
		 */
	}
}

4、传输协议——TCP/UDP
  端口号就只是一个数字标识,因此它没有必要封装成为对象。那么接下来我们说一下参数协议。
  TCP 和UDP的区别——具体见《计算机网络》与视频23-05
在这里插入图片描述
  三次握手用于确定通信的对象在不在线。(23-05,10.30)
  在说传输协议之前,我们先来介绍一个概念Scoket(视频23-07解析)

Socket:传输层实现端到端的通信,因此,每一个传输层连接有两个端点。
那么,传输层连接的端点是什么呢?不是主机,不是主机的IP地址,不是应用进程,也不是传输层的协议端口。传输层连接的端点叫做套接字(socket)。
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。
套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。网络套接字是IP地址与端口的组合。

  我们平时说的网络编程,其实就是socket的编程。简单地说,socket有以下4个特点:

  • Socket就是为网络服务提供的一种机制。
  • 通信的两端都有Socket。
  • 网络通信其实就是Socket间的通信。
  • 数据在两个Socket间通过IO传输。

  不同的传输协议都有自己建立socket断点的方式。因此针对不同的传输协议,我们需要使用不同的java类来创建socket服务。

4.1、UDP协议
  UDP相关介绍如下图:
在这里插入图片描述
  DatagramSocket类与DatagramPacket类描述如下:

public class DatagramSocket extends Object
此类表示用来发送和接收数据报包的套接字。——既用于封装发送接收功能的类。 

public final class DatagramPacket extends Object
此类表示数据报包。——既用于封装数据包的类。

  代码实例如下:这里需要注意的是,发送端与接收端都是独立的运行程序,因此2个程序都有自己的主函数。事实上,在真正的运行环境下,这2个程序应该运行于不同的主机。
  首先,我们按照视频中的代码与方法来操作(注意,用上一节的方法查询,我的主机的IP地址是:192.168.1.195,而原代码中是192.168.1.254,这里要特别注意,因为一个小错误浪费很久!)(此外,注意到达G:\project文件夹后再start打开2个cmd窗口)
在这里插入图片描述
  我们将UDP的接收与发送代码封装为2个类,分别放在不同的java文件夹中运行,这次用我们自己的代码,使用cmd命令行的方法。可以发现,同样正常运行!
在这里插入图片描述
  那么如何在eclipse中同时运行发送端与接收端的程序?
  我们将接收端与发送端各自的类封装到2个java文件中,首先我们运行UdpReceive.java文件,如下图,发现只有一个控制台,下面的“Display Selected Console”图标是灰色的。
在这里插入图片描述
  我们再运行UdpSend.java文件,发现成功发送,而且“Display Selected Console”图标有了颜色,我们可以在这里选择2个程序的控制台,2个程序同时运行,而且可以通过“Display Selected Console”查看他们运行的不同的控制台。发现先启动接收端,再启动发送端,最后接收端收到相关信息!
(同样记住要使用本机的IP地址:192.168.1.195)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
  相应的代码如下:

首先是UdpSend类
-----------------
package pack;

import java.net.*;

/*
需求:通过udp传输方式,将一段文字数据发送出去,既定义一个udp发送端。
思路:
1、建立updsocket服务。
2、提供数据,并将数据封装到数据包中。
3、通过socket服务的发送功能,将数据包发出去。
4、关闭资源。
*/


public class  UdpSend
{
   
	public static void main(String[] args) throws Exception
	{
   
		//1、创建udp服务——通过DatagramSocket对象。
		//DatagramSocket(int port) 创建数据报套接字并将其绑定到本地主机上的指定端口。
		DatagramSocket ds = new DatagramSocket(8888);//发送机器的发送端口是8888

		//2、确定数据,并封装成数据包。DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
		//DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
        //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
		byte[] buf = "lkj dsvb sdvj dfo".getBytes();
		DatagramPacket dp = 
			new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.195"),10000);
		//这里将数据发送到IP地址为192.168.1.254的主机的10000端口
		
		//3、通过socket服务,将已有的数据包发送出去。通过send方法。
		ds.send(dp);

		//4,关闭资源。
		ds.close();
		System.out.println("send complete");
//结果,没有任何现象,UDP是无连接的,这里只有发送端,而接收端没有打开,因此数据没有接收者,直接丢失。
	}
}

--------------------------------------

其次是UdpReceive类
package pack;

import java.net.*;
/*
需求:定义一个应用程序,用于接收udp协议传输的数据并处理的,既定义一个UDP的接收端。
思路:
1、定义udpsocket服务。通常会监听一个端口,其实就是给这个接收数据的网络应用程序定义一个数字标识。(不定义的话系统会分配一个随机的端口)
	方便于明确哪些数据过来该应用程序可以处理。
2、定义一个数据包,因为要存储接收到的字节数据,数据包对象中有更多功能可以提取字节数据中的不同数据信息。
3、通过socket服务的receive方法将收到的数据存入已定义好的数据包中。
4、通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。
5、关闭资源。
*/
public class UdpReceive //这里差点犯错,注意类表示的是对象,是无法抛出异常的,只有方法才能抛出异常,并且最后都得在方法中处理
{
   
	public static void main(String[] args) throws Exception 
	{
   
		//首先,定义UDPSocket服务,并将接收端用端口10000表示
		DatagramSocket ds = new DatagramSocket(10000);
		
/*
 其次,定义数据包接收,我们为了可以持续接收发送端发送过来的数据,我们要将接收端一直开着,否则我们接收端接收一次之后,程序就会停止,
 下一次发送端继续发送,接收端无法收到。因此,我们这里设计一个while循环,这里并不会发生死循环,因为receive是阻塞式方法,
接收到数据它就会执行,没有接受数据就会停止进入休眠状态,因此不会有死循环的现象。
我们修改发送端发送的数据,发现持续循环的接收端可以接收到这些数据!!!

注意:不能将socket服务的定义也放到while中,这种循环建立服务的行为会出现bindException异常(见视频23-09,2.00解析)
*/
		while(true)
		{
   
			byte[] buf = new byte[1024];//定义一个字节数组用于接收
			//DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
			DatagramPacket dp = new DatagramPacket(buf , buf.length);
			
			//使用DatagramSocket对象接收数据包对象,接收的数据包对象保存在本类的dp中
			ds.receive(dp);//注意,receive是一个阻塞式方法,如果没有接收到数据它就会一直等待
			
			//使用DatagramPacket对象的方法取出各类数据——这个时候接收的数据包对象已经保存到dp中
			//InetAddress getAddress() 返回某台机器的 IP地址,此数据报将要发往该机器或者是从该机器接收到的。 
			String IP = dp.getAddress().getHostAddress();//这里是返回发送数据包的机器的主机IP地址
			
			//byte[] getData() 返回数据缓冲区。 ——将接收到的数据的字节数组转换为String
			String data = new String(dp.getData() , 0 , dp.getLength());
			
			// int getPort() 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
			int port = dp.getPort();
			
			//打印接收到的数据
			System.out.println("发送机器:"+IP+"\r\n"+"接收到的数据:"+data+"\r\n"+"发送机器的端口"+port);
		}
		//关闭socket服务
//		ds.close();
	}
}

  
  
  通过上面的例子发现,每一次发送的都是同一句话,就算修改发送内容,还是要重新编译才能运行。我们通过键盘录入的方式来写入是最方便的!相应的代码如下

首先是UdpSend发送端
-------------
package pack;
import java.net.*;
import java.io.*;

public class  UdpSend
{
   
	public static void main(String[] args)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值