0
点赞
收藏
分享

微信扫一扫

java_web 快速入门之第九章 UDP连接&Map集合

caoxingyu 2022-04-27 阅读 53
java前端

一:网络通信协议(以TCP/IP模型为例)

     常用网络通信协议:TCP/IP协议、IPX/SPX协议、NetBEUI协议等。

  TCP/IP,即Transmission Control Protocol/Internet Protocol的简写
    中译名为传输控制协议/因特网互联协议,是Internet最基本的协议、Internet国际互联网络的基础。

二:IP地址和端口号(组合就是网络套接字)

      URL url = new URL("文件的地址");
    1. IP 地址:InetAddress(在Java中使用InetAddress类代表IP)

    2. 端口号
        端口号就是标识正在计算机上运行的进程(程序)
        不同的进程有不同的端口号
        被规定为一个 16 位的整数 0~65535。
 

  重点:TCP|IP模型中的传输层2个重要的协议:TCP协议和UDP协议

 

三:TCP协议详解

           (1)使用TCP协议前,须先建立TCP连接,形成传输数据通道

            (2) 传输前,采用“三次握手”方式,点对点通信,是可靠的。四次挥手

            (3) TCP协议进行通信的两个应用进程:客户端、服务端。

            (4) 在连接中可进行大数据量的传输

            (5) 传输完毕,需释放已建立的连接,

                    TCP网络编程 基于Socket套接字实现服务端与客户端对话
  

 

四:UDP协议详解(无连接)

       (1)将数据、源、目的封装成数据包,不需要建立连接

      (2) 每个数据报的大小限制在64K内(以容器的形式进行发送)

      (3) 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的

     (4) 可以广播发送

     (5) 发送数据结束时无需释放资源,开销小,速度快

    UDP没有服务器与客户端这样的说法,有发送方和接收方
            DatagramSocket----比作发送方    比作接收方
            DatagramPacket-----数据打包对象

        a.类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序。
        b.UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证 UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。
        c.DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
        d.UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。如同发快递包裹一样。

五: UDP网络通信编程的基本概念

       类DatagramSocket和DatagramPacktet[数据包|数据报]实现了基于UDP协议网络程序
        UDP数据报通过数据报套接字DatagramSocket发送和接受,系统不保证UDP数据报一定能安全送到目的地,也不能确定什么时候可以抵达。
        DatagramPacket对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。
       UDP协议中每个数据报都给出了完整的地址信息,因此无需建立发送方和接收方的连接。

六:UDP网络编程基本流程

        注意:发送端与接收端是两个独立的运行程序      

   

 

   UDP 不止支持一对一的传输方式,同样支持一对多,多对多,多对一的方式,也就是说 UDP 提供了单播,多播,广播的功能。

  • 单播

        是指封包在计算机网络的传输中,目的地址为单一目标的一种传输方式。
        【你对小月月喊“小月月”,那么只有小月月回过头来答应你。】
 

  • 广播

           是指封包在计算机网络中传输时,目的地址为网络中所有设备的一种传输方式。实际上,这里所 说的“所有设备”也是限定在一个范围之中,称为“广播域”。
                255.255.255.255        
        【你在公司大喊一声“放假了”, 全部同事都会响应,大叫爽死了。】

  • 组播

         也叫多播, 多点广播或群播。 指把信息同时传递给一组目的地址。它使用策略是最高效的,因为消息在每条网络链路上只需传递一次,而且只有在链路分叉的时候,消息才会被复制。

