套接字
一、概述
- 本质上是一套基于网络传输数据的流
- 实际上是一套用于网络通信的API
- IP地址
- IPv4是指由4组数组成的IP地址 , 每组数的范围在0~255 。一共有2的32次方个地址(43亿多)
- IPv6是由六组十六进制的数组成, 每组数的取值范围0~ffff 。 一共有2的96次方的地址 。
- 端口 : 用于和外界进行信息交互的媒介 。 计算机上有65535个端口 , 0~1024已经被系统内部以及一些常用应用占用。 在访问网站时 , 默认80端口 ,可以不写
- DNS服务器 : 将域名解析为对应的IP地址
- 网络模型
- 七层模型: 物理层 、 数据链路层、网络层(IP) 、 传输层(UDP\TCP) 、 会话层 、 应用层 、 表示层 (HTTP 、 HTTPS , FTP 、POP3 、 SMTP 、 TENLNET…)
- 四层: 物理层 、 数据链路层 、 网络层 、 应用层 。
二、SocketAddress类
- 本身是一个抽象类 。
- 其子类 InetSocketAdress , 实现IP地址套接字(IP地址+端口号) 。
代码:
import java.net.InetSocketAddress; //地址类 public class Test_01 { public static void main(String[] args) { //每次计算机介入互联网时都会被自动分配一个IP地址 , 每次都会变 //127.0.0.1 永远指向本机 //localhost也表示本机 InetSocketAddress isa = new InetSocketAddress("www.baidu.com",5555); //获取地址 System.out.println(isa.getAddress()); //获取计算机名 //如果不是本机IP , 则试图从网络获取 , 如果获取失败则返回IP地址 System.out.println(isa.getHostName()); //获取端口号 System.out.println(isa.getPort()); } }
三、UDP传输协议
- 基于流的 , 不需要建立连接直接发送 , 不能确保接收方能接收到 , 传输速度比较快 , 会对数据进行封包 ,每个包不超过64k。
- 适用于与一些要求速度而不要求可靠性的场景 。
代码:
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; //UDP发送端 /* * 1. 创建套接字对象 * 2. 数据封包 * 3. 发送数据 * 4. 关流 * */ public class UDPSender_01 { public static void main(String[] args) throws IOException { //UDP发送端的套接字对象 DatagramSocket ds = new DatagramSocket(); //对数据进行封包 //第一个参数:字节数组 , 数组中放的是实际数据 //第二个参数: 字节数组的大小 (数据的字节个数) //第三个参数:发送地址 DatagramPacket dp =new DatagramPacket( "你好".getBytes(), "你好".getBytes().length, new InetSocketAddress("localhost" , 9999)); //发送数据 ds.send(dp); //关流 ds.close(); } } import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; //UDP接收端 /* * 1. 创建套接字对象 * 2. 准备数据报 * 3. 接收数据 * 4. 读取数据 * */ public class UDPReciever_01 { public static void main(String[] args) throws IOException { //创建套接字对象 , 并指定端口 DatagramSocket ds = new DatagramSocket(9999); //准备一个数据报 //第一个参数: 一个字节数组 , 表示作为存储接收数据的容器 //第二个参数: 表示指定容器的大小 DatagramPacket dp = new DatagramPacket(new byte[1024], 1024); //接收数据 //在没有接收到数据时处于阻塞状态 ds.receive(dp); //关流 ds.close(); //将数据从数据包中解析出来 byte[] bs = dp.getData();//获取数据存储数据的直接数组 int len = dp.getLength();//获取实际数据长度 System.out.println(new String(bs ,0, len)); //可以获取发送地址 System.out.println(dp.getAddress()); //获取发送用的端口号 端口号会一直变 , 这是为什么? System.out.println(dp.getPort()); } }
练习:单人聊天室
import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.util.Scanner; //单人聊天 发送端 public class UDPIMSender_03 implements Runnable { @Override public void run() { // 创建套接字 try { DatagramSocket ds = new DatagramSocket(); // 创建数据报 Scanner in = new Scanner(System.in); //获取数据 while(true){ System.out.println("回车发送:"); String str = in.nextLine(); DatagramPacket dp = new DatagramPacket(str.getBytes(), str.getBytes().length, new InetSocketAddress("localhost", 10000)); // 发送数据 ds.send(dp); if(str.equals("over")){ System.out.println("发送端结束"); break; } } //关流 ds.close(); } catch (IOException e) { // TODO 自动生成的 catch 块 e.printStackTrace(); } } } import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.SocketException; //UDP聊天室 接收端 public class UDPIMReciever_03 implements Runnable{ @Override public void run() { //创建套接字对象 try { DatagramSocket ds = new DatagramSocket(10000); //创建数据包 DatagramPacket dp = new DatagramPacket(new byte[1024], 1024); //接收数据 while(true){ ds.receive(dp); //读取数据 byte[] data = dp.getData(); String str = new String(data , 0 , data.length); if("over".equals(str.trim())){ System.out.println("接收端结束"); break; } System.out.println("客户端发来:"+str); } //关流 ds.close(); } catch (IOException e) { e.printStackTrace(); } } } //单人聊天 public class Test_03 { public static void main(String[] args) { //开启聊天 new Thread(new UDPIMReciever_03()).start(); new Thread(new UDPIMSender_03()).start(); } }
四、TCP传输协议
- 基于流的
- 发送数据前 , 需要经过三次握手 。是可靠地安全的连接 。 但传输效率相对较低 。
- 理论上不限制数据大小 。
- 适用于要求可靠性而不要求速度的场景 。
- 一般分为客户端和服务器端
- 注意: receive 、 connect 、 accept 、 read 、 write 都会产生阻塞
代码:
import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer_01 { public static void main(String[] args) throws IOException { //创建套接字 , 并绑定端口号 ServerSocket ss = new ServerSocket(7000); //接受连接 //阻塞 Socket s = ss.accept(); //读取数据 InputStream is = s.getInputStream(); byte[] bs = new byte[1024]; int len = -1; while((len = is.read(bs))!=-1){ System.out.println(new String(bs)); } //向客户端响应信息 OutputStream out = s.getOutputStream(); out.write("接受成功!".getBytes()); //关流 ss.close(); s.shutdownOutput(); } } import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; public class TCPCilent_01 { public static void main(String[] args) throws IOException { //创建客户端套接字对象 Socket s = new Socket(); //客户端发起连接 , 绑定链接地址 //连接时会发生阻塞 s.connect(new InetSocketAddress("localhost", 7000)); //获取一个输出流 OutputStream out = s.getOutputStream(); //写出数据 out.write("你好".getBytes()); //通知服务器端 数据已经写出完毕 s.shutdownOutput(); //接服务器端响应 InputStream in = s.getInputStream(); byte[] bs = new byte[1024]; int len = -1; while((len = in.read(bs)) != -1){ System.out.println(new String(bs)); } //关流 s.close(); } }
练习: 上传文件 — 文件名不能变
import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; public class TCPServer_04 { public static void main(String[] args) throws IOException { //创建套接字对象 , 并指定端口 ServerSocket ss = new ServerSocket(7777); //接受连接 Socket s = ss.accept(); //获取输入流 InputStream is = s.getInputStream(); //读取文件名字节个数 int count= is.read(); //读取文件名 byte[] name = new byte[count]; is.read(name); //创建文件实例 File file = new File("C:\\Users\\user\\Desktop\\暂存\\副本 -- "+new String(name)); FileOutputStream fos = new FileOutputStream(file); //读取数据 int len = -1 ; byte bs[] = new byte[1024]; while((len = is.read(bs)) != -1){ System.out.println(new String(bs , 0, len)); fos.write(bs, 0, len); } s.shutdownInput(); fos.close(); System.out.println("接收成功"); //向客户端响应信息 OutputStream out = s.getOutputStream(); out.write("接收完成!".getBytes()); //关流 s.shutdownOutput(); } } import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; //上传文件客户端 public class TCPClient_04 { public static void main(String[] args) throws IOException { //创建文件实例 File file = new File("C:\\Users\\user\\Desktop\\暂存\\c.txt"); //获取文件名字 String name = file.getName(); //创建套接字 Socket s= new Socket(); //建立连接 s.connect(new InetSocketAddress("localhost" , 7777)); //获取输出流 OutputStream out = s.getOutputStream(); //输出文件名字节个数 out.write(name.length()); //输出文件名 out.write(name.getBytes()); //读取文件并写出 FileInputStream fis = new FileInputStream(file); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String line = null; while((line = br.readLine()) != null){ out.write(line.getBytes()); } //通知服务端写出完毕 s.shutdownOutput(); //向客户端响应信息 InputStream in = s.getInputStream(); byte[] inbs = new byte[1024]; in.read(inbs); if(new String(inbs).trim().equals("接收完成!")){ System.out.println("发送成功"); }else{ System.out.println("发送失败"); } //关流 s.shutdownInput(); } }
五、扩展
- BIO — BlockingIO 同步式阻塞式IO
- NIO — NewIO – NonBlockingIO JDK1.4出现 同步式非阻塞式IO - 框架:Mina Netty
- AIO — AsynchronousIO 异步式非阻塞式IO JDK1.8