网络的一些基础知识
IP 地址分类
IP 地址根据网络 ID 的不同分为 5 种类型, A 类地址, B 类地址, C 类地址, D 类地址和 E 类地址. A 类保留给政府机构, B 类分配给中等规模的公司, C 类分配给任何需要的人, D 类用于组播, E 类用于实验, 各类可容纳的地址数目不同.(IP 地址由网络号和主机号组成).
A 类地址
一个 A 类 IP 地址由 1 字节的网络地址和 3 字节主机地址组成, 网络地址的最高位必须是 "0", 地址范围从 1.0.0.0 到 126.0.0.0. 可用的 A 类网络有 126 个, 每个网络能容纳 1 亿多个主机. A 类地址一般分配给大型网络.
A 类地址第 1 字节为网络地址, 其它 3 个字节为主机地址, 默认的子网掩码 255.0.0.0.
A 类地址范围: 0.0.0.0-126.255.255.255(0.0.0.0 和 126.255.255.255 这种 IP 一般不使用).
A 类地址中的私有地址和保留地址.
10.X.X.X 是私有地址(所谓的私有地址就是在互联网上不使用, 而被用在局域网络中的地址).
127.X.X.X 是保留地址, 用做循环测试用的.
B 类地址
一个 B 类 IP 地址由 2 个字节的网络地址和 2 个字节的主机地址组成, 网络地址的最高位必须是 "10", 地址范围从 128.0.0.0 到 191.255.255.255. 可用的 B 类网络有 16382 个, 每个网络能容纳 6 万多个主机, 一般分配给中型网络 .
B 类地址第 1 字节和第 2 字节为网络地址, 其它 2 个字节为主机地址, 默认子网掩码是 255.255.0.0.
B 类地址范围: 128.0.0.0-191.255.255.255(128.0.0.0 和 191.255.255.255 这种地址一般不用).
B 类地址的私有地址和保留地址.
172.16.0.0-172.31.255.255 是私有地址.
169.254.X.X 是保留地址. 如果你的 IP 地址是自动获取 IP 地址, 而你在网络上又没有找到可用的 DHCP 服务器. 就会得到其中一个 IP.
C 类地址
一个 C 类 IP 地址由 3 字节的网络地址和 1 字节的主机地址组成, 网络地址的最高位必须是 "110". 范围从 192.0.0.0 到 223.255.255.255.C 类网络可达 209 万余个, 每个网络能容纳 254 个主机.
C 类地址前 3 个字节为网络地址, 第 4 个个字节为主机地址. 另外第 1 个字节的前三位固定为 110, 默认子网掩码是 255.255.255.0.
C 类地址范围: 192.0.0.0-223.255.255.255(192.0.0.0 和 223.255.255.255 一般不用).
C 类地址中的私有地址.
192.168.X.X 是私有地址.
D 类地址
D 类 IP 地址第一个字节以 "1110" 开始, 它是一个专门保留的地址. 它并不指向特定的网络, 目前这一类地址被用在多点广播 (Multicast) 中. 多点广播地址用来一次寻址一组计算机, 它标识共享同一协议的一组计算机.
D 类地址不分网络地址和主机地址, 它的第 1 个字节的前四位固定为 1110.
D 类地址范围: 224.0.0.1-239.255.255.254.
E 类地址
以 "11110" 开始, 为将来使用保留. 全零 ("0.0.0.0") 地址对应于当前主机. 全 "1" 的 IP 地址 ("255.255.255.255") 是当前子网的广播地址.
E 类地址也不分网络地址和主机地址, 它的第 1 个字节的前五位固定为 11110.
E 类地址范围: 240.0.0.1-255.255.255.254
端口分类
公认端口(well know port): 从 0 到 1023, 他们紧密绑定一些特定服务. 比如 80 端口绑定 http 服务, 443 端口绑定 https 服务, 22 端口绑定 SSH 服务等;
主粗端口(Registed port):1024 到 49151, 他们松散的绑定一些服务, 我们自己写的应用程序应该使用这个范围内的端口;
动态或私有端口: 49152 到 65535, 这些端口应用程序会动态使用.
Java 的基本网络支持
Java 中对网络支持的类大都在 java.NET 这个包下面, 常用的类有 URL,URLConnection,URLDecoder,URLENcoder 和 InetAddress 等.
InetAddress 的使用
InetAddress 代表 IP 地址, 它有两个子类 Inet4Address 和 Inet6Address.
- public class InetAddressDemo {
- public static void main(String[] args) throws Exception {
- // 获取本机的 IP 地址
- InetAddress localHost = InetAddress.getLocalHost();
- System.out.println("host address:"+localHost.getHostAddress());
- System.out.println("host name:"+localHost.getHostName());
- // 根据百度的域名, 随机获取百度的一个 IP 地址
- InetAddress baidu = InetAddress.getByName("www.baidu.com");
- System.out.println("host address:"+baidu.getHostAddress());
- System.out.println("host name:"+baidu.getHostName());
- // 根据 IP 地址, 货期 InetAddress
- InetAddress loopAddress = InetAddress.getByAddress(new byte[]{127,0,0,1});
- System.out.println("host address:"+loopAddress.getHostAddress());
- System.out.println("host name:"+loopAddress.getHostName());
- // 根据域名, 获取域名对应的所有 IP 地址
- InetAddress[] baidus = InetAddress.getAllByName("www.baidu.com");
- for (InetAddress inetAddress : baidus) {
- System.out.println("host address:"+inetAddress.getHostAddress());
- System.out.println("host name:"+inetAddress.getHostName());
- }
- }
- }
URLEncoder 和 URLDecoder 的使用
当 URL 地址中包含非西欧字符的字符串时, 系统会将这些非西欧字符自动编码. 在我们编程过程中就会涉及到将这些普通的字符串和特殊字符串之间的转换, 这时就需要使用 URLEncoder 和 URLDecoder.
- String encodedUrl = "https://www.baidu.com/s?wd=ip地址分类&rsv_spt=1&rsv_iqid=0xe6273c5300071242&issp=1&f=8&rsv_bp=1&rsv_idx=2&ie=utf-8&rqlang=cn&tn=baiduhome_pg&rsv_enter=1&oq=IP%E5%9C%B0%E5%9D%80%E5%88%86%E7%B1%BB&rsv_t=8798Pr4JiDoBuHX8kSW6i384TlOk5p8vEQ4c4tWrc0suF31CjvBh6stq0gyq0PtETa9x&inputT=9569&rsv_sug3=24&rsv_sug1=14&rsv_sug7=100&rsv_pq=b3c08c4200035639&bs=IP地址分类";
- String decodeURL = URLDecoder.decode(encodedUrl, Charset.forName("UTF8").name());
- System.out.println("decodeURL:"+decodeURL);
URLEncoder 和 URLDecoder 这两个类只提供了 encode 和 decode 方法供我们使用.
URL,URLConnection 和 URLPermission 的使用
这边先讲下 URL 和 URI 的区别:
URI: 是 uniform resource identifier, 统一资源标识符, 用来唯一的标识一个资源. web 上可用的每种资源如 html 文档, 图像, 视频片段, 程序等都是一个来 URI 来定位的 URI 一般由三部组成:1访问资源的命名机制2存放资源的主机名3资源自身的名称, 由路径表示, 着重强调于资源.
URL 是 uniform resource locator, 统一资源定位器, 它是一种具体的 URI, 即 URL 可以用来标识一个资源, 而且还指明了如何 locate 这个资源. URL 一般由三部组成:1协议(或称为服务方式)2存有该资源的主机 IP 地址(有时也包括端口号)3主机资源的具体地址.
简单的说, URI 是一种互联网上资源的唯一标识符(我们可以将它看成一个资源的 id),URL 是一种特殊的 URI,URL 不仅能标识一个互联网资源, 而且通过 URL 能够获取到这个资源. Java 中的两个类 URI 和 URL 就分别对应这两个概念. URL 类可以打开一个流来获取具体的资源.
- String[] urls = {"https://img0.pconline.com.cn/pconline/1707/21/9625301_20150814_6d20f056ee9803d9419buyemaASeB0KJ_thumb.jpg"};
- for (String url : urls) {
- URL url1 = new URL(url);
- int port = url1.getPort();
- System.out.println("port:"+port);
- String host = url1.getHost();
- System.out.println("host:"+host);
- String protocol = url1.getProtocol();
- System.out.println("protocol:"+protocol);
- String file = url1.getFile();
- System.out.println("fileName:"+file);
- // 这段是否要设置权限, 为什么 open 总是失败?
- InputStream inputStream = url1.openStream();
- FileOutputStream fos = new FileOutputStream("D:\\"+new Date()+".jpeg");
- FileCopyUtils.copy(inputStream,fos);
- inputStream.close();
- fos.close();
- }
基于 TCP 协议的网络编程
使用 ServerSocket 建立 TCP 服务端
Java 中能够接收其他通信实体请求的类是 ServerSocket. 这个对象可以监听来自客户端的 Socket 连接(每个 TCP 连接两个 Socket, 一个 IP 加一个端口组成一个 Socket). 如果没有连接, 它将一直处于等待状态.
Socket accept(): 该方法返回客户端 Socket, 没有连接将一直处于等待状态(同步), 线程也被阻塞(阻塞);
ServerSocket 存在如下的构造函数:
public ServerSocket(int port): 指定端口, backlog 默认 50;
public ServerSocket(int port, int backlog) :backlog 用于指定连接队列的长度, 这个值和操作系统也有关, 尝试了下 Windows 下最多设置 200 个, 如果我们设置的值超过 200, 就取 200.
public ServerSocket(int port, int backlog, InetAddress bindAddr): 如果机器有多个网卡还可以指定具体监听哪个网卡.
(netstat 命令详解)
使用 Socket 进行通信
下面是一个很加单的 clientSocket 和 serverSocket 的列子: 客户端每隔一秒钟给服务端发一个消息, 服务端给出响应:
- Socket clientSocket = new Socket("127.0.0.1",30000);
- //inputStream 用来接受服务端返回的消息
- InputStream inputStream = clientSocket.getInputStream();
- //outputStream 用来给服务端发消息
- OutputStream outputStream = clientSocket.getOutputStream();
- while (true){
- outputStream.write(("hi, l am clinetSocket").getBytes());
- byte[] bytes = new byte[1024];
- inputStream.read(bytes);
- System.out.println("get message from server:"+new String(bytes));
- Thread.sleep(1000);
- }
服务端程序
- public class ServerSocketDemo {
- private static ExecutorService executorService = Executors.newFixedThreadPool(10);
- public static void main(String[] args) throws Exception {
- ServerSocket serverSocket = new ServerSocket(30000, 5);
- while (true) {
- Socket socket = serverSocket.accept();
- System.out.println("get socket:" + socket);
- executorService.execute(new Printer(socket));
- }
- }
- private static class Printer implements Runnable {
- private Socket socket;
- public Printer(Socket socket) {
- this.socket = socket;
- }
- @Override
- public void run() {
- OutputStream outputStream;
- InputStream inputStream;
- try {
- // 对于服务端来说, inputStream 用来接收客户端的报文
- inputStream = socket.getInputStream();
- // 对于服务端来说, outputStream 用来给客户端响应报文
- outputStream = socket.getOutputStream();
- while (true) {
- byte[] bytes = new byte[1024];
- inputStream.read(bytes);
- System.out.println("wa.. l got you" + new String(bytes));
- outputStream.write("l am serverSocket".getBytes());
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
半关闭的 Socket
半关闭的 Socket 是指只关闭 Socket 的输入输出流, 但是不关闭整个 Socket 连接.
使用 NIO 实现非阻塞的 Socket 通信
Java NIO 下几个用于 Socket 通信的类的简单说明:
Selector: 它是 SelectableChannel 对象的多路复用器器, 所有希望采用非阻塞方式进行通信的 Channel 都应该注册到 Selector 对象上. 可以调用 selector = Selector.open()来构造 Selector 对象.
SelectionKey: 一个 Selector 实例有三种 SelectionKey 集合. 第一种是通过 selector 的 keys()方法返回的所有 SelectionKey 集合, 代表所有注册在这个 Selector 实例上的 Channel; 第二种是通过 selectedKeys()方法返回的 SelectionKey 集合, 代表需要进行 IO 处理的 channel; 第三种是已经取消注册的 Channel, 一般不用.
- public class NioServerSocketDemo {
- private Selector selector;
- public static final int port = 30000;
- private Charset charset = Charset.forName("UTF-8");
- public void init() throws Exception{
- selector = Selector.open();
- ServerSocketChannel server = ServerSocketChannel.open();
- InetSocketAddress address = new InetSocketAddress("127.0.0.1",port);
- server.bind(address,5);
- server.configureBlocking(false);
- //serverSocketChannel 也要注册到 selector 上面
- server.register(selector, SelectionKey.OP_ACCEPT);
- //selector.select()会阻塞当前线程
- //selector.select(long timeout), 设置超时时间
- //selector.selectNow()不会阻塞线程
- while (selector.select()>0){
- for(SelectionKey key : selector.selectedKeys()){
- // 已经处理过了, 将其删除
- selector.selectedKeys().remove(key);
- if(key.isConnectable()){
- SocketChannel channel = (SocketChannel)key.channel();
- System.out.println(channel+"has connected...");
- }
- if(key.isAcceptable()){
- SocketChannel acceptChannel = server.accept();
- acceptChannel.configureBlocking(false);
- acceptChannel.register(selector,SelectionKey.OP_READ);
- key.interestOps(SelectionKey.OP_ACCEPT);
- }
- if(key.isReadable()){
- SocketChannel channel = (SocketChannel)key.channel();
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- String content = "";
- try{
- while (channel.read(buffer)>0){
- buffer.flip();
- content+=charset.decode(buffer);
- }
- System.out.println("get content:"+content);
- channel.write(buffer);
- key.interestOps(SelectionKey.OP_READ);
- }catch (IOException ex){
- key.cancel();
- if(key.channel()!=null){
- key.channel().close();
- }
- }
- }
- }
- }
- }
- public static void main(String[] args) throws Exception {
- new NioServerSocketDemo().init();
- }
- }
使用 AIO 实现非阻塞的 Socket 通信
- public class AIOServerSocket {
- private static Charset charset = Charset.forName("UTF-8");
- public static void main(String[] args) throws Exception {
- AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
- serverSocketChannel.bind(new InetSocketAddress(30000),1000);
- while (true){
- Future<AsynchronousSocketChannel> future = serverSocketChannel.accept();
- AsynchronousSocketChannel socketChannel = future.get();
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- socketChannel.read(buffer);
- System.out.println("get from client:"+charset.decode(buffer));
- }
- }
- }
来源: https://www.cnblogs.com/54chensongxia/p/12678143.html