多播组通过 D 类 IP 地址和标准 UDP 端口号指定。D 类 IP 地址在 224.0.0.0 和 239.255.255.255 的范围内(包括两者)。地址 224.0.0.0 被保留,不应使用。
        【你在大街上大喊一声“美女”, 会有一群女性回头看你。】

       发送的IP地址:238.222.111.0
		接收方:
		MulticastSocket ms=new MulticastSocket(4399);
		/* 加入那个组? */
		ms.joinGroup(InetAddress.getByName("238.222.111.0"));
		byte[] bs=new byte[100];
		DatagramPacket dp=new DatagramPacket(bs, bs.length);
		ms.receive(dp);
		System.out.println(new String(bs).trim());
		/* 离开小组 */
		ms.leaveGroup(InetAddress.getByName("238.222.111.0"));
		ms.close();

  对于三种udp协议
         1.发送方完全相同,单播是指定一个ip
               广播是指定255.255.255.255这个ip
               组播是指定D类ip(224-239)
          2.接收方单播直接指定一个ip与端口
              广播不能指定ip,只要端口
              组播要使用MulticastSocket,
              然后使用joinGroup加入该组

 

案例:UDP网络通信编程应用案例

                          接收端A---------------UDPReceiverA.java  

package com.test;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
 * 接收端
 * 
 */
public class UDPReceiverA {
	public static void main(String[] args) throws Exception {
		// 1.创建一个DatagramSocket对象,准备在9999接收数据
		DatagramSocket socket = new DatagramSocket(9999);
		// 2.构建一个DatagramPacket对象,准备接收数据
		// 限制64K
		byte[] buf = new byte[1024];
		DatagramPacket packet = new DatagramPacket(buf, buf.length);
		// 3.调用接收方法,将通过网络传输的DatagramPacket对象
		// 填充到packet对象
		// 注意事项:当有数据包发送到本机的9999端口时,就会接收到数据,如果没有数据包发送到本机的9999端口,就会堵塞等待。
		System.out.println("接收端A 等待接收...");
		socket.receive(packet);
		// 4.可以把packet进行拆包,取出数据,进行显示
		int length = packet.getLength();// 实际接收到的数据字节长度
		byte[] data = packet.getData();// 接收到数据
		String s = new String(data, 0, length);
		System.out.println("接收的内容为: " + s);
		// 关闭资源
		socket.close();
		System.out.println("A exit");
	}
}

         发送端B-------------------------------------------UDPSenderB.java

package com.test;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 发送端===>也可以接收数据
 *
 */
public class UDPSenderB {
	public static void main(String[] args) throws Exception {

		// 创建 DatagramSocket对象,准备在9998端口接收数据
		DatagramSocket socket = new DatagramSocket(9998);

		// 将需要发送的数据,封装到DatagramPacket对象
		byte[] data = "hello 明天吃火锅".getBytes();
		DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("127.0.0.1"), 9999);

		// 发送数据
		socket.send(packet);

		// 关闭资源
		socket.close();

		System.out.println("B exit");
	}
}

案例: UDP传输协议演示

----------------------------------------------发送方-------------------------------------------------------------------------

​
package com.zking.test2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * UDP传输协议
 * 
 * 
 * 
 * 1.一句话发送和接收 端口号9999
 * 
 * 发送方
 * 
 * @author Administrator
 *
 */
public class Sender {
	public static void main(String[] args) throws Exception {
		// 1.通过DatagramSocket是一个发送方,同时也可以比作成一个接收方
		DatagramSocket ds = new DatagramSocket(7979);
		System.out.println("发送方已开启");
		// 准备数据
		String str = "李太白今晚约会";
		// 进行打包
		DatagramPacket dp = null;
		// 已经打包成功
		// buf: 字节数组 数据包封装字节数组
		// length 字节数组的长度
		// address 发送方的IP地址
		// port 端口号
		dp = new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 9999);
		// 发送
		ds.send(dp);
		System.out.println("发送完毕");
		
		//接收数据
		byte[] buf = new byte[1024];
		dp = new DatagramPacket(buf, buf.length);
		ds.receive(dp);//接收
		int length = dp.getLength();
		byte[] data = dp.getData();
		System.out.println(new String(data,0,length));
		
		ds.close();

	}
}

​

 

------------------------------------------接收方-----------------------------------------------------------------------------

package com.zking.test2;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 接收方-------也是发送方法
 * 
 * @author Administrator
 *
 */
public class Revicer {

