java网络编程(七层模型介绍、UDP详解、URL类)

java网络编程




1.1网络通信的模型

OSI将计算机网络体系结构(architecture)划分为以下七层:

  1. 物理层: 将数据转换为可通过物理介质传送的电子信号 相当于邮局中的搬运工人。
  2. 数据链路层: 决定访问网络介质的方式。在此层将数据分帧,并处理流控制。本层指定拓扑结构并提供硬件寻址,相当于邮局中的装拆箱工人。
  3. 网络层: 使用权数据路由经过大型网络 相当于邮局中的排序工人。
  4. 传输层: 提供终端到终端的可靠连接 相当于公司中跑邮局的送信职员。
  5. 会话层: 允许用户使用简单易记的名称建立连接 相当于公司中收寄信、写信封与拆信封的秘书。
  6. 表示层: 协商数据交换格式 相当公司中简报老板、替老板写信的助理。
  7. 应用层: 用户的应用程序和网络之间的接口。



1.2 TCP/UDP通信协议

TCP:稳定协议,需要经历三次握手,四次挥手(断开连接需进行的确认),效率低下,例如打电话

UDP:不稳定协议,由一方就可以直接发送,不需要在乎对方是否接收到,例如发短信,DDOS攻击





1.3 UDP实现聊天功能(TCP通信需建立连接,百度即可)

UDP不需要对方回应,但是需要知道对面的地址


UDP有关的java类:

  1. DatagramPacket 类:建立一个信息包,负责将要发送或接收的信息包装成一个“包裹”

  2. DatagramSocket类:代表着使用UDP通信的n台主机,主机之间可以来回发送消息或接收消息


    1.3.1通信展示

    其中一台主机(以发送消息为主)

    package bin;
    
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;
    import java.net.SocketException;
    import java.net.UnknownHostException;
    /**
     * 发送信息包的类不需要等待,直接发送
     * @author 86155
     *
     */
    public class UdpDemo {
    
    	public static void main(String[] args) throws IOException {
    		//建立一个socket对象用来负责发送消息
    		DatagramSocket socket = new DatagramSocket();
    		
    		//获取目标电脑的ip
    		InetAddress targetIp = InetAddress.getByName("localhost");
    		
    		//设置端口号
    		int port = 8010;//设置端口号只会通过这个端口进行通信,不会监听端口和占用端口号
    		
    		//建立要发送的信息包
    		String msg = "我是UDP发送给你的消息";
    		
    		
    		//建立发送的信息包的对象:参数说明:1、发送的信息字节数组 2、发送的信息长度 3、目标ip地址 4、目标端口号
    		DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,targetIp,port);
    		
    		//发送信息包
    		socket.send(packet);
    		System.out.println("已发送消息,我不管对面是否能接收到");
    		//关闭流
    		socket.close();
    		
    		
    	}
    
    }
    
    
  • 控制台输出的消息:已发送消息,我不管对面是否能接收到

  • 控制台直接结束


    下一台主机(负责接收信息为主)

    package bin;
    
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    /**
     * 在建立一个信息包,可以发送数据,也可以接收数据
     * @author 86155
     *
     */
    public class UpdDemo2 {
    
    	public static void main(String[] args) throws Exception {
    		//这里使用带端口号的类,可以接收数据
    		DatagramSocket socket = new DatagramSocket(8010);
    		//建立一个信息包,既可以发送数据,也可以接收数据
    		byte[] b= new byte[1024];
    		DatagramPacket packet = new DatagramPacket(b, 0,b.length);
    		
    		//这里的packet负责接收数据包
    		socket.receive(packet);//将会阻塞等待接收
    		
    		//将包装好的信息包拆开
    		System.out.println("客户端"+packet.getSocketAddress()+"发送的信息:"+new String(packet.getData()));//带端口号
    		System.out.println("客户端"+packet.getAddress()+"发送的信息:"+new String(packet.getData()));//不带端口号
    		
    		socket.close();
    		
    	}
    
    }
    
  • 控制台输出的消息:客户端/127.0.0.1:50492发送的信息:我是UDP发送给你的消息;客户端/127.0.0.1发送的信息:我是UDP发送给你的消息

  • 控制台直接结束

