【Socket网络编程原理&实践】

socket 基于 TCP/IP协议实现,在网络模型中属于传输层

Java 网络编程中的核心概念

  • IP 地址:用于标识网络中的计算机
  • 端口号:用于标识计算机上的应用程序或进程
  • Socket(套接字):网络通信的基本单位,通过 IP 地址和端口号标识
  • 协议:网络通信的规则,如 TCP(传输控制协议)和 UDP(用户数据报协议)

Java 网络编程的核心类

  • Socket:用于创建客户端套接字
  • ServerSocket:用于创建服务器套接字
  • DatagramSocket:用于创建支持 UDP 协议的套接字
  • URL:用于处理统一资源定位符
  • URLConnection:用于读取和写入 URL 引用的资源

原理:Socket 实现线程通信

在这里插入图片描述

Socket 常用 API

构造方法

(1) public Socket() 
(2) public Socket(String host, int port) throws UnknownHostException, IOException 
(3) public Socket(InetAddress address,int port) throws IOException 
(4) public Socket(String host, int port, InetAddress localAddr, int localPort) throws IOException 
(5) public Socket(InetAddress address, int port, InetAddress localAddr, int localPort) throws IOException 
(6) public Socket(Proxy proxy)

需要注意的是:其中第(4)种和第(5)种会指定本地客户端的IP地址和端口信息

获取 socket 信息

获得远程服务器的IP地址。
getInetAddress()

获得远程服务器的端口。
getPort()

获得客户本地的IP地址。
getLocalAddress()

获得客户本地的端口。
getLocalPort()

获得输入流。如果 Socket还没有连接,或者已经关闭,或者已经通过shutdownInput()方法关闭输入流,那么此方法会抛出IOExceptiongetInputStream()

获得输出流。如果Socket还没有连接,或者已经关闭,或者已经通过shutdownOutput()方法关闭输出流,那么此方法会抛出IOExceptiongetOutputStream()

socket状态和关闭

isClosed() :如果Socket已经连接到远程主机,并且还没有关闭,则返回true,否则返回falseisConnected() :如果Socket曾经连接到远程主机,则返回true,否则返回falseisBound() :如果 Socket已经与一个本地端口绑定,则返回true,否则返回falseclose():关闭 socket

shutdownInput():关闭输入流。
shutdownOutput():关闭输出流。

值得注意的是,先后调用Socket的 shutdownInput()和 shutdownOutput()方法,仅仅关闭了输入流和输出流,并不等价于调用Socket 的 close()方法。在通信结束后,仍然要调用Socket的close()方法,因为只有该方法才会释放Socket占用的资源,如占用的本地端口等。

实践:Socket 实现线程通信

server

  1. socket():创建 serverSocket 连接
  2. bind():绑定 6301 端口
  3. listen():serverSocket 底层实现
  4. accept():serverSocket.accept()
  5. read():创建 read 线程
  6. write():创建 write 线程
  7. close():结束 server 线程

server 代码:

Server

public class Server {
    public static void main(String[] args) {
        System.out.println("服务端启动");
        try {
            ServerSocket serverSocket = new ServerSocket(6301);
            while (true) {
                // 接受 socket 请求
                Socket socket = serverSocket.accept();
                new Thread(new ServerReader(socket)).start();
                new Thread(new ServerWriter(socket)).start();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        
    }
}

ServerReader

class ServerReader implements Runnable {
    private Socket socket;
    
    public ServerReader(Socket socket) {
        this.socket = socket;
    }
    
    @Override
    public void run() {
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            
            while (true) {
                System.out.println(objectInputStream.readObject());
            }
            
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

ServerWriter

class ServerWriter implements Runnable {
    private Socket socket;
    
    public ServerWriter(Socket socket) {
        this.socket = socket;
    }
    
    @Override
    public void run() {
        try {
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            Scanner scanner = new Scanner(System.in);
            while (true) {
                System.out.println("服务端 请输入:");
                String str = scanner.nextLine();
                
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("type", "msg");
                jsonObject.put("content", str);
                
                objectOutputStream.writeObject(jsonObject);
                objectOutputStream.flush();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

client

  1. socket():new Socket("localhost", 6301);
  2. bind():new Socket("localhost", 6301);
  3. connect():new Socket("localhost", 6301);
  4. write():创建 write 线程
  5. read():创建 read 线程
  6. close():socket.close()

client 代码:

Client

public class Client {
    
    private static Socket socket;
    
    public static boolean socketStatus = false; // 是否连接成功
    
    public static void main(String[] args) {
        while (!socketStatus) {
            try {
                socket = new Socket("localhost", 6301);
                connect();
                Thread.sleep(5000);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    private static void connect() {
        try {
            socketStatus = true;
            ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
            ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
            new Thread(new ClientReader(socket, inputStream)).start();
            new Thread(new ClientWriter(socket, outputStream)).start();
            new Thread(new ClientHeart(socket, outputStream)).start();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    
    public static void reconnect() {
        while (true) {
            System.out.println("正在尝试重新连接...");
            connect();
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

ClientReader

class ClientReader implements Runnable {
    
    private Socket socket;
    
    private ObjectInputStream objectInputStream;
    
    public ClientReader(Socket socket, ObjectInputStream objectInputStream) {
        this.socket = socket;
        this.objectInputStream = objectInputStream;
    }
    
    @Override
    public void run() {
        while (true) {
            try {
                System.out.println(objectInputStream.readObject());
            } catch (IOException | ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

ClientWriter

class ClientWriter implements Runnable {
    
    private Socket socket;
    
    private ObjectOutputStream objectOutputStream;
    
    public ClientWriter(Socket socket, ObjectOutputStream objectOutputStream) {
        this.socket = socket;
        this.objectOutputStream = objectOutputStream;
    }
    
    @Override
    public void run() {
        Scanner scanner = new Scanner(System.in);
        while (true) {
            try {
                System.out.println(socket.getLocalPort() + "客户端 请输入:");
                String str = scanner.nextLine();
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("type", "msg");
                jsonObject.put("content", str);
                objectOutputStream.writeObject(jsonObject);
                objectOutputStream.flush(); // flush 把数据从缓存 buffer刷到流中
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

值得注意的是:ClinetWriter 和 ClientHeart 的区别在于发送消息的不同,包括 typecontent

class ClientHeart implements Runnable {
    
    Socket socket;
    
    ObjectOutputStream objectOutputStream;
    
    public ClientHeart(Socket socket, ObjectOutputStream objectOutputStream) {
        this.socket = socket;
        this.objectOutputStream = objectOutputStream;
    }
    
    @Override
    public void run() {
        try {
            while (true) {
                Thread.sleep(5000);
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("type", "heart");
                jsonObject.put("content", "===心跳💓===");
                
                objectOutputStream.writeObject(jsonObject);
                objectOutputStream.flush();
            }
        } catch (Exception e) {
            // throw new RuntimeException(e);
            e.printStackTrace();
            try {
                // 连接异常断开后的操作
                socket.close();
                Client.socketStatus = false;
                Client.reconnect();
            } catch (Exception ex) {
                e.printStackTrace();
            }
            
        }
    }
}

测试结果

实现了线程通信、server重启后 client重新连接

在这里插入图片描述

序列化

这里使用ObjectOutputStream实现序列化,以及 fastjson 的 JSONObject 实现对消息的封装

via

  1. https://www.bilibili.com/video/BV1vf4y1p7uN
  2. https://www.mianshiya.com/question/1800349212845875202
  3. https://blog.csdn.net/penny611476/article/details/119905062
  4. https://juejin.cn/post/7026156159874646024
  • 13
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值