	public static void main(String[] args) throws Exception {

		// 1.通过DatagramSocket是一个接收方,同时也可以比作成一个发送方
		DatagramSocket ds = new DatagramSocket(9999);// 该端口号来源与发送方
		System.out.println("接收方已开启成功......");
		// 2.准备一个数据包(DatagramPacket)
		DatagramPacket dp = null;// 定义好
		byte[] buf = new byte[1024];
		dp = new DatagramPacket(buf, buf.length);
		// 调用接收的方法将接收的数据进行封包。
		ds.receive(dp);
		// System.out.println(new String(buf).trim());
		// 获取数据的实际大小的长度
		int length = dp.getLength();
		// 获取打包的实际数据
		byte[] data = dp.getData();
		System.out.println(new String(data, 0, length));
		
		
		
		//接收方如果要回复信息
		String str = "好的带上装备今晚是个好日子";
		dp = new DatagramPacket(str.getBytes(), 
				str.getBytes().length, 
				InetAddress.getByName("127.0.0.1"), 7979);
		
		ds.send(dp);
		
		// 关闭
		ds.close();

	}

}

案例:群聊客户端与服务端

--------------------------------------------------服务器端---------------------------------------------------------------

package com.zking.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Vector;

/**
 * 服务器端
 * 
 * @author Administrator
 *
 */
public class MyServerChat {
	public static void main(String[] args) throws Exception {
		// 开启服务器
		ServerSocket ss = new ServerSocket(9797);
		System.out.println("服务器已开启成功");

		// 调用方法无线接收客户端 (群聊---多个客户端)

		// Vector
		Vector<Socket> vc = new Vector<Socket>();
		// 无线接收客户端
		while (true) {
			// 连一个
			Socket sk = ss.accept();
			// 加一个
			vc.add(sk);
			// 转一个
			new Thread(new MyServerThreadChat(vc, sk)).start();
		}
	}
}

class MyServerThreadChat implements Runnable {
	private Vector<Socket> vc;
	private Socket sk;

	public MyServerThreadChat(Vector<Socket> vc, Socket sk) {
		this.vc = vc;
		this.sk = sk;
	}