1.3.2 实现循环接收发送

发送信息的类:

package bin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
 * 发送信息包的类不需要等待,直接发送
 * @author 86155
 *
 */
public class UdpDemo {

	public static void main(String[] args) throws IOException {
		//建立一个socket对象用来负责发送消息
		DatagramSocket socket = new DatagramSocket();
		
		//获取目标电脑的ip
		InetAddress targetIp = InetAddress.getByName("localhost");
		
		//设置端口号
		int port = 8010;//设置端口号只会通过这个端口进行通信,不会监听端口和占用端口号
		
		while(true) {
			//建立要发送的信息包
			//Scanner scan = new Scanner(System.in);与下面实例等价
			BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
			String msg = buffer.readLine();
			
			
			//建立发送的信息包的对象:参数说明:1、发送的信息字节数组 2、发送的信息长度 3、目标ip地址 4、目标端口号
			DatagramPacket packet = new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,targetIp,port);
			
			//发送信息包
			socket.send(packet);
			System.out.println("已发送消息:"+msg);
			if(msg.trim().equals("bye")) {
				break;
			}
		}
		
		//关闭流
		socket.close();	
	}

}
  • 输入:你好 输出:已发送消息:你好

  • 控制台不结束,继续等待输入

    package bin;
    
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    
    /**
     * 在建立一个信息包,可以发送数据,也可以接收数据
     * @author 86155
     *
     */
    public class UpdDemo2 {
    
    	public static void main(String[] args) throws Exception {
    		//这里使用带端口号的类,可以接收数据
    		DatagramSocket socket = new DatagramSocket(8010);
    		
    		while(true) {
    			//建立一个信息包,既可以发送数据,也可以接收数据
    			byte[] b= new byte[1024];
    			DatagramPacket packet = new DatagramPacket(b, 0,b.length);
    			
    			//这里的packet负责接收数据包
    			socket.receive(packet);//将会阻塞等待接收
    			
    			//将包装好的信息包拆开
    			String data = new String(packet.getData());
    			System.out.println("客户端"+packet.getAddress()+"发送的信息:"+data);//不带端口号
    			if(data.trim().equals("bye")) {
    				break;
    			}
    		}
    		
    		
    		socket.close();
    		
    	}
    
    }
    
    
    • 输入:客户端/127.0.0.1发送的信息:你好 输出:无
    • 服务端继续等待接收


1.3.3 聊天系统最终版

思路:一个发送消息类(SendMsg),一个接收消息类(AcceptMsg),这两个类分别继承Runnable接口,通过两个线程的不断监听实现消息的发送和接收。再通过两个people类来测试通信过程

SendMsg类

