一:网络通信协议(以)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传输层在模型中有两个重要协议: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网络编程的基本流程
核心两类/对象DatagramSocket与DatagramPacket
建立发送端和接收端
建立数据包
调用DatagramSocket发送和接收方法。
关闭DatagramSocket
注:发送端和接收端是两个独立的操作程序
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,和一个发送端B
- 接收端A在9999端口等待接收数据(receiver)
- 发送端A向接收端B发送数据"hello,明天吃火锅~"
- 接收端B接收到发送端A发送的数据,回复"好的,明天见",再退出
- 发送端接收回复的数据,再退出
接收端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集合
- Map集合是一个双列集合,一个元素包含了两个值(key和value);
- Map集合中的元素,key和value可以相同,也可以不同;
- Map集合中的元素,key是不允许重复的,value是可以重复的;
- Map中的元素,key和value是一一对应的;
HashMap
java.util.HashMap
implements Map<k,v>接口;
特点:
- HashMap集合底层是哈希表,查询速度特别快,多线程的集合,线程不安全
- HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致,并且可以存储null键和null值;
LinkedHashMap
- java.util.LinkedHashMap<k,v>集合 extends HashMap<k,v>集合
特点:
1.LinkedHashMap集合底层是哈希表+链表(保证迭代的顺序); 2. LinkedHashMap集合是一个有序的集合,存储元素和取出元素的顺序是有序的;
HashTable
java.util.LinkedHashtable
<K,V>集合 implements Map<K,V>接口
特点:底层也是一个哈希表,是线程安全的集合,是单线程的集合,速度慢,不能存储null值null键;
方法集合
put(K key , V value) : 把指定的键与指定的值添加到Map集合中;
- 存储键值对的时候,k不重复的情况下,返回的v是null;
- 存储键值对的时候,k重复的情况下,会使用新的value替换重复的k对应的value,然后返回被替换的value;
remove(Object key) : 把指定的键所对应的键值对元素,在Map集合中删除,返回被删除元素的值;
- 如果key存在,v返回被删除的值;
- 如果Key不存在,v返回null;
get(Object key) : 根据指定的键,在Map中获取对应的值;
- 如果key存在,v返回被对应的值;
- 如果Key不存在,v返回null;
containsKey(Obejct key) : 判断集合中是否包含指定的key;
- 包含返回true;
- 不包含返回false;
基本使用:
Map map = new HashMap();
map.put("杨过", "小龙女");
map.put("李太白", "王昭君");
map.put("李世民", "武则天");
System.out.println(map.size());//3
map.clear();
System.out.println(map.size());//0
boolean containsKey = map.containsKey("李太白");
System.out.println(containsKey);
boolean containsValue = map.containsValue("小龙女");
System.out.println(containsValue);
Object object = map.get("李太白");
System.out.println(object);
boolean empty = map.isEmpty();
System.out.println(empty);
Object remove = map.remove("李太白");
System.out.println(remove);
System.out.println(map.size());
集合遍历:
Collection values = map.values();//获取所有的值
for (Object object : values) {
System.out.println(object);
}
-
键找值
- 使用Map集合中的keySet(),把Map集合中所有的key取出来存储到一个set集合中;
- 遍历set集合,获取Map中的每一个key;
- 通过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);
}
entrySet 对象,将集合中的每一个键值对进行封装 得到一个entry对象 ,因此键和值作为了 Entry对象的属性 可以通过entrySet+迭代器进行遍历
- 使用Map集合中的keySet(),把Map集合中所有的key取出来存储到一个set集合中;
- 遍历set集合,获取Map中的每一个key;
- 通过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<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);
}
}
}