16.3.2.2 BufferedReader
例:
public class BufferedReader_ { public static void main(String[] args) throws Exception { String filePath = "e:\\a.java"; //创建bufferedReader BufferedReader bufferedReader = new BufferedReader(new FileReader(filePath)); //读取 String line; //按行读取, 效率高 //说明 //1. bufferedReader.readLine() 是按行读取文件 //2. 当返回null 时,表示文件读取完毕 while ((line = bufferedReader.readLine()) != null) { System.out.println(line); } //关闭流, 这里注意,只需要关闭 BufferedReader ,因为底层会自动的去关闭 节点流 //FileReader。 /* public void close() throws IOException { synchronized (lock) { if (in == null) return; try { in.close();//in 就是我们传入的 new FileReader(filePath), 关闭了. } finally { in = null; cb = null; } } } */ bufferedReader.close(); } }
16.3.2.3 InputStreamReader
基本介绍:
- InputStreamReader:Reader的子类,可以将lnputStream(字节流)包装成Reader(字符流)
- 当处理纯文本数据时,如果使用宇符流效率重高,井且可以有效解決中文问题,所以建议字节流转换成字符流
例:
public class InputStreamReader_ { public static void main(String[] args) throws IOException { String filePath = "e:\\a.txt"; //解读 //1. 把 FileInputStream 转成 InputStreamReader //2. 指定编码 gbk //InputStreamReader isr = new InputStreamReader(new FileInputStream(filePath), "gbk"); //3. 把 InputStreamReader 传入 BufferedReader //BufferedReader br = new BufferedReader(isr); //将2 和 3 合在一起 BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(filePath), "gbk")); //4. 读取 String s = br.readLine(); System.out.println("读取内容=" + s); //5. 关闭外层流 br.close(); } }
16.4 输出流⭐️
网络异常,图片无法展示|
基本介绍:
16.4.1 OutputStream
16.4.1.1 FileOutputStream
网络异常,图片无法展示|
常用方法:
使用方法:
public class FileOutputStream01 { public static void main(String[] args) { } /** * 演示使用FileOutputStream 将数据写到文件中, * 如果该文件不存在,则创建该文件 */ @Test public void writeFile() { //创建 FileOutputStream对象 String filePath = "e:\\a.txt"; FileOutputStream fileOutputStream = null; try { //得到 FileOutputStream对象 //说明 //1. new FileOutputStream(filePath) 创建方式,当写入内容是,会覆盖原来的内容 //2. new FileOutputStream(filePath, true) 创建方式,当写入内容是,是追加到文件后面 fileOutputStream = new FileOutputStream(filePath, true); //写入一个字节 //fileOutputStream.write('H');// //写入字符串 String str = "hsp,world!"; //str.getBytes() 可以把 字符串-> 字节数组 //fileOutputStream.write(str.getBytes()); /* write(byte[] b, int off, int len) 将 len字节从位于偏移量 off的指定字节数组写入此文件输出流 */ fileOutputStream.write(str.getBytes(), 0, 3); } catch (IOException e) { e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
16.4.1.2 BufferedOutputStream
基本介绍:
BufferedOutputstream是字节流,实现缓冲的输出流,可以将多个字节写入底层输出流中,而不必对每次字节写入调用底层
系统
网络异常,图片无法展示|
常用方法:
例:
public class BufferedCopy02 { public static void main(String[] args) { // String srcFilePath = "e:\\Koala.jpg"; // String destFilePath = "e:\\hsp.jpg"; // String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi"; // String destFilePath = "e:\\hsp.avi"; String srcFilePath = "e:\\a.java"; String destFilePath = "e:\\a3.java"; //创建BufferedOutputStream对象BufferedInputStream对象 BufferedInputStream bis = null; BufferedOutputStream bos = null; try { //因为 FileInputStream 是 InputStream 子类 bis = new BufferedInputStream(new FileInputStream(srcFilePath)); bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); //循环的读取文件,并写入到 destFilePath byte[] buff = new byte[1024]; int readLen = 0; //当返回 -1 时,就表示文件读取完毕 while ((readLen = bis.read(buff)) != -1) { bos.write(buff, 0, readLen); } System.out.println("文件拷贝完毕~~~"); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 , 关闭外层的处理流即可,底层会去关闭节点流 try { if(bis != null) { bis.close(); } if(bos != null) { bos.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
16.4.1.3 ObjectOutputStream
基本介绍:
- Objectoutputstream 提供序列化功能
- 序列化就是在保存数据时,保存数据的值和数据类型
- 列化就是在恢复数据时,恢复数据的值和数据类型
- 需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:Serializable和Externalizable
例:
public class ObjectOutStream_ { public static void main(String[] args) throws Exception { //序列化后,保存的文件格式,不是存文本,而是按照他的格式来保存 String filePath = "e:\\data.dat"; ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath)); //序列化数据到 e:\data.dat oos.writeInt(100);// int -> Integer (实现了 Serializable) oos.writeBoolean(true);// boolean -> Boolean (实现了 Serializable) oos.writeChar('a');// char -> Character (实现了 Serializable) oos.writeDouble(9.5);// double -> Double (实现了 Serializable) oos.writeUTF("韩顺平教育");//String //保存一个dog对象 oos.writeObject(new Dog("旺财", 10, "日本", "白色")); oos.close(); System.out.println("数据保存完毕(序列化形式)"); } }
注意事项和细节:
- 读写顺序要一致
- 要求实现序列化或反序列化对象,需要实现 Serializable
- 序列化的类中建议添加SerialVersionUID,为了提高版本兼容性
- 序列化对象时,默认将里面所有属性都进行序列化,但除了static或transient修饰的成员
- 序列化对象时,要求里面属性的类型也需要实现序列化接口
- 序列化具备可继承性,也就是如果某类已经实现了序列化,则它的所有子类也己经默认实现了序列化
16.4.1.4 PrintStream
例:
public class PrintStream_ { public static void main(String[] args) throws IOException { PrintStream out = System.out; //在默认情况下,PrintStream 输出数据的位置是 标准输出,即显示器 /* public void print(String s) { if (s == null) { s = "null"; } write(s); } */ out.print("john, hello"); //因为print底层使用的是write , 所以我们可以直接调用write进行打印/输出 out.write("韩顺平,你好".getBytes()); out.close(); //我们可以去修改打印流输出的位置/设备 //1. 输出修改成到 "e:\\f1.txt" //2. "hello, 韩顺平教育~" 就会输出到 e:\f1.txt //3. public static void setOut(PrintStream out) { // checkIO(); // setOut0(out); // native 方法,修改了out // } System.setOut(new PrintStream("e:\\f1.txt")); System.out.println("hello, 韩顺平教育~"); } }
16.4.2 Writer
16.4.2.1 FileWriter
常用方法:
- new File Writer (File/String):覆盖模式,相当于流的指针在首端
- new FileWriter (File/String.true):追加模式,相当于流的指针在尾端
- write(int):写入单个字符
- write(char[]):写入指定数组
- write(char[],off len):写入指定数组的指定部分
- write (string):写入整个字符律
- write(string,off,len):写入字符串的指定部分
方法使用:
public class FileWriter_ { public static void main(String[] args) { String filePath = "e:\\note.txt"; //创建FileWriter对象 FileWriter fileWriter = null; char[] chars = {'a', 'b', 'c'}; try { fileWriter = new FileWriter(filePath);//默认是覆盖写入 // 3) write(int):写入单个字符 fileWriter.write('H'); // 4) write(char[]):写入指定数组 fileWriter.write(chars); // 5) write(char[],off,len):写入指定数组的指定部分 fileWriter.write("韩顺平教育".toCharArray(), 0, 3); // 6) write(string):写入整个字符串 fileWriter.write(" 你好北京~"); fileWriter.write("风雨之后,定见彩虹"); // 7) write(string,off,len):写入字符串的指定部分 fileWriter.write("上海天津", 0, 2); //在数据量大的情况下,可以使用循环操作. } catch (IOException e) { e.printStackTrace(); } finally { //对应FileWriter , 一定要关闭流,或者flush才能真正的把数据写入到文件 //老韩看源码就知道原因. /* 看看代码 private void writeBytes() throws IOException { this.bb.flip(); int var1 = this.bb.limit(); int var2 = this.bb.position(); assert var2 <= var1; int var3 = var2 <= var1 ? var1 - var2 : 0; if (var3 > 0) { if (this.ch != null) { assert this.ch.write(this.bb) == var3 : var3; } else { this.out.write(this.bb.array(), this.bb.arrayOffset() + var2, var3); } } this.bb.clear(); } */ try { //fileWriter.flush(); //关闭文件流,等价 flush() + 关闭 fileWriter.close(); } catch (IOException e) { e.printStackTrace(); } } System.out.println("程序结束..."); } }
文件拷贝:
public class FileCopy { public static void main(String[] args) { //完成 文件拷贝,将 e:\\Koala.jpg 拷贝 c:\\ //思路分析 //1. 创建文件的输入流 , 将文件读入到程序 //2. 创建文件的输出流, 将读取到的文件数据,写入到指定的文件. String srcFilePath = "e:\\Koala.jpg"; String destFilePath = "e:\\Koala3.jpg"; FileInputStream fileInputStream = null; FileOutputStream fileOutputStream = null; try { fileInputStream = new FileInputStream(srcFilePath); fileOutputStream = new FileOutputStream(destFilePath); //定义一个字节数组,提高读取效果 byte[] buf = new byte[1024]; int readLen = 0; while ((readLen = fileInputStream.read(buf)) != -1) { //读取到后,就写入到文件 通过 fileOutputStream //即,是一边读,一边写 fileOutputStream.write(buf, 0, readLen);//一定要使用这个方法 } System.out.println("拷贝ok~"); } catch (IOException e) { e.printStackTrace(); } finally { try { //关闭输入流和输出流,释放资源 if (fileInputStream != null) { fileInputStream.close(); } if (fileOutputStream != null) { fileOutputStream.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
注意事项:
- FileWriter使用后,必须要关闭(close)或刷新(flush),否则写入不到指定的文件
16.4.2.2 BufferedWriter
例:
public class BufferedWriter_ { public static void main(String[] args) throws IOException { String filePath = "e:\\ok.txt"; //创建BufferedWriter //说明: //1. new FileWriter(filePath, true) 表示以追加的方式写入 //2. new FileWriter(filePath) , 表示以覆盖的方式写入 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(filePath)); bufferedWriter.write("hello, 韩顺平教育!"); bufferedWriter.newLine();//插入一个和系统相关的换行 bufferedWriter.write("hello2, 韩顺平教育!"); bufferedWriter.newLine(); bufferedWriter.write("hello3, 韩顺平教育!"); bufferedWriter.newLine(); //说明:关闭外层流即可 , 传入的 new FileWriter(filePath) ,会在底层关闭 bufferedWriter.close(); } }
文件拷贝:
public class BufferedCopy_ { public static void main(String[] args) { //1. BufferedReader 和 BufferedWriter 是安装字符操作 //2. 不要去操作 二进制文件[声音,视频,doc, pdf ], 可能造成文件损坏 //BufferedInputStream //BufferedOutputStream String srcFilePath = "e:\\a.java"; String destFilePath = "e:\\a2.java"; // String srcFilePath = "e:\\0245_韩顺平零基础学Java_引出this.avi"; // String destFilePath = "e:\\a2韩顺平.avi"; BufferedReader br = null; BufferedWriter bw = null; String line; try { br = new BufferedReader(new FileReader(srcFilePath)); bw = new BufferedWriter(new FileWriter(destFilePath)); //说明: readLine 读取一行内容,但是没有换行 while ((line = br.readLine()) != null) { //每读取一行,就写入 bw.write(line); //插入一个换行 bw.newLine(); } System.out.println("拷贝完毕..."); } catch (IOException e) { e.printStackTrace(); } finally { //关闭流 try { if(br != null) { br.close(); } if(bw != null) { bw.close(); } } catch (IOException e) { e.printStackTrace(); } } } }
16.4.2.3 OutputStreamWriter
基本介绍:
- OutputStreamWriter:Writer的子类,实现将Outputstream(字节流)包装成Writer(字符流)
- 当处理纯文本数据时,如果使用宇符流效率重高,井且可以有效解決中文问题,所以建议格字节流转快成字符流
- 可以在使用时指定编码格式
例:
public class OutputStreamWriter_ { public static void main(String[] args) throws IOException { String filePath = "e:\\hsp.txt"; String charSet = "utf-8"; OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), charSet); BufferedWriter bw = new BufferedWriter(osw); bw.write("hi, 韩顺平教育"); bw.close(); System.out.println("按照 " + charSet + " 保存文件成功~"); } } 16.4.2.4 PrintWrite • 例: public class PrintWriter_ { public static void main(String[] args) throws IOException { //PrintWriter printWriter = new PrintWriter(System.out); PrintWriter printWriter = new PrintWriter(new FileWriter("e:\\f2.txt")); printWriter.print("hi, 北京你好~~~~"); printWriter.close();//flush + 关闭流, 才会将数据写入到文件.. } }
16.5 Properties
基本介绍:
- 专门用于读号配置文件的集合类配置文件的格式:键=值
- 键值对不需要有空格,值不需要用引号一起来
- 默认类型是String
常用方法:
- load:加载配置文件的键值对到Properties对象
- list:将数据显示到指定设备
- getProperty (key):根据键获取值
- setProperty(key value):设置键值对到Properties对象
- store:将Properties中的键值对存储到配置文件,在idea 中,保存信息到配置文件,如果含有中文,会存储为unicode码
读取:
public class Properties02 { public static void main(String[] args) throws IOException { //使用Properties 类来读取mysql.properties 文件 //1. 创建Properties 对象 Properties properties = new Properties(); //2. 加载指定配置文件 properties.load(new FileReader("src\\mysql.properties")); //3. 把k-v显示控制台 properties.list(System.out); //4. 根据key 获取对应的值 String user = properties.getProperty("user"); String pwd = properties.getProperty("pwd"); System.out.println("用户名=" + user); System.out.println("密码是=" + pwd); } }
配置:
public class Properties03 { public static void main(String[] args) throws IOException { //使用Properties 类来创建 配置文件, 修改配置文件内容 Properties properties = new Properties(); //创建 //1.如果该文件没有key 就是创建 //2.如果该文件有key ,就是修改 /* Properties 父类是 Hashtable , 底层就是Hashtable 核心方法 public synchronized V put(K key, V value) { // Make sure the value is not null if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; int hash = key.hashCode(); int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; for(; entry != null ; entry = entry.next) { if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value;//如果key 存在,就替换 return old; } } addEntry(hash, key, value, index);//如果是新k, 就addEntry return null; } */ properties.setProperty("charset", "utf8"); properties.setProperty("user", "汤姆");//注意保存时,是中文的 unicode码值 properties.setProperty("pwd", "888888"); //将k-v 存储文件中即可 properties.store(new FileOutputStream("src\\mysql2.properties"), null); System.out.println("保存配置文件成功~"); } }
17 网络编程
17.1 网络基础
ip地址:
- 用于唯一标识网络中的每台计算机/主机
- 查看ip地址:ipconfig
- ip地址的表示形式:点分十进制 xx.xx.xx.xx
- 每一个十进制数的范围:0~255
- ip地址的组成=网络地址+主机地址,比如:192.168.16.69
- ilPv6是互联网工程任务组设计的用于替代1PV4的下一代IP协议
网络异常,图片无法展示|
网络异常,图片无法展示|
ipv4地址分类:
域名:
- 将ip地址映射成域名
端口号:
- 用于标识计算机上某个特定的网络程序
- 表示形式:以整数形式,范围0~65535
- 0~1024己经被占用,比如ssh 22,ftp 21, smtp 25 http 80
- 常见的网络程序端口号:
tomcat :8080
mysql:3306
oracle:1521
sqlserver:1433
网络通信协议:
- TCP/IP (Transmission Control Protocol/Internet Protocol的简写,中文译名为传输控制协议/因特网互联协议,又叫网络通讯协议,这个协议是internet最基本的协议,internet国际互联网络的基础就是由网络层的IP协议和传输层的TCP协议组成的
网络异常,图片无法展示|
网络异常,图片无法展示|
TCP:
- 使用TCP协议前,须先建立TCP连接,形成传输数据通道
- 传输前,采用”三次握手"方式,是可靠的
- TCP协议进行通信的两个应用进程:客户端、服务端
- 在连接中可进行大数据量的传输
- 传输完毕,需释放已建立的连接,效率低
UDP:
- 格数据源、目的封装成数据包,不需要建立连接
- 每个数据报的大小限制在64K内
- 因无需连接,故是不可靠的
- 发送数据结束时无需释放资源(因为不是面向连接的),速度快
17.2 InetAddress
常用方法:
- 获取本机InetAddress对象 getLocalHost
- 根据指定主机名/域名获取ip地址对象 getByName
- 获取InetAddress对象的主机名 getHostName
- 获取InetAddress对象的地址 getHostAddress
public class API_ { public static void main(String[] args) throws UnknownHostException { //1. 获取本机的InetAddress 对象 InetAddress localHost = InetAddress.getLocalHost(); System.out.println(localHost);//DESKTOP-S4MP84S/192.168.12.1 //2. 根据指定主机名 获取 InetAddress对象 InetAddress host1 = InetAddress.getByName("DESKTOP-S4MP84S"); System.out.println("host1=" + host1);//DESKTOP-S4MP84S/192.168.12.1 //3. 根据域名返回 InetAddress对象, 比如 www.baidu.com 对应 InetAddress host2 = InetAddress.getByName("www.baidu.com"); System.out.println("host2=" + host2);//www.baidu.com / 110.242.68.4 //4. 通过 InetAddress 对象,获取对应的地址 String hostAddress = host2.getHostAddress();//IP 110.242.68.4 System.out.println("host2 对应的ip = " + hostAddress);//110.242.68.4 //5. 通过 InetAddress 对象,获取对应的主机名/或者的域名 String hostName = host2.getHostName(); System.out.println("host2对应的主机名/域名=" + hostName); // www.baidu.com } }
17.3 Socket
基本介绍:
- 通信的两端都要有Socket,是两台机器间通信的端点
- Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输
- 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端
TCP网络通信编程:
- 基于客户端——服务端的网络通信
- 底层使用TCP/IP协议
- 基于Socket的TCP编程
网络异常,图片无法展示|
17.4 TCP编程⭐️
服务端:
public class SocketTCP03Server { public static void main(String[] args) throws IOException { //思路 //1. 在本机 的9999端口监听, 等待连接 // 细节: 要求在本机没有其它服务在监听9999 // 细节:这个 ServerSocket 可以通过 accept() 返回多个Socket[多个客户端连接服务器的并发] ServerSocket serverSocket = new ServerSocket(9999); System.out.println("服务端,在9999端口监听,等待连接.."); //2. 当没有客户端连接9999端口时,程序会 阻塞, 等待连接 // 如果有客户端连接,则会返回Socket对象,程序继续 Socket socket = serverSocket.accept(); System.out.println("服务端 socket =" + socket.getClass()); //3. 通过socket.getInputStream() 读取客户端写入到数据通道的数据, 显示 InputStream inputStream = socket.getInputStream(); //4. IO读取, 使用字符流, 老师使用 InputStreamReader 将 inputStream 转成字符流 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); System.out.println(s);//输出 //5. 获取socket相关联的输出流 OutputStream outputStream = socket.getOutputStream(); // 使用字符输出流的方式回复信息 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("hello client 字符流"); bufferedWriter.newLine();// 插入一个换行符,表示回复内容的结束 bufferedWriter.flush();//注意需要手动的flush //6.关闭流和socket bufferedWriter.close(); bufferedReader.close(); socket.close(); serverSocket.close();//关闭 } } • 客户端: public class SocketTCP03Client { public static void main(String[] args) throws IOException { //思路 //1. 连接服务端 (ip , 端口) //解读: 连接本机的 9999端口, 如果连接成功,返回Socket对象 Socket socket = new Socket(InetAddress.getLocalHost(), 9999); System.out.println("客户端 socket返回=" + socket.getClass()); //2. 连接上后,生成Socket, 通过socket.getOutputStream() // 得到 和 socket对象关联的输出流对象 OutputStream outputStream = socket.getOutputStream(); //3. 通过输出流,写入数据到 数据通道, 使用字符流 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream)); bufferedWriter.write("hello, server 字符流"); bufferedWriter.newLine();//插入一个换行符,表示写入的内容结束, 注意,要求对方使用readLine()!!!! bufferedWriter.flush();// 如果使用的字符流,需要手动刷新,否则数据不会写入数据通道 //4. 获取和socket关联的输入流. 读取数据(字符),并显示 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String s = bufferedReader.readLine(); System.out.println(s); //5. 关闭流对象和socket, 必须关闭 bufferedReader.close();//关闭外层流 bufferedWriter.close(); socket.close(); System.out.println("客户端退出....."); } }
图片拷贝
public class TCPFileUploadServer { public static void main(String[] args) throws Exception { //1. 服务端在本机监听8888端口 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务端在8888端口监听...."); //2. 等待连接 Socket socket = serverSocket.accept(); //3. 读取客户端发送的数据 // 通过Socket得到输入流 BufferedInputStream bis = new BufferedInputStream(socket.getInputStream()); byte[] bytes = StreamUtils.streamToByteArray(bis); //4. 将得到 bytes 数组,写入到指定的路径,就得到一个文件了 String destFilePath = "src\\abc.mp4"; BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(destFilePath)); bos.write(bytes); bos.close(); // 向客户端回复 "收到图片" // 通过socket 获取到输出流(字符) BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())); writer.write("收到图片"); writer.flush();//把内容刷新到数据通道 socket.shutdownOutput();//设置写入结束标记 //关闭其他资源 writer.close(); bis.close(); socket.close(); serverSocket.close(); } }
音乐拷贝:
public class Homework03Server { public static void main(String[] args) throws Exception { //1 监听 9999端口 ServerSocket serverSocket = new ServerSocket(9999); //2.等待客户端连接 System.out.println("服务端,在9999端口监听,等待下载文件"); Socket socket = serverSocket.accept(); //3.读取 客户端发送要下载的文件名 // 这里老师使用了while读取文件名,时考虑将来客户端发送的数据较大的情况 InputStream inputStream = socket.getInputStream(); byte[] b = new byte[1024]; int len = 0; String downLoadFileName = ""; while ((len = inputStream.read(b)) != -1) { downLoadFileName += new String(b, 0 , len); } System.out.println("客户端希望下载文件名=" + downLoadFileName); //老师在服务器上有两个文件, 无名.mp3 高山流水.mp3 //如果客户下载的是 高山流水 我们就返回该文件,否则一律返回 无名.mp3 String resFileName = ""; if("高山流水".equals(downLoadFileName)) { resFileName = "src\\高山流水.mp3"; } else { resFileName = "src\\无名.mp3"; } //4. 创建一个输入流,读取文件 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(resFileName)); //5. 使用工具类StreamUtils ,读取文件到一个字节数组 byte[] bytes = StreamUtils.streamToByteArray(bis); //6. 得到Socket关联的输出流 BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream()); //7. 写入到数据通道,返回给客户端 bos.write(bytes); socket.shutdownOutput();//很关键. //8 关闭相关的资源 bis.close(); inputStream.close(); socket.close(); serverSocket.close(); System.out.println("服务端退出..."); } }
netstat指令:
- netstat-an 可以查看当前主机网络情况,包括端口监听情况和网络连接情况
- netstat -an l more 可以分页显示
- 要求在dos控制台下执行
17.5 UDP编程
基本介绍:
- 类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序
- UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达
- DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号
- UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接
基本流程:
- 核心的两个类/对象 Datagramsocket与DatagramPacket
- 建立发送端,接收端
- 建立数据包
- 调用DatagramSocket的发送、接收方法
- 关网DatagramSocket
注意事项和细节:
- 没有明确的服务端和客户端,演变成数据的发送端和接收端
- 接收数据和发送数据是通过 DatagramSocket 对象完成
- 将数据封装到DatagramPacket 对象/ 装包
- 当接收到 DatagramPacket 对象,需要进行拆包,取出数据
- DatagramSocket 可以指定在哪个端口接收数据
例:
public class UDPReceiverA { public static void main(String[] args) throws IOException { //1. 创建一个 DatagramSocket 对象,准备在9999接收数据 DatagramSocket socket = new DatagramSocket(9999); //2. 构建一个 DatagramPacket 对象,准备接收数据 // 在前面讲解UDP 协议时,老师说过一个数据包最大 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); //===回复信息给B端 //将需要发送的数据,封装到 DatagramPacket对象 data = "好的, 明天见".getBytes(); //说明: 封装的 DatagramPacket对象 data 内容字节数组 , data.length , 主机(IP) , 端口 packet = new DatagramPacket(data, data.length, InetAddress.getByName("192.168.12.1"), 9998); socket.send(packet);//发送 //5. 关闭资源 socket.close(); System.out.println("A端退出..."); } }