网络编程
概述:
-
三要素
确定发给哪个接收端(IP地址)
- 确定发给接收端中的哪个应用程序(端口号)
-
IP地址介绍
查看本机IP地址
- 在DOS命令行输入: ipconfig
- 检查网络是否连通
- 在DOS命令行输入: ping IP地址
特殊IP地址
UDP协议: 用户数据报协议(User Datagram Protocol)
- 不需要连接
- 速度快
- 有大小限制
- 一次最多发送64K
- 易丢失数据
TCP协议: 传输控制协议 (Transmission Control Protocol)
static InetAddress getLocalhost() 静态方法,获取本机的IP地址对象
- static InetAddress getByName(String host) 静态方法,通过主机名或IP地址得到InetAddress对象
- String getHostName() 获取此IP地址的主机名
String getHostAddress() 返回IP地址字符串
DatagramPacket发送数据的构造方法:
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
DatagramPacket(byte[] buf, int length)
- buf: 用来保存接收到的数据
length: 指定接收数据的长度 :::
/*
目标:编写UDP发送端
发送数据的步骤:
1.创建发送端。 DatagramSocket s = new DatagramSocket();
2.创建数据包,并指定IP(cmd -> ipconfig)和端口。DatagramPacket
3.发送数据。send(DatagramPacket p)方法
4.关闭资源。
DatagramPacket发送数据的构造方法:
DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)
buf :要发送的字节数据
offset:从数组的哪个位置开始发送
length:发送数据的长度
address:接收端的IP
port:接收端的端口号
*/
//数据发送端
public class UDPSender {
public static void main(String[] args) throws Exception{
//创建发送端(快递员)
DatagramSocket socket = new DatagramSocket();
//创建数据包,并指定IP和端口(快递)
byte[] arr = "hello".getBytes();//数据
InetAddress ip = InetAddress.getByName("192.168.88.49");//IP
int port = 8888;//端口
DatagramPacket packet = new DatagramPacket(arr, 0, arr.length, ip, port);
//发送数据(快递员发快递)
socket.send(packet);
//关闭资源
socket.close();
System.out.println("发送端发送完毕");
}
}
/*
目标:编写UDP接收端
接收数据的步骤:
1.创建接收端。 DatagramSocket s = new DatagramSocket(int port);
2.创建空的数据包,用于装数据。DatagramPacket
3.接收数据。receive(DatagramPacket p)方法
4.解析数据。(从数组中获取有效内容)
5.关闭资源。close
DatagramPacket接收数据的构造方法:
DatagramPacket(byte[] buf, int length)
buf: 用来保存接收到的数据
length: 指定接收数据的长度
*/
//数据接收端
public class UDPReceiver {
public static void main(String[] args) throws Exception{
System.out.println("接收端返回");
//创建接收端
DatagramSocket socket = new DatagramSocket(8888);
//创建空的数据包,用于装数据
byte[] arr = new byte[1024];
DatagramPacket packet = new DatagramPacket(arr, arr.length);
//接收数据
socket.receive(packet);
//从packet中获取数据的长度
int length = packet.getLength();
//解析数据(从数组中获取有效内容)
System.out.println(new String(arr, 0, length));
socket.close();
}
}
TCP通信程序
原理
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象
- 通信之前要保证连接已经建立(TCP三次握手)
-
通信流程
TCP协议通信是需要连接的,建立连接后以流的形式传输 ::: ```java /* 目标:编写TCP客户端程序
- 创建客户端Socke对象,与指定服务端连接
Socket(String host, int port)
host :服务端IP地址
port :服务端端口号
- 得到输出流写数据 OutputStream getOutputStream()
- 得到输入流读取数据 InputStream getInputStream()
关闭资源 void close() */ public class TCPClinet { public static void main(String[] args) throws Exception{
System.out.println("客户端启动成功"); //创建TCP客户端,指定目的地的IP和端口 Socket socket = new Socket("127.0.0.1", 8887); //往服务端发送数据 OutputStream output = socket.getOutputStream(); output.write("dddd".getBytes()); //发出输出结束 socket.shutdownOutput(); //接收服务端和回复信息(得到输入流读取数据) InputStream input = socket.getInputStream(); byte[] arr = new byte[1024]; int len = input.read(arr); System.out.println(new String(arr, 0, len)); //关闭资源 input.close(); output.close(); socket.close();
} }
- 创建TCP服务端ServerSocket对象。 ServerSocket(int port)
监听客户端的连接,连接成功返回一个Socket对象。 Socket accept() 3.得到输入流读取数据。 InputStream getInputStream() 4.得到输出流写数据。 OutputStream getOutputStream() 5.关闭资源 void close() */ public class TCPServer { public static void main(String[] args) throws Exception{
System.out.println("服务端启动"); //创建服务端 ServerSocket sSok = new ServerSocket(8887); //和客户端建立连接(TCP通讯的连接) Socket socket = sSok.accept(); System.out.println(socket); //接收客户端信息(读取数据) InputStream input = socket.getInputStream(); byte[] arr = new byte[1024]; int len; while ((len = input.read(arr)) != -1) { System.out.println(new String(arr, 0, len)); } //回复客户端(写出数据) OutputStream output = socket.getOutputStream(); output.write("dddd".getBytes()); //关闭资源 output.close(); input.close(); //sSok.close();//为了持续化接收输出通常不关,
} }
System.out.println("客户端启动"); //1.创建Socket客户端 Socket socket = new Socket("127.0.0.1",8888); //2.创建输入流读取文件 FileInputStream fis = new FileInputStream("D:\\MyTest\\test.jpg"); //3.从Socket获取输出流 OutputStream out = socket.getOutputStream(); //循环读写数据 byte[] array = new byte[1024*8]; int len; while ((len=fis.read(array))!=-1){ out.write(array,0,len); } //给出输出结束的标记 socket.shutdownOutput(); //4.从Socket获取输入流,获取服务端的回应。 InputStream in = socket.getInputStream(); len = in.read(array); System.out.println("服务端回应:"+new String(array,0,len)); //5.关闭资源 in.close(); out.close(); fis.close(); socket.close();
} }
/ 编写文件上传的服务端 1.创建ServerSocket服务端 while(true){ 2.监听客户端连接 3.获取Socket输入流,用于接收客户端文件 4.创建文件输出流,循环读写数据 5.获取Socket输出流,向客户端写出上传结果 6.关闭资源 } / public class UploadServer { public static void main(String[] args) throws Exception{ //1.创建ServerSocket服务端 System.out.println(“服务端启动”); ServerSocket ss = new ServerSocket(8888);
//线程池,提高效率
ExecutorService pool = Executors.newFixedThreadPool(20);
while (true) {
//2.监听客户端连接
//等待客户端的连接,一个客户端对应一个socket
Socket socket = ss.accept();
System.out.println(socket);
/* UploadThread upload = new UploadThread(socket);
Thread t = new Thread(upload);
t.start();*/
UploadThread upload = new UploadThread(socket);
pool.submit(upload);
}
}
}
//线程 public class UploadThread implements Runnable{ private Socket socket; public UploadThread(Socket socket) { this.socket = socket; } @Override public void run() { InputStream in = null; FileOutputStream fos = null; OutputStream out = null; try { //3.获取Socket输入流,用于接收客户端文件 in = socket.getInputStream(); //4.创建文件输出流,保存用户的图片到服务器中 //UUID通用唯一识别码 String filename = UUID.randomUUID().toString()+ “.jpg”; fos = new FileOutputStream(“t_day12\upload\“+filename); byte[] array = new byte[1024*8]; int len; while ((len=in.read(array))!=-1){ fos.write(array,0,len); } //5.获取Socket输出流,向客户端写出上传结果 out = socket.getOutputStream(); out.write(“上传成功!”.getBytes()); System.out.println(“传输完毕”); }catch (Exception e){ System.out.println(“传输失败:”+e.toString()); }finally { //6.关闭资源 try { out.close(); fos.close(); in.close(); socket.close(); } catch (Exception e) { System.out.println(“传输失败:”+e.toString()); } } } }
<a name="xqsgP"></a>
### NIO
:::tips
<a name="Iq7Ug"></a>
#### NIO概述
- JDK1.4以前:InputStream/OutputStream称为BIO(Blocking IO) 阻塞式IO BIO
- JDK1.4推出了一套新的IO体系称为NIO (New IO/ Not Blocking IO) 非阻塞式IO
<a name="Bc58u"></a>
#### 阻塞和非阻塞都是处理IO数据的方式
- 阻塞:如果没有数据就一直等待
- 非阻塞:如果没有数据,不会一直等待,可以做其他事情
<a name="pzhL2"></a>
#### BIO模型(单向流)
- BIO是同步阻塞模型,一个客户端连接对应一个处理线程
- 在BIO中,accept和read方法都是阻塞操作
- 如果没有连接请求,accept方法阻塞
- 如果无数据可读取,read方法阻塞。
<a name="GaesO"></a>
#### NIO(双向通道)
- Buffer缓存
- 用于存储数据,底层基于数组实现,针对8种基本类型提供了对应的缓冲区类
- Buffer相当于之前BIO的byte[],但效率更高,功能更强大。
- Channel通道
- 用于进行数据传输,面向缓冲区进行操作,支持双向传输
- 数据可以从Channel读取到Buffer中,也可以从Buffer写到Channel中
- Selector选择器
- Selector也叫多路复用器,使用一个线程高效地管理多个Channel
- 当向一个Selector中注册Channel后,Selector内部就可以自动不断地查询这些注册的Channel是否有已就绪的 I/O 事件
- 当某个Channel上面发生了读或写事件,这个Channel就会被Selector监听到,然后通过SelectionKeys可以获取就绪Channel的集合,进行后续的I/O操作。
:::
<a name="JD5MO"></a>
### commons-io工具包
:::tips
<a name="ewFGz"></a>
#### 概述
- commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以提高IO功能开发的效率
- commons-io工具包提供了很多有关io操作的类。有两个主要的类FileUtils, IOUtils
<a name="E8muq"></a>
#### FileUtils主要方法
- String readFileToString(File file, String encoding) 读取文件中的数据, 返回字符串
- void copyFile(File srcFile, File destFile) 复制文件
- void copyDirectoryToDirectory(File srcDir, File destDir) 复制文件夹
- void deleteDirectory(File directory) 删除文件夹
:::
```java
/*
1.FileUtils操作文件或文件夹
拷贝文件:copyFile(File srcFile, File destFile)
拷贝文件夹:copyDirectoryToDirectory(File srcDir, File destDir)
文件夹里面的子文件夹也会一起拷贝
删除文件夹: deleteDirectory(File directory)
注意:整个文件夹都会直接删除,不管里面是否有内容
获取文件或文件夹大小:long sizeOf(File file)
2.FileUtils读取文件
读取文件内容为字符串:String readFileToString(File file, String encoding)
将文件内容按行读取成字符串集合:List<String> readLines(File file, String encoding)*/
public class Demo {
public static void main(String[] args) throws Exception{
FileUtils.copyFile(
new File("E:\\File\\hello.txt"), new File("Day12UDPTCP\\A\\hello.txt")
);
FileUtils.copyDirectoryToDirectory(
new File("E:\\File"), new File("Day12UDPTCP\\A")
);
FileUtils.deleteDirectory(new File("Day12UDPTCP\\A\\File"));
long l = FileUtils.sizeOf(new File("E:\\File"));
System.out.println(l);
//readFileToString(文件对象,编码)
String s = FileUtils.readFileToString(
new File("Day12UDPTCP\\A\\gbk_file.txt"), "GBK"
);
System.out.println(s);
//List<String> readLines(文件对象,编码)
List<String> list = FileUtils.readLines(
new File("Day12UDPTCP\\A\\gbk_file.txt"), "GBK"
);
list.forEach(System.out::println);
}
}