package com.hr.threads;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class SendMsg implements Runnable {
	private DatagramSocket socket = null;
	private DatagramPacket packet = null;
	private int port;//要发送到那个端口号
	private String address;//目标ip
	
	public SendMsg(int port,String address) {
		this.port = port;
		this.address = address;
	}
	
	@Override
	public void run() {
		try {
			Scanner scan = new Scanner(System.in);
			socket = new DatagramSocket();
			InetAddress targetIp = InetAddress.getByName(address);
			while(true) {
				//包装信息包,将信息包打散成字节
				String msg = scan.nextLine();
				packet = new DatagramPacket(msg.getBytes(), 0,msg.getBytes().length,targetIp,this.port);
				//发送
				socket.send(packet);
				
				if(msg.trim().equals("bye")) {
					break;
				}
			}
			socket.close();
			scan.close();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

AcceptMsg类

package com.hr.threads;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.util.Scanner;

public class AcceptMsg implements Runnable {
	private DatagramSocket socket = null;
	private DatagramPacket packet = null;
	private int port;//要采用那个端口号接收信息
	
	public AcceptMsg(int port) {
		this.port = port;
	}
	
	@Override
	public void run() {
		try {
			socket = new DatagramSocket(this.port);
			while(true) {
				//接收信息包
				byte[] b =new byte[1024];
				packet = new DatagramPacket(b,0,b.length);//规定一次性读取的字节数
				socket.receive(packet);
				//获取信息包里面的信息
				String msg = new String(packet.getData(),0,packet.getLength());
				InetAddress ip =  packet.getAddress();
				
				//信息展示区域
				System.out.println(ip+"发来消息:"+msg);
				if(msg.trim().equals("bye")) {
					break;
				}
			}
			socket.close();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

people1:

package com.hr.people;

import com.hr.threads.AcceptMsg;
import com.hr.threads.SendMsg;

public class People1 {

	public static void main(String[] args) {
		new Thread(new SendMsg(8010,"localhost")).start();
		new Thread(new AcceptMsg(8009)).start();
		
	}

}

people2:

package com.hr.people;

import com.hr.threads.AcceptMsg;
import com.hr.threads.SendMsg;

public class People2 {

	public static void main(String[] args) {
		new Thread(new SendMsg(8009,"localhost")).start();
		new Thread(new AcceptMsg(8010)).start();
		
	}

}


总结

通过测试,两个程序均能实现发送和接收消息,如果结合图形化界面,需要知道两台电脑的ip地址和通信端口号,然后将两端程序代码放入两台电脑当中,复杂过程需深入解决

新的知识点

  1. 代表一个电脑的类:DatagramSocket
  2. 代表一个信息包的类:DatagramPacket
  3. 发送消息时需要知道对方的ip地址和进行通信的端口号,ip地址通过 InetAdress ip = InetAdress.getByName(adress);获取
  4. 接收消息只需要表明自己是以那个端口号进行连接通信的即可
  5. 发送消息需要将信息包装成一个packet,接收消息需要将信息包打散读取,并告知一次读取的字节数




更新

1.4 通过java代码访问网络资源并下载

思路:获取到网络上的某个资源的url地址,将url进行解析,通过java的io流技术将资源下载到本地

1.4.1 URL类一览

package com.hr.threads;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

public class URLDemo {
	public static void main(String[] args) throws IOException {
		//伪造的url地址
		URL url = new URL("http://localhost:8080/sms/mytest.txt?username=zhangsan&pwd=123456");
		
		System.out.println(url.getAuthority());//   localhost:8080
//		System.out.println(url.getContent());//		需要一个能够正确链接到资源的地址
		System.out.println(url.getDefaultPort());	//80
		System.out.println(url.getFile());//   		/sms/mytest.txt?username=zhangsan&pwd=123456
		System.out.println(url.getHost());//		localhost
		System.out.println(url.getPath());//		/sms/mytest.txt
		System.out.println(url.getPort());//		8080
		System.out.println(url.getProtocol());//	http
		System.out.println(url.getQuery());//		username=zhangsan&pwd=123456
		System.out.println(url.getRef());//			null
		System.out.println(url.getUserInfo());//	null
	}
}

1.4.2 URL获取网络资源

package com.hr.threads;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;

public class URLDemo {
	public static void main(String[] args) throws IOException {
		//1、获取到要下载资源的url地址
		URL url = new URL("https://m10.music.126.net/20210206123341/3842575a88f8be7258cfc1779b91e496/yyaac/015a/0"
				+ "55d/525a/60ea8f22932761f95da8e0df17c5d319.m4a");
		//获取到资源名称
		int last = url.getPath().lastIndexOf("/");
		String resource = url.getPath().substring(last);
		
		//2、连接到资源,将资源根据协议转换成相应的对象
		URLConnection openConn = url.openConnection();
		HttpURLConnection conn = (HttpURLConnection) openConn;
		
		//3、将这个资源放入输入流中(放入本地内存中)
		InputStream is = conn.getInputStream();
		
		//4、通过io输出流将资源下载到本地
		FileOutputStream fis = new FileOutputStream("E:\\"+resource);
		
		byte[] b = new byte[1024];
		int len= -1;
		while((len = is.read(b))!= -1) {
			fis.write(b,0,len);
		}
		//5、关闭资源
		fis.close();
		is.close();
		conn.disconnect();
	}
}

注意:不要将资源放在C盘中,因为java程序没有写入C盘中的权限,所以放在桌面会报错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值