java中的网络支持 [技术实现]

1.InetAddress:用于标识网络上的硬件资源
2.URL:统一资源定位符 通过URL可以直接读取或写入网络上的数据
3.Sockets 使用TCP协议实现网络通信的Socket相关的类
4Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信

InetAddress类

InetAddress类用于标识网络上的硬件资源表示互联网协议IP地址
没有构造方法,通过调用static方法获取实例

1.getLocalHost返回本地主机。

  1. public static InetAddress getLocalHost() throws UnknownHostException

返回:本地主机的 IP 地址。
抛出:UnknownHostException - 如果找不到 host 的任何 IP 地址。
使用:

  1. //获取实例
  2. InetAddress address = InetAddress.getLoacalHost();
  3. address.getHostName();//获取主机名
  4. address.getHostAddress();//获取本机ip
  5. InetAddress address = InetAddress.get..By...();//其他获取实例的方法

2.根据计算机名/ip获取实例

InetAddress.getByName("主机名");
InetAddress.getByName("IP地址");

java.net.URL类

统一资源定位符 协议:资源名称

try{
    URL myURL = new URL("https://blog.csdn.net");
    //也可以加?的参数  和#的锚点
    URL myURL2 = new URL(myURL ,"/qq_36762677");
    myURL2.getProtocol();//获取协议名
    myURL2.getPort();//获取端口号,默认端口号返回-1
    myURL2.getHost();//获取主机
}catch(Exception e){
}

使用URL读取页面内容[ java爬虫 ]

try{
    URL url  = new URL("https://blog.csdn.net/qq_36762677");
    // 获取字节输入流
    InputStream is = url.openStream();
    // 将字节输入流转为字符输入流
    InputStreamReader isr  = new InputStreamReader(is,"utf-8");
    //为字符输入流添加缓冲,提高读取效率
    BufferedReader br = new BufferedReader(isr);
    String data = br.readLine();//读取数据
    while(data!=null){
         System.out.println(data);
         data = br.readLine();
    }
    br.close();
    isr.close();
    is.close();
}

socket编程

  • 中文名:套接字
  • 基于TCP的网络通信
  • TCP面向连接的,可靠地,有序的,字节流方式发送数据

    1. 基于TCP的Socket编程

    Socket编程 - 图1

服务端java.net.ServerSocket 客户端Socket
image.pngimage.png
服务端代码


import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @author zhaojy
 * @date 19/12/15 11:03
 */
public class SocketServer {
    /**
     * 当前连接数
     */
    public static int count = 0;

