美文网首页
Socket编程(Java)

Socket编程(Java)

作者: 麦崎 | 来源:发表于2020-09-28 11:03 被阅读0次

    概念

    Socket

    在开发过程中,会用到操作系统提供的类库,这种类库一般被称为 API(Application Programming Interface,应用编程接口); Socket 是TCP/IP 提供的用于网络开发的API;Socket原本是由 BSD UNIX 开发的,但是后被移植到了 Windows 以及嵌入式操作系统中。

    TCP(Transmission Control Protocol )

    TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。


    image.png 2.gif

    三次握手

    TCP建立连接的过程包括三次握手


    image.png
    1. 客户端发送 SYN(SEQ=x)报文给服务器端,进入 SYN_SEND 状态。
    2. 服务器端收到 SYN 报文,回应一个 SYN (SEQ=y)ACK(ACK=x+1)报文,进入 SYN_RECV 状态。
    3. 客户端收到服务器端的 SYN 报文,回应一个 ACK(ACK=y+1)报文,进入 Established 状态。

    四次挥手

    TCP断开连接的过程包括四次挥手


    image.png
    1. 某个端首先调用 close,称该端执行“主动关闭”。该端的 TCP 于是发送一个 FIN 分节,表示数据发送完毕。
    2. 接收到这个 FIN 的对端执行 “被动关闭”,这个 FIN 由 TCP 确认。
    3. 一段时间后,接收到这个文件结束符的应用进程将调用 close 关闭它的套接字,这导致它的 TCP 也发送一个 FIN。
    4. 接收这个最终FIN的原发送端 TCP(即执行主动关闭的那一端)确认这个 FIN。

    无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。

    UDP(User Datagram Protocol)

    UDP是一种无连接的、不可靠的、面向数据报文的运输层协议;由于UDP在传输数据报前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。


    image.png 1.gif

    关键类

    ServerSocket

    供服务端使用,以获取一个端口,并监听客户端请求

    序号 方法 描述
    1 public ServerSocket() throws IOException 创建非绑定服务器套接字
    2 public ServerSocket(int port) throws IOException 创建绑定到特定端口的服务器套接字
    3 public ServerSocket(int port, int backlog) throws IOException 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号
    4 public ServerSocket(int port, int backlog, InetAddress address) throws IOException 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器
    5 public int getLocalPort() 返回此套接字在其上侦听的端口
    6 public Socket accept() throws IOException 侦听并接受到此套接字的连接
    7 public void setSoTimeout(int timeout) 通过指定超时值启用/禁用 SO_TIMEOUT,以毫秒为单位
    8 public void bind(SocketAddress host, int backlog) 将 ServerSocket 绑定到特定地址(IP 地址和端口号)

    Socket

    序号 方法 描述
    1 public Socket(String host, int port) throws UnknownHostException, IOException. 创建一个流套接字并将其连接到指定主机上的指定端口号
    2 public Socket(InetAddress host, int port) throws IOException 创建一个流套接字并将其连接到指定 IP 地址的指定端口号
    3 public Socket(String host, int port, InetAddress localAddress, int localPort) throws IOException 创建一个套接字并将其连接到指定远程主机上的指定远程端口
    4 public Socket(InetAddress host, int port, InetAddress localAddress, int localPort) throws IOException 创建一个套接字并将其连接到指定远程地址上的指定远程端口
    5 public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字
    6 public void connect(SocketAddress host, int timeout) throws IOException 将此套接字连接到服务器,并指定一个超时值
    7 public InetAddress getInetAddress() 返回套接字连接的地址
    8 public int getPort() 返回此套接字连接到的远程端口
    9 public int getLocalPort() 返回此套接字绑定到的本地端口
    10 public SocketAddress getRemoteSocketAddress() 返回此套接字连接的端点的地址,如果未连接则返回 null
    11 public InputStream getInputStream() throws IOException 返回此套接字的输入流
    12 public OutputStream getOutputStream() throws IOException 返回此套接字的输出流
    13 public void close() throws IOException 关闭此套接字

    DatagramSocket

    序号 方法 描述
    1 1 1

    DatagramPacket

    序号 方法 描述
    1 1 1

    示例

    TCP

    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.net.SocketTimeoutException;
    
    public class GreetingTcp {
    
        private static final String LOCAL_HOST = "localhost";
        private static final int PORT_SERVER = 2048;
    
    
        public static void main(String[] args) {
            try {
                // 开启服务端
                Server server = new Server();
                server.start();
                // 开启客户端1
                Client client1 = new Client(1);
                client1.start();
                // 开启客户端2
                Client client2 = new Client(2);
                client2.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
    
        private static class Client extends Thread {
    
            private int num;
    
            Client(int num) {
                this.num = num;
            }
    
            @Override
            public void run() {
                String serverName = LOCAL_HOST;
                int port = PORT_SERVER;
                try {
                    // 连接(客户端端口由系统随机分配)
                    Socket client = new Socket(serverName, port);
                    System.out.println("客户端-" + num + "-log--连接成功,"
                            + "LocalSocketAddress:" + client.getLocalSocketAddress()
                            + ",RemoteSocketAddress:" + client.getRemoteSocketAddress()
                            + ",LocalAddress:" + client.getLocalAddress()
                            + ",InetAddress:" + client.getInetAddress());
    
                    // 发送
                    OutputStream outToServer = client.getOutputStream();
                    DataOutputStream out = new DataOutputStream(outToServer);
                    out.writeUTF("=====Hello from " + client.getLocalSocketAddress() + "====");
    
                    // 接收
                    InputStream inFromServer = client.getInputStream();
                    DataInputStream in = new DataInputStream(inFromServer);
                    System.out.println(in.readUTF());
    
                    // 关闭
                    client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static class Server extends Thread {
    
            ServerSocket serverSocket;
    
            Server() throws IOException {
                serverSocket = new ServerSocket(PORT_SERVER);
                serverSocket.setSoTimeout(10000);
                System.out.println("服务端-0-log--创建欢迎socket,"
                        + "LocalSocketAddress:" + serverSocket.getLocalSocketAddress()
                        + ",InetAddress:" + serverSocket.getInetAddress());
            }
    
            @Override
            public void run() {
                while (!interrupted()) {
                    try {
                        // 连接
                        Socket server = serverSocket.accept();
                        System.out.println("服务端-0-log--创建连接socket,"
                                + "LocalSocketAddress:" + server.getLocalSocketAddress()
                                + ",RemoteSocketAddress:" + server.getRemoteSocketAddress()
                                + ",LocalAddress:" + server.getLocalAddress()
                                + ",InetAddress:" + server.getInetAddress());
    
                        // 接收
                        DataInputStream in = new DataInputStream(server.getInputStream());
                        System.out.println(in.readUTF());
    
                        // 回复
                        DataOutputStream out = new DataOutputStream(server.getOutputStream());
                        out.writeUTF("=====Hello too from " + server.getLocalSocketAddress() + "====");
    
                        // 关闭
                        server.close();
                    } catch (SocketTimeoutException s) {
                        System.out.println("服务端-0-log--ServerSocket timed out!");
                        break;
                    } catch (IOException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            }
        }
    
    
    }
    

    运行结果:

    服务端-0-log--创建欢迎socket,LocalSocketAddress:0.0.0.0/0.0.0.0:2048,InetAddress:0.0.0.0/0.0.0.0
    客户端-2-log--连接成功,LocalSocketAddress:/127.0.0.1:59159,RemoteSocketAddress:localhost/127.0.0.1:2048,LocalAddress:/127.0.0.1,InetAddress:localhost/127.0.0.1
    客户端-1-log--连接成功,LocalSocketAddress:/127.0.0.1:59160,RemoteSocketAddress:localhost/127.0.0.1:2048,LocalAddress:/127.0.0.1,InetAddress:localhost/127.0.0.1
    服务端-0-log--创建连接socket,LocalSocketAddress:/127.0.0.1:2048,RemoteSocketAddress:/127.0.0.1:59159,LocalAddress:/127.0.0.1,InetAddress:/127.0.0.1
    =====Hello from /127.0.0.1:59159====
    =====Hello too from /127.0.0.1:2048====
    服务端-0-log--创建连接socket,LocalSocketAddress:/127.0.0.1:2048,RemoteSocketAddress:/127.0.0.1:59160,LocalAddress:/127.0.0.1,InetAddress:/127.0.0.1
    =====Hello from /127.0.0.1:59160====
    =====Hello too from /127.0.0.1:2048====
    

    UDP

    import java.io.*;
    import java.net.*;
    
    public class GreetingUDP {
    
        private static final String LOCAL_HOST = "localhost";
        private static final int PORT_SERVER = 2049;
    
    
        public static void main(String[] args) {
            try {
                // 开启服务端
                Server server = new Server();
                server.start();
    
                // 开启客户端1
                Client client1 = new Client(1);
                client1.start();
    
                // 开启客户端2
                Client client2 = new Client(2);
                client2.start();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
        }
    
    
        private static class Client extends Thread {
    
            private int num;
    
            Client(int num) {
                this.num = num;
            }
    
            @Override
            public void run() {
                String serverName = LOCAL_HOST;
                int port = PORT_SERVER;
                try {
                    // 连接
                    DatagramSocket client = new DatagramSocket();
                    client.connect(InetAddress.getByName(serverName), port);
                    System.out.println("客户端-" + num + "-log--连接成功,"
                            + "LocalSocketAddress:" + client.getLocalSocketAddress()
                            + ",RemoteSocketAddress:" + client.getRemoteSocketAddress()
                            + ",LocalAddress:" + client.getLocalAddress()
                            + ",InetAddress:" + client.getInetAddress());
    
                    // 发送
                    byte[] data = ("=====Hello from " + client.getLocalSocketAddress() + "====").getBytes();
                    DatagramPacket packet = new DatagramPacket(data, data.length);
                    client.send(packet);
    
                    // 接收
                    byte[] buffer = new byte[1024];
                    packet = new DatagramPacket(buffer, buffer.length);
                    client.receive(packet);
                    String resp = new String(packet.getData(), packet.getOffset(), packet.getLength());
                    System.out.println(resp);
    
    
                    // 关闭
                    client.disconnect();
                    client.close();
    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private static class Server extends Thread {
    
            private DatagramSocket datagramSocket;
    
            Server() throws SocketException {
                datagramSocket = new DatagramSocket(PORT_SERVER);
                datagramSocket.setSoTimeout(10000);
                System.out.println("服务端-0-log--创建socket :" + datagramSocket.getLocalSocketAddress());
            }
    
            @Override
            public void run() {
                while (!interrupted()) {
                    try {
                        // 连接
                        byte[] buffer = new byte[1024];
                        DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                        datagramSocket.receive(packet);
    
                        System.out.println("服务端-0-log--连接成功,"
                                + "LocalSocketAddress:" + datagramSocket.getLocalSocketAddress()
                                + ",RemoteSocketAddress:" + datagramSocket.getRemoteSocketAddress()
                                + ",LocalAddress:" + datagramSocket.getLocalAddress()
                                + ",InetAddress:" + datagramSocket.getInetAddress());
    
                        // 接收
                        String data = new String(packet.getData(), packet.getOffset(), packet.getLength());
                        System.out.println("服务端-0-log--读取到数据:" + data);
    
                        // 回复
                        String str_send = "=====Hello too from " + datagramSocket.getLocalSocketAddress() + "====";
                        packet = new DatagramPacket(str_send.getBytes(), str_send.length(), packet.getAddress(), packet.getPort());
                        datagramSocket.send(packet);
    
                    } catch (SocketTimeoutException s) {
                        System.out.println("服务端-0-log--DatagramSocket timed out!");
                        break;
                    } catch (IOException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            }
        }
    
    }
    

    运行结果:

    服务端-0-log--创建socket :0.0.0.0/0.0.0.0:2049
    客户端-2-log--连接成功,LocalSocketAddress:0.0.0.0/0.0.0.0:52745,RemoteSocketAddress:localhost/127.0.0.1:2049,LocalAddress:0.0.0.0/0.0.0.0,InetAddress:localhost/127.0.0.1
    客户端-1-log--连接成功,LocalSocketAddress:0.0.0.0/0.0.0.0:52744,RemoteSocketAddress:localhost/127.0.0.1:2049,LocalAddress:0.0.0.0/0.0.0.0,InetAddress:localhost/127.0.0.1
    服务端-0-log--连接成功,LocalSocketAddress:0.0.0.0/0.0.0.0:2049,RemoteSocketAddress:null,LocalAddress:0.0.0.0/0.0.0.0,InetAddress:null
    服务端-0-log--读取到数据:=====Hello from 0.0.0.0/0.0.0.0:52745====
    服务端-0-log--连接成功,LocalSocketAddress:0.0.0.0/0.0.0.0:2049,RemoteSocketAddress:null,LocalAddress:0.0.0.0/0.0.0.0,InetAddress:null
    服务端-0-log--读取到数据:=====Hello from 0.0.0.0/0.0.0.0:52744====
    =====Hello too from 0.0.0.0/0.0.0.0:2049====
    =====Hello too from 0.0.0.0/0.0.0.0:2049====
    

    参考资料

    http://tutorials.jenkov.com/java-networking/index.html
    https://blog.csdn.net/freekiteyu/article/details/72236734
    https://blog.fundebug.com/2019/03/22/differences-of-tcp-and-udp/
    https://zhuanlan.zhihu.com/p/33797520

    相关文章

      网友评论

          本文标题:Socket编程(Java)

          本文链接:https://www.haomeiwen.com/subject/ylkluktx.html