一:网络通信协议(以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集合
- Map集合是一个双列集合,一个元素包含了两个值(key和value);
- Map集合中的元素,key和value可以相同,也可以不同;
- Map集合中的元素,key是不允许重复的,value是可以重复的;
- Map中的元素,key和value是一一对应的;
特点:
- HashMap集合底层是哈希表,查询速度特别快,多线程的集合,线程不安全
- 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()
- 使用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);
}
-
键值对 ----------- Set<Map.Entry<K,V>> entrySet()------------使用Entry对象遍历
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集合保存每个分类下所有的商品
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);
}
}
}