	@Override
	public void run() {
		while (true) {
			// 通过当前传递过来的连接服务器的客户端---sk 获取网络流读取内容
			try {
				InputStream is = sk.getInputStream();
				InputStreamReader isr = new InputStreamReader(is);
				BufferedReader br = new BufferedReader(isr);
				String readLine = br.readLine();
				System.out.println(readLine);
				if("bye".equals(readLine)) {
					vc.remove(sk);
				}
				// 当前的内容转发给其它客户端--不包括自己
				for (Socket socket : vc) {
					if (socket != null) {// 排除空对象
						if (socket != sk) {
							// 将内容写入其它所有客户端
							OutputStream os = socket.getOutputStream();
							OutputStreamWriter osw = new OutputStreamWriter(os);
							BufferedWriter bw = new BufferedWriter(osw);
							bw.write(readLine);
							bw.newLine();
							bw.flush();
						}
					}
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

}

-------------------------------------------------------------客户端-----------------------------------------------------------

package com.zking.test;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;

/**
 * 客户端
 * @author Administrator
 *
 */
public class MyClientChat {
	public static void main(String[] args) throws Exception{
		//开启客户端
		Socket sk = new Socket("127.0.0.1", 9797);
		System.out.println("OK了........");
		//发送-------输出
		new Thread(new MyClientThreadChat1(sk)).start();
		
		//接收-----输入
		new Thread(new MyClientThreadChat1(sk)).start();
	}
}

//线程发送
class MyClientThreadChat1 implements Runnable{
	private Socket sk = null;
	public MyClientThreadChat1(Socket sk) {
		this.sk = sk;
	}
	@Override
	public void run() {
		while(true) {
			try {
				OutputStream os = sk.getOutputStream();
				BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
				Scanner sc = new Scanner(System.in);
				System.out.println("发送内容: ");
				String next = sc.next();
				bw.write(next);
				bw.newLine();
				bw.flush();
				if("bye".equalsIgnoreCase(next)) {
					break;
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}


//无线接收
class MyClientThreadChat2 implements Runnable{
	private Socket sk = null;
	public MyClientThreadChat2(Socket sk) {
		this.sk = sk;
	}
	@Override
	public void run() {
		while(true) {
			try {
				InputStream is = sk.getInputStream();
				BufferedReader br = new BufferedReader(new InputStreamReader(is));
				
				String readLine = br.readLine();
				System.out.println("   "+readLine);
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
}





七:Map集合

  1. Map集合是一个双列集合,一个元素包含了两个值(key和value);
  2. Map集合中的元素,key和value可以相同,也可以不同;
  3. Map集合中的元素,key是不允许重复的,value是可以重复的;
  4. Map中的元素,key和value是一一对应的;

 

特点:

  1. HashMap集合底层是哈希表,查询速度特别快,多线程的集合,线程不安全
  2. HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致,并且可以存储null键和null值;

特点:

     1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序);
     2. LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是有序的;
 

特点:底层也是一个哈希表,是线程安全的集合,是单线程的集合,速度慢,不能存储null值null键;

 

基本使用:

  •  添加的方法----put

Map map = new HashMap();		
map.put("杨过", "小龙女");
map.put("李太白", "王昭君");
map.put("李世民", "武则天");
  • 获取集合大小

System.out.println(map.size());//3
  • 清除集合中所有的键值对

map.clear();
System.out.println(map.size());//0
  • 判断map集合中是否包含指定的键

boolean containsKey = map.containsKey("李太白");
System.out.println(containsKey);
  • 判断map集合中是否包含指定的值

boolean containsValue = map.containsValue("小龙女");
System.out.println(containsValue);
  • 根据指定的键获取指定的值

Object object = map.get("李太白");
System.out.println(object);
		
  • 判断map集合是否为空

boolean empty = map.isEmpty();
System.out.println(empty);
  • 根据键移除整个元素,返回对应的值

Object remove = map.remove("李太白");
System.out.println(remove);
System.out.println(map.size());

集合遍历:

  •        Collection 单列集合

Collection values = map.values();//获取所有的值
	for (Object object : values) {
			System.out.println(object);
		}

  •     Map集合遍历方法------键找值----Set keySet()---------Set keySet()

  1. 使用Map集合中的keySet(),把Map集合中所有的key取出来存储到一个set集合中;
  2. 遍历set集合,获取Map中的每一个key;
  3. 通过Map集合的get()方法,获取key对应的value;
Set keySet = map.keySet();
		for (Object object : keySet) {
		System.out.println(object);
		}
需求:定义一个Map集合  一次行进行遍历输出
		Map map = new HashMap();
		map.put("ZG", "中国");
		map.put("MG", "美国");
		map.put("RB", "日本");
		map.put("YG", "英国");
		map.put("XBY", "西班牙");
		
		//得到所有的键
		Set keySet = map.keySet();
		for (Object object : keySet) {
			//根据获取的键再获取对应的值
			Object object2 = map.get(object);
			System.out.println(object+"----"+object2);
	}

     

  •   键值对 ----------- Set<Map.Entry<K,V>> entrySet()------------使用Entry对象遍历

         entrySet  对象,将集合中的每一个键值对进行封装  得到一个entry对象 ,因此键和值作为了        Entry对象的属性   可以通过entrySet+迭代器进行遍历

  1. 使用Map集合中的keySet(),把Map集合中所有的key取出来存储到一个set集合中;
  2. 遍历set集合,获取Map中的每一个key;
  3. 通过Map集合的get()方法,获取key对应的value;

     


		//1.调用entrySet方法将集合中的键值对封装每一个Entry对象存储到Set集合中
		Set entrySet = map.entrySet();
	//2.通过所谓的迭代器
		Iterator iterator = entrySet.iterator();
		//3.遍历迭代器
		while(iterator.hasNext()) {//如果迭代器中存在下一条数据
			//直接获取
		System.out.println(iterator.next());
		Entry<String, String> entry = (Entry<String, String>) iterator.next();
			System.out.println(entry.getKey()+ "\t"+entry.getValue());//获取所有的键和值
			
		}
		

案例:有10间监狱   每间监狱中有10个犯人
            每间监狱的房间名称:   1号   2号  .....

                要求:使用Map集合完成以上需求定义

Map<String,List<FanRen>> map2 = new HashMap<String,List<FanRen>>();
		
		//模拟数据
		for (int i = 1; i <= 10; i++) {//10间房
			List<FanRen> list = new ArrayList<FanRen>();
			//每间房间  有10个犯人
			for (int j = 1; j <= 10; j++) {
				FanRen fr = new FanRen(j+1, "张三"+j, "男");
				//每创建一个犯人  加入List集合
				list.add(fr);
			}
			//将每间房加入map集合
			map2.put(i+"号房间", list);
		}
		
		System.out.println(map2.size());
		
		//遍历显示
		Set<Entry<String, List<FanRen>>> entrySet = map2.entrySet();
		
		Iterator<Entry<String, List<FanRen>>> iterator = entrySet.iterator();
		
		while(iterator.hasNext()) {
			Entry<String, List<FanRen>> next = iterator.next();
			System.out.println("监狱房间号: "+next.getKey());
			List<FanRen> value = next.getValue();
			for (FanRen fanRen : value) {
				System.out.println("\t"+fanRen);
			}
		}

  • 通过Map集合保存每个分类下所有的商品

Map<GoodsType,List<Goods>> map3 = new HashMap<GoodsType,List<Goods>>();)

案例:文件发送接收

------------------------------------------发送方---------------------------------------------------------------------------

package com.zking.test3;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * 发送方
 * @author Administrator
 *
 */
public class Sender {
	public static void main(String[] args) throws Exception{
		//三边原则:边读  边打包 边发送
		
		//定义发送方
		DatagramSocket ds = new DatagramSocket();
		//准备数据报容器
		DatagramPacket dp = null;
		
		
		//1.指定图片文件
		File file = new File("D:\\a.jpg");
		//通过流进行读取
		FileInputStream fis = new FileInputStream(file);
		//转缓冲流
		BufferedInputStream bis = new BufferedInputStream(fis);
		
		//读取文件每次读取一个字节数组
		byte[] bytes = new byte[10];
		int len = 0;//保存读取后的下表
//		边读
		int count = 10;
		while(-1!=(len = bis.read(bytes))) {
//			边打包
			dp = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("127.0.0.1"), 7979);
//			边发送
			ds.send(dp);
			Thread.sleep(100);
			count+=10;
			System.out.println(count);
		}
		//问题:接受方没有关闭-----手动再发送一个标记(如果接收方接到标记后,方可关闭)
		dp = new DatagramPacket("sb".getBytes(), "sb".getBytes().length,
				InetAddress.getByName("127.0.0.1"), 7979);
		
		ds.send(dp);
		
		
		
		
		
		
	}
}

------------------------------------------接收方----------------------------------------------------------------------------

package com.zking.test3;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 接收方
 * @author Administrator
 *
 */
public class Revicer {

	public static void main(String[] args) throws Exception{
		// 边收   边拆  边写
		//接收方对象
		DatagramSocket ds = new DatagramSocket(7979);
		
		DatagramPacket dp = null;
		
		//定义保存的文件对象路径
		File file = new File("D:\\c.jpg");
		FileOutputStream fos = new FileOutputStream(file);
		BufferedOutputStream bos = new BufferedOutputStream(fos);
		//定义一个字节数组
		byte [] bytes = new byte[10];
		int count = 10;
		while(true) {
			//接收数据包
			dp = new DatagramPacket(bytes, bytes.length);
			//边收
			ds.receive(dp);
			
			if(new String(bytes).contains("sb")) {//bytes  sb  结束了
				System.out.println("全部接收完毕");
				break;
			}
			count+=10;
			System.out.println(count);
			bos.write(bytes);
		}
		
		
		
	}

}
举报

相关推荐

0 条评论