Loading... # 套接字(JAVA) 套接字——进行网络数据传输的一套API——本质上是可以在网络上使用流 ## 网络的概念 七层模型:物理层、数据链路层、网络层、传输层、会话层、应用层、表示层 前三层属于底层结构,传输层(UDP/TCP) ## UDP 基于流的。不建立连接,本身不可靠,用之前需要对数据进行封包,每个包不超过64KB 因为只管发出去,不管对方收没收到,所以发数据流很快,适合对速度依赖性比较强,对可靠性依赖性比较低的场景 场景:视频/电话(因为有时候会卡,卡的那一下并不希望数据能找回来,而是对实时性要求高) ### 发送端 - DatagramSocket 1. 创建套接字对象 ```java DatagramSocket ds = new DatagramSocket(); ``` 2. 准备数据包,将数据放入数据包中,并绑定发送地址 ```java DatagramPacket dp = new DatagramPacket("你好~~~".getBytes(), "你好~~~".getBytes().length, new InetSocketAddress("localhost", 8090)); ``` 3. 发送数据包 ```java ds.send(dp); ``` 4. 关流 ```java ds.close(); ``` ### 接收端 - DatagramSocket 1. 创建套接字对象,并且绑定要接收的端口号 ```java DatagramSocket ds = new DatagramSocket(8090); ``` 2. 准备数据包 ```java DatagramPacket dp = new DatagramPacket(new byte[1024], 1024); ``` 3. 接收数据 ```java ds.receive(dp); ``` 4. 关流 ```java ds.close(); ``` 5. 解析数据 ```java // 将数据从数据包中解析出来 // 获取数据包的底层数组 byte[] bs = dp.getData(); // 获取数据包的实际大小 int len = dp.getLength(); System.out.println(new String(bs, 0, len)); ``` - 小练习 -- 群聊天 ```java import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; import java.util.Scanner; public class UDPChatDemo { public static void main(String[] args) { new Thread(new Sender()).start(); new Thread(new Receiver()).start(); } } class Sender implements Runnable { private DatagramSocket ds; private DatagramPacket dp; private Scanner s; { // 创建一个套接字对象 try { ds = new DatagramSocket(); s = new Scanner(System.in); } catch (Exception e) { e.printStackTrace(); } } @Override public void run() { while(true) { // 读取数据 byte[] bs = s.nextLine().getBytes(); // 准备数据包 // 255.255.255.255是广播地址,大喇叭 dp = new DatagramPacket(bs, bs.length, new InetSocketAddress("255.255.255.255", 8090)); try { // 发送数据 ds.send(dp); } catch (IOException e) { e.printStackTrace(); } } } } class Receiver implements Runnable { private DatagramSocket ds; private DatagramPacket dp = new DatagramPacket(new byte[1024], 1024); { try { ds = new DatagramSocket(8090); } catch (SocketException e) { e.printStackTrace(); } } @Override public void run() { while(true) { try { // 接收数据 ds.receive(dp); // 解析数据 System.out.println(dp.getAddress()); System.out.println(new String(dp.getData(),0,dp.getLength())); } catch(IOException e) { e.printStackTrace(); } } } } ``` ## TCP 基于流的。建立连接,要经历三次握手,来确保可靠性,但是传输速率相对较慢。理论上不限制传输的数据大小。适用于对可靠性依赖性较高。对速率依赖性小的场景。 例如:文件传输  Client用来发送,Server用来接收 网络数据产生阻塞的原因: - `receive` 等待数据包到来 - `connect` 阻塞时间短,会试图和目标主机进行连接 - `accept` 服务端等待TCP客户端的连接 - `write` 一直向外写,但是对方没有读,就会一直积攒在缓冲区,等缓冲区满了就写不进去了 - `read` 客户端没写数据,数据没过来 BIO - Blocking IO 同步式阻塞式IO - 上面学的都是属于这个,因为都会阻塞 NIO - New IO - NonBlocking IO 同步式非阻塞式IO - jdk1.4 AIO - Asynchronous IO - 异步式非阻塞式IO - jdk1.8 - 一般不用,因为国内绝大部分工程都是jdk1.8之前的版本 ### 客户端 - Socket 1. 创建客户端套接字对象 ```java Socket s = new Socket(); ``` 2. 发起连接,绑定连接地址 ```java s.connect(new InetSocketAddress("127.0.0.1", 1234)); ``` 3. 获取自带的输出流,写出数据,禁用输出流 ```java OutputStream out = s.getOutputStream(); out.write("你好,服务器端~~".getBytes()); s.shutdownOutput(); ``` 4. 如果服务器端有打回的数据,则需要获取输入流读取数据,禁用输入流 ```java InputStream in = s.getInputStream(); byte[] bs = new byte[1024]; int len = -1; while ((len = in.read(bs)) != -1) { System.out.println(new String(bs, 0, len)); } s.shutdownInput(); ``` 5. 关流 ```java s.close(); ``` ### 服务器端 - ServerSocket 1. 创建服务器端的套接字对象,并且绑定监听的端口号 ```java ServerSocket ss = new ServerSocket(1234); ``` 2. 接受连接,获取到一个Socket对象 ```java Socket s = ss.accept(); ``` 3. 获取输入流,读取消息,禁用输入流 ```java // 获取Socket自带输入流 InputStream in = s.getInputStream(); // 读取客户端发来的数据 byte[] bs = new byte[1024]; int len = -1; while ((len = in.read(bs)) != -1) { System.out.println(new String(bs, 0, len)); } s.shutdownInput(); ``` 4. 如果需要向客户端打回消息,则需要获取输出流写出数据,禁用输出流 ```java // 给客户端返回消息 s.getOutputStream().write("客户端:消息读取成功".getBytes()); s.shutdownOutput(); ``` 5. 关流 ```java ss.close(); ``` - 小练习 - 传输文件(在客户端读取文件并将读取的内容传到服务器端,在服务器端接收数据然后将接受的数据写在对应的文件上) > FileClient ```java import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; public class FileClient { public static void main(String[] args) throws Exception { // 创建一个客户端套接字 Socket s = new Socket(); // 发起连接 s.connect(new InetSocketAddress("127.0.0.1", 8090)); // 获取输出流 OutputStream out = s.getOutputStream(); // 创建File对象来指向写出文件 File file = new File("C:\\Users\\liuch\\Nutstore\\1\\我的坚果云\\Java-Learning\\java-top-speed\\src\\File\\a.zip"); // 获取文件的名字及类型 byte[] name = file.getName().getBytes(); // 写出文件名的长度 out.write(name.length - 128); // 写出文件名 out.write(name); // 读取文件内容 并将内容写出 FileInputStream fin = new FileInputStream(file); byte[] bs = new byte[1024]; int len = -1; while ((len = fin.read(bs)) != -1) { out.write(bs, 0, len); } // 关流 s.shutdownOutput(); fin.close(); s.close(); } } ``` > FileServer ```java import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.ServerSocket; import java.net.Socket; public class FileServer { public static void main(String[] args) throws Exception { // 创建服务器套接字 ServerSocket ss = new ServerSocket(8090); // 接受连接 Socket s = ss.accept(); // 获取输入流 InputStream in = s.getInputStream(); // 读取文件名长度 byte len = (byte)in.read(); // 读取文件名 byte[] name = new byte[len + 128]; in.read(name); // 读取文件内容,并将读取文件的数据写出 FileOutputStream fout = new FileOutputStream("D:\\tmp\\" + new String(name)); byte[] bs = new byte[1024]; int i = -1; while ((i = in.read(bs)) != -1) { fout.write(bs, 0, i); } // 关流 s.shutdownInput(); fout.close(); ss.close(); } } ``` 最后修改:2024 年 04 月 24 日 © 允许规范转载 赞 0 如果觉得我的文章对你有用,请随意赞赏