    public static void main(String[] args) throws Exception {
        // 开始监听8888 port
        try (ServerSocket serverSocket = new ServerSocket(8888)) {
            // 循环接收客户端请求
            while (true) {
                Socket socket = serverSocket.accept();
                ServerThread serverThread = new ServerThread(socket);
                count++;
                // 设置线程优先级[1-10] 默认5,防止运行速度减慢
                serverThread.setPriority();
                serverThread.start();

                InetAddress clientInetAddress = socket.getInetAddress();
                System.out.println("当前连接数:" + count + "客户端IP" + clientInetAddress.getHostAddress());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    static class ServerThread extends Thread {
        Socket socket = null;

        public ServerThread(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            //字节输入流
            try (InputStream is = socket.getInputStream()) {
                //提高性能,变字符流
                try (InputStreamReader isr = new InputStreamReader(is)) {
                    //为字符输入流添加缓冲
                    try (BufferedReader br = new BufferedReader(isr)) {
                        //读取数据
                        String data = br.readLine();
                        while (data != null) {
                            System.out.println("收到客户端发来的消息:"+data);
                            data = br.readLine();
                        }

                        try (OutputStream os = socket.getOutputStream()) {
                            try (PrintWriter pw = new PrintWriter(os)) {
                                pw.write("听得到");
                                pw.flush();
                            }
                        }

                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                SocketServer.count--;
                System.out.println("连接断开----剩余连接数" + count);
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端代码


import java.io.*;
import java.net.Socket;

/**
 * @author zhaojy
 * @date 19/12/15 11:03
 */
public class SocketClient {
    public static void main(String[] args) throws Exception {
       try( Socket socket = new Socket("127.0.0.1",8888)){
           // 获取输出流, 向服务器发送消息
           OutputStream os =  socket.getOutputStream();
           // 将输出流包装成打印流
           PrintWriter pw = new PrintWriter(os);

           pw.print("能听到吗");
           pw.flush();
           socket.shutdownOutput();

           //字节输入流
           InputStream is = socket.getInputStream();
           //提高性能,变字符流
           InputStreamReader isr = new InputStreamReader(is);
           //为字符输入流添加缓冲
           BufferedReader br = new BufferedReader(isr);
           //读取数据
           String data = br.readLine();
           while (data != null) {
               System.out.println(data);
               data = br.readLine();
           }

           br.close();
           isr.close();
           is.close();
           pw.close();
           os.close();
       }catch (Exception e){

       }
    }
}

2. 基于UDP的Socket编程

image.png
服务端代码:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author zhaojy
 * @date 19/12/15 11:03
 */
public class SocketServer {

    public static void main(String[] args) throws Exception {
        // 创建服务端
        DatagramSocket datagramSocket = new DatagramSocket(8889);
        // 创建数据报
        DatagramPacket datagramPacket = getDatagramPacket(datagramSocket);

        // 接收后获取客户端地址,端口 进行响应
        InetAddress address = datagramPacket.getAddress();
        int port = datagramPacket.getPort();
        byte[] byte1 = "收到".getBytes();
        DatagramPacket packet2 = new DatagramPacket(byte1, byte1.length, address, port);
        datagramSocket.send(packet2);

        datagramSocket.close();

    }

    private static DatagramPacket getDatagramPacket(DatagramSocket datagramSocket) throws IOException {
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);

        // 接收数据, 再没接收到会一直阻塞
        datagramSocket.receive(datagramPacket);

        // 读取数据
        String info = new String(bytes, 0, datagramPacket.getLength());
        System.out.println("服务端收到消息:" + info);
        return datagramPacket;
    }

}

客户端代码:


import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * @author zhaojy
 * @date 19/12/15 11:03
 */
public class SocketClient {
    public static void main(String[] args) throws Exception {
        // 定义服务端 地址,端口
        InetAddress address = InetAddress.getByName("localhost");
        int port = 8889;

        // 定义发送的数据报
        byte[] data = "你好?".getBytes();
        DatagramPacket datagramPacket = new DatagramPacket(data, data.length, address, port);

        DatagramSocket socket = new DatagramSocket();

        socket.send(datagramPacket);


        DatagramPacket serverPacket = getDatagramPacket(socket);
    }

    private static DatagramPacket getDatagramPacket(DatagramSocket datagramSocket) throws IOException {
        byte[] bytes = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);

        // 接收数据, 再没接收到会一直阻塞
        datagramSocket.receive(datagramPacket);

        // 读取数据
        String info = new String(bytes, 0, datagramPacket.getLength());
        System.out.println("客户端收到消息:" + info);
        return datagramPacket;
    }
}

传输文件和对象

http://www.imooc.com/article/16226

STOMP 简单文本协议

(WebSocket遵循此规范)
,类似http,基于TCP
支持STOMP的产品:RabbitMQ等
learn more

WebSocket

基于TCP的一种的网络协议,实现了浏览器与服务器全双工通信,允许服务器发送信息给客户端
解决了客户端与服务端实时通信而产生的技术,http只能客户端发起

HTTP协议是半双工协议,也就是说在同一时间点只能处理一个方向的数据传输,同时HTTP消息也是过于庞大,里面包含大量消息头数据,真正在消息处理中很多数据不是必须的,这也是对资源的浪费。


Socket编程 - 图5

使用场景

  1. 公告
  2. 实时监控
  3. 股票实时推送
  4. 聊天,弹幕

    ws建立连接过程

  5. 客户端发起握手请求(特殊的http请求)

  6. 服务端响应请求
  7. 连接建立

  8. 关闭连接

服务器底层关闭TCP连接
客户端发起TCP close

两种通讯模式

广播式
点对点

ws生命周期

打开事件
消息事件 文本,二进制
错误事件
关闭事件

SockJS

是一个浏览器JavaScript库,它提供了一个类似于网络的对象。SockJS提供了一个连贯的、跨浏览器的Javascript API,它在浏览器和web服务器之间创建了一个低延迟、全双工、跨域通信通道。

服务端注解

@``ServerEndpoint(value = "/webSocketProxy/{nickName}")
表示一个URI映射的路径,标准的restfulAPI,{nickName}为路径参数。用于注解于类上。

@OnOpen @OnMessage @OnClose @OnError
以上四个注解用于方法上,分别对应不同的事件:打开连接、收到消息、连接关闭、发生错误。当发生对应事件时,该方法将会被触发。通常开发中主要在@OnMessage方法内进行业务开发。

@PathParam("nickName")
此注解用于获取路径参数{nickName}。

当WS使用Nginx反向代理时,需要在Nginx Conf上添加配置

链接状态

NOT_YET_CONNECTED, CONNECTING, OPEN, CLOSING, CLOSED;

spring-websocket 开源代码
https://github.com/ai1045152332/SpringBoot-Cloud/tree/master/springboot-websocket