• 网络协议:一文搞懂Socket套接字


    本篇内容包括:Socket 套接字的简介、Socket 套接字的分类、Java 中的 Socket 即 java.net.ServerSocketjava.net.Socket 的使用,以及Java 使用套接字 Scoket 编程的Demo。

    一、Socket 简介

    TCP(传输控制协议)是一种面向连接的、可靠的、基于字节流的通信协议,数据在传输前要建立连接,传输完毕后还要断开连接。TCP 协议提供的是点对点的通信,每条 TCP 连接由两端的套接字唯一确定。可以理解为 TCP 连接两端的套接字来连起来就形成了管道,管道的两端或者说管道的端口就是 Socket 套接字。

    Socket 的原意是“插座”,在计算机通信领域,Socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据。

    在这里插入图片描述

    Socket 的典型应用就是 Web 服务器和浏览器:浏览器获取用户输入的 URL,向服务器发起请求,服务器分析接收到的 URL,将对应的网页内容返回给浏览器,浏览器再经过解析和渲染,就将文字、图片、视频等元素呈现给用户。


    二、Socket 分类

    TCP/IP 协议族提供三种常见的 Socket 类型:流式 Socket(SOCK_STREAM)流式套接字、数据报 Socket(SOCK_DGRAM)数据报套接字、原始 Socket(SOCK_RAW)原始套接字。

    1、流式套接字(SOCK_STREAM)

    用于提供面向连接、可靠的数据传输服务。该服务将保证数据能够实现无差错、无重复发送,并按顺序接收。流套接字之所以能够实现可靠 的数据服务,原因在于其使用了传输控制协议 TCP。 这类套接字中,传输数据之前必须在两个应用进程之间建立一条通信连接, 这就确保了参与通信的两个应甩进程都是活动并具响应的e当连接建立之卮应用进程只要通过套接字向 TCP 层发送数据流,而另一个应用进程便可以接收到相应的数据流,它们不需要知道传输层是如何对数据流进行处理。特别责要注意的是通信连接必须显式建文。该套接字类型适食传输大量的数据,但不支持广播和多播方式。

    2、数据报套接字(SOCK_DGRAM)

    提供了一种无连接的服务,通信双方不需要建立任何显式连接,数据可以发送到指定的套接字,并且可以从指定的套接字接收数据。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。数据报套接字使用UDP进行数据的传输。由于数据包套接字不能保证数据传输的可靠性,对于有可能出现的数据丢失情况,需要在程序中做相应的处理。与数据报套接字相比,使用流式套接字是一个更为可靠的方法,但对于某些应用,建立一个显式连接所导致的系统开销是令人难以接收的,并且数据报套接字支持广播和多播方式。

    3、原始套接字(SOCK_RAW)

    与标准套接字(标准套接字指的是前面介绍的流套接字和数据报套接字)的区别在于:原始套接字可以读写内核没有处理的 IP 数据包,而流套接字只能读取 TCP 的数据,数据报套接字只能读取 UDP 的数据。使用原始套接字的主要目的是为了避开 TCP/IP 处理机制,被传送的数据包可以被直接传送给需要它的应用程序。因此,其主要是在编写自定义底层协议的应用程序时使用,例如各种不同的 TCP/IP 实用程序

    三、Java 中的 Socket

    Java 中对 Socket 的使用是基于两个类 java.net.ServerSocketjava.net.Socket

    img

    1、java.net.Socket 构造方法
    //不含参构造方法
    Socket();
    // 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
    Socket(InetAddress address, int port)
    // 创建一个流套接字并将其连接到指定主机上的指定端口号
    Socket(String host, int port)
    // 创建一个套接字并将其连接到指定远程地址上的指定远程端口
    Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
    // 创建一个套接字并将其连接到指定远程主机上的指定远程端口
    Socket(String host, int port, InetAddress localAddr, int localPort)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    2、java.net.Socket 常用方法
    // 将此套接字连接到服务器
    connect(SocketAddress endpoint)
    // 将此套接字连接到服务器,并指定一个超时值
    connect(SocketAddress endpoint, int timeout)
    
    // 返回服务端的ip地址
    getInetAddress();
    // 获取服务端的端口号
    getPort();
    // 获取本地客户端的ip地址
    getLocalAddress();
    // 获取本地客户端的端口号
    getLocalPort();
    // 返回此套接字的输入流
    getInputStream()
    // 返回此套接字的输出流
    getOutputStream()
    
    // 根据连接是否关闭返回一个boolean值,关闭则返回true,否则返回false
    isClose();
    // 如果连接是否曾被连接过则返回true,否则返回false
    isConnect();
    // 如果Socket已经与本地的一个端口绑定,返回true,否则返回false
    isBound();
    
    // 关闭输入流
    shutdownInput();
    // 关闭输出流
    shutdownOutput();
    
    // 关闭Socket
    close();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    3、java.net.ServerSocket 构造方法
    // 创建绑定到特定端口的服务器套接字
    ServerSocket(int port)
    
    • 1
    • 2
    4、java.net.ServerSocket 常用方法
    // 侦听并接受到此套接字的连接。
    accept()
    // 返回此服务器套接字的本地地址
    getInetAddress()
    
    • 1
    • 2
    • 3
    • 4

    四、Java Socket Demo

    Demo:编程实现基于 TCP 的 Socket 服务器端和客户端的通信

    1、Demo 服务端

    服务端的 Socket Demo 流程思路:

    1. 创建 ServerSocket 对象,绑定监听端口;
    2. 通过 accept() 方法监听客户端请求;
    3. 链接建立后,通过输入流读取客户端发送的请求信息;
    4. 通过输出流向客户端发送响应信息;
    5. 关闭相关资源。
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    /*
     * 基于TCP协议的Socket通信,实现用户登录
     * 服务器端
     */
    public class Server {
        public static void main(String[] args) {
            try {
                // 1、创建一个服务器Socket,即ServerSocket,指定绑定的端口,并监听此端口
                ServerSocket serverSocket = new ServerSocket(8888);
                // 2、调用()方法开始监听,等待客户端的连接
                System.out.println("服务器即将启动,等待客户端的连接");
                Socket socket = serverSocket.accept();// 就会处于阻塞的状态,等待监听
                // 3、获取输入流,病读取客户端信息
                InputStream is = socket.getInputStream();// 字节输入流
                // 将字节流转换为字符流
                InputStreamReader isr = new InputStreamReader(is);
                // 为输入流添加缓冲
                BufferedReader br = new BufferedReader(isr);
                String info = null;
                while((info = br.readLine())!=null){
                    System.out.println("我是服务器,读取客户端发过来的信息:"+info);
                    }
                socket.shutdownInput();//关闭输入流
                
                // 关闭资源
                br.close();
                isr.close();
                is.close();
                socket.close();
                serverSocket.close();
                
             } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
             }
         }
     }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    2、Demo 客户端

    客户端的 Socket Demo 流程思路:

    1. 创建 Socket对象,指明需要连接的服务器的地址和端口号;

    2. 连接建立后,通过输出流向服务器端发送请求信息;

    3. 通过输入流获取服务器响应的信息;

    4. 关闭相关资源。

    import java.io.IOException;
    import java.io.OutputStream;
    import java.io.PrintWriter;
    import java.net.Socket;
    import java.net.UnknownHostException;
    
    /*
     * 客户端
     */
    public class Client {
        public static void main(String[] args) {
            // 1、创建客户端Socket,指定服务器地址和端口
            try {
                Socket  socket = new Socket("localhost", 8888);
                // 2、获取输出流,向服务器端发送信息
                OutputStream os = socket.getOutputStream();// 获取字节输出流
                // 将输出流包装为打印流
                PrintWriter pw = new PrintWriter(os);
                pw.write("用户名:user 密码:pawd");
                pw.flush();
                socket.shutdownInput();//关闭输出流
                
                // 3、关闭资源
                pw.close();
                os.close();
                socket.close();
                
            } catch (UnknownHostException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
  • 相关阅读:
    华为OCR识别技术 [C#]
    [题] 快速排序 #分治
    Linux内核 6.6版本将遏制NVIDIA驱动的不正当行为
    使用beef劫持用户浏览器
    探索 VisionOS 辅助功能:VoiceOver 手势
    Mybatis-plus分页查询不生效之问题排查
    UNIAPP框架中使用BLE蓝牙连接
    数据仓库性能测试方法论与工具集
    【数据结构】最小生成树的概念
    单链表在线OJ题(详解+图解)
  • 原文地址:https://blog.csdn.net/weixin_45187434/article/details/127745492