基于TCP/IP协议的Java Socket网络编程

一、什么是TCP/IP协议?

        详见:计算机网络(重点)运输层_江南煮酒的博客-CSDN博客

二、什么是Socket?

        所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口 (解释出处:王雷,TCP/IP网络编程基础教程,北京理工大学出版社,2017.02,第4页)

        PC使用TCP/IP协议建立一个连接需要“三次握手”,而Socket使用的网络协议是TCP/IP协议,因此使用Socket建立一个连接自然也需要“握手三次

 socket的一些api:

三、Java Socket网络编程

        Java对基于TCP协议的网络通信提供了良好的封装,Java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

        Java中能接收其他通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。

构造方法:

  • ServerSocket():创建一个未绑定的服务器套接字。
  • ServerSocket(int port, int backlog, InetAddress bindAddr):创建具有指定端口侦听积压要绑定到的本地 IP 地址的服务器套接字。 
        bindAddr参数可以在多宿主主机上用于 ServerSocket,该服务器仅接受到其地址之一的连接请求。 如果bindAddr为 null,它将默认接受任何/所有本地地址上的连接。 
    
        端口必须介于 0 和 65535 之间,包括 0 和 65535。 端口号0表示端口号是自动分配的,通​​常来自临时端口范围。 然后可以通过调用getLocalPort来检索该端口号。
    
        如果存在安全管理器,则此方法调用其checkListen方法,并将port参数作为其参数,以确保允许操作。 这可能会导致 SecurityException。 
    
        backlog参数是套接字上请求的最大挂起连接数。它的具体功能是基于具体的实现来定义的,特别是,它既可以被定义成强加最大长度也可以选择完全忽略该参数,提供的值应大于0,如果它小于或等于0,则将使用特定于实现的默认值。
    
    参数:
        port – 端口号,或0使用自动分配的端口号。
        backlog – 请求的传入连接队列的最大长度。
        bindAddr – 服务器将绑定到的本地 InetAddress
    
    抛出异常:
        SecurityException – 如果安全管理器存在且其checkListen方法不允许该操作。
        IOException – 如果打开套接字时发生 I/O 错误。
        IllegalArgumentException – 如果端口参数超出指定的有效端口值范围,该范围在 0 和 65535 之间,包括 0 和 65535。
  • ServerSocket(int port):等价于ServerSocket(port, 50, null)。
  • ServerSocket(int port, int backlog):等价于ServerSocket(port, backlog, null)。

基础方法:

  • void bind(SocketAddress endpoint):等价于bind(endpoint, 50)。
  • void bind(SocketAddress endpoint, int backlog):将ServerSocket绑定到特定地址(IP 地址和端口号)。
        如果地址为null ,则系统将选择一个临时端口和一个有效的本地地址来绑定套接字。
        backlog参数是套接字上请求的最大挂起连接数。 它的确切语义是特定于实现的。
    参数:
        endpoint – 要绑定到的 IP 地址和端口号。
        backlog – 请求的传入连接队列的最大长度。
  •  void close() :关闭此套接字。 当前在accept()阻塞的任何线程都将抛出SocketException 。如果此套接字具有关联的通道,则该通道也将关闭。
  • Socket accept():监听要与此套接字建立的连接并接受它。该方法阻塞,直到建立连接。返回一个可以操作的Socket对象。
    
  • ServerSocketChannel getChannel():返回与此套接字关联的唯一ServerSocketChannel对象(如果有)。当且仅当通道本身是通过ServerSocketChannel.open方法创建时,服务器套接字才会有通道。
    
  • boolean isBound():返回 ServerSocket 的绑定状态。如果 ServerSocket 成功绑定到地址,则为 true。
  • boolean isClosed():返回 ServerSocket 的关闭状态。
  • synchronized void setSoTimeout(int timeout) :设置指定的超时时间(以毫秒为单位)。指定ServerSocket的accept()方法调用时阻塞的这段时间。如果超时到期,则会引发java.net.SocketTimeoutException,但ServerSocket仍然有效。 该选项必须在进入阻止操作之前启用才能生效。超时必须> 0。零超时被解释为永不超时。
    
    参数:
        timeout – 指定的超时时间,以毫秒为单位
    
  • synchronized int getSoTimeout():返回该套接字设置的超时连接时间。
  • void setReuseAddress(boolean on):设置是否开启2MSL的时间等待计时器。
  • boolean getReuseAddress() :查看是否开启2MSL的时间等待计时器。

ServerSocket类方法:

  • synchronized void setSocketFactory(SocketImplFactory fac):设置应用程序的服务器套接字实现工厂。工厂只能指定一次。当应用程序创建新的服务器套接字时,将调用套接字实现工厂的createSocketImpl方法来创建实际的套接字对象。

其它方法:

  • InetAddress getInetAddress():如果该套接字没有绑定ip则返回null,如果该套接字绑定了ip,即使该套接字对象被close()方法关闭了,仍然可以获取到绑定到该套接字的ip对应的InetAddress对象。
  • getLocalPort():此套接字正在侦听的端口号,如果尚未绑定套接字,则为 -1。套接字关闭了也能使用。
  • SocketAddress getLocalSocketAddress():返回一个SocketAddress表示此套接字的本地端点,或SocketAddress代表的环回地址,如果安全管理器,或拒绝null如果套接字尚未绑定。套接字关闭后仍能使用。
  • synchronized void setReceiveBufferSize (int size):既用于设置内部套接字接收缓冲区的大小,也用于设置通告给远程对等方的 TCP 接收窗口的大小,如果应用程序希望允许大于 64K 字节的接收窗口(如 RFC1323 所定义),则必须在ServerSocket 绑定到本地地址之前设置建议值。这意味着:当使用无参数构造器创建ServerSocket时,需要自己调用setReceiveBufferSize()方法,最后通过调用 bind()将ServerSocket绑定到地址。
    即使不这样做也不会导致错误,但缓冲区大小可能设置为请求的值,但从该ServerSocket接受的套接字中的TCP接收窗口将不大于64K字节。
    
    参数:
        size – 设置接收缓冲区大小的大小。 该值必须大于 0。
  • synchronized int getReceiveBufferSize():获取ServerSocket接受的Sockets的建议缓冲区大小。

        当ServerSocket使用完毕后,应使用ServerSocket的close()方法来关闭该ServerSocket。在通常情况下,服务器不应该只接收一个客户端请求,而应该不断地接收来自客户端的所有请求,所以Java程序通常会通过循环不断地调用ServerSocket的accept()方法。

            ServerSocket serverSocket = new ServerSocket(1024);
            //循环来反复接收连接
            while (true){
                serverSocket.accept();
                //业务代码
            }

        客户端通常可以使用Socket的构造器来连接到指定服务器。

构造方法:

  • Socket():创建一个未连接的套接字。
  • Socket(Proxy proxy):创建一个未连接的套接字,指定代理的类型(如果有),无论任何其他设置都应使用该类型。如果存在安全管理器,则调用其checkConnect方法并使用代理主机地址和端口号作为其参数。 这可能会导致 SecurityException。
    
//将创建一个忽略任何其他代理配置的普通套接字。
Socket s = new Socket(Proxy.NO_PROXY); 
//将创建一个通过指定 SOCKS 代理服务器连接的套接字。
Socket s = new Socket(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("socks.mydom.com", 1080))); 
  • Socket(String host, int port)
    Socket(InetAddress address, int port)
    host - 主机名,或为null回环地址,
    address - 远程地址,
    port - 端口号。
    
        如果指定的主机为null ,则相当于将地址指定为InetAddress.getByName (null) 。 也就是说,相当于指定了loopback接口的地址。
  • :创建流套接字并将其连接到指定 IP 地址的指定端口号。
  • Socket(String host, int port, InetAddress localAddr, int localPort)
    Socket(InetAddress address, int port, InetAddress localAddr, int localPort)
        创建一个套接字并将其连接到指定远程端口上的指定远程地址。 Socket 还将 bind() 到提供的本地地址和端口。
        如果指定的本地地址为null ,则相当于将该地址指定为 AnyLocal 地址(请参阅InetAddress.isAnyLocalAddress () )。
        如果本地端口号为0则系统默认将在bind操作中选择一个空闲端口。
        如果存在安全管理器,则调用其checkConnect方法并使用主机地址和port作为其参数。 这可能会导致 SecurityException。
    
    参数:
        host - 远程地址。
        address - 远程地址。
        port - 远程端口。
        localAddr – 套接字绑定到的本地地址,或者anyLocal地址为null 。
        localPort – 套接字绑定到的本地端口,或者对于系统选择的空闲端口zero 。
    
    抛出异常:
        IOException – 如果创建套接字时发生 I/O 错误。
        SecurityException – 如果安全管理器存在且其checkConnect方法不允许连接到目标,或者其checkListen方法不允许绑定到本地端口。
        IllegalArgumentException – 如果 port 参数或 localPort 参数超出指定的有效端口值范围,该范围在 0 到 65535 之间,包括 0 和 65535。
        NullPointerException – 如果address为空。

常用方法:

  • void connect(SocketAddress endpoint, int timeout):将此套接字连接到具有指定超时值的服务器。timeout=0被则永不超时。 然后连接将阻塞,直到建立或发生错误。
    
    参数:
        endpoint – SocketAddress
        timeout – 以毫秒为单位使用的超时值。
    
    抛出异常:
        IOException - 如果在连接过程中发生错误
        SocketTimeoutException - 如果超时在连接之前到期
        java.nio.channels.IllegalBlockingModeException – 如果此套接字具有关联的通道,并且该通道处于非阻塞模式
        IllegalArgumentException – 如果端点为 null 或者是此套接字不支持的 SocketAddress 子类
    
  • void connect(SocketAddress endpoint):等价于connect(endpoint, 0)。
  • void bind(SocketAddress bindpoint):将套接字绑定到本地地址。如果地址为null ,则系统将选择一个临时端口和一个有效的本地地址来绑定套接字。
  • SocketChannel getChannel():返回与此套接字关联的唯一SocketChannel对象(如果有)。当且仅当通道本身是通过SocketChannel.open或ServerSocketChannel.accept方法创建时,套接字才会有通道。
  • InputStream getInputStream():返回此套接字的输入流。
  • OutputStream getOutputStream():返回此套接字的输出流。
    
  • synchronized void close():关闭此套接字。
        1、当前在此套接字上的 I/O 操作中阻塞的任何线程都将抛出SocketException 。
        2、一旦套接字被关闭,它就不能用于进一步的网络使用(即不能重新连接或重新连接)。 需要创建一个新的套接字。
        3、关闭此套接字也将关闭套接字的InputStream和OutputStream 。
        4、如果此套接字具有关联的通道,则该通道也将关闭。 
  • void shutdownInput():将此套接字的输入流放置在“流末尾”。 任何发送到套接字输入流端的数据都会被确认,然后被静默丢弃。如果在套接字上调用此方法后从套接字输入流中读取,则流的available方法将返回 0,其read方法将返回-1 (流结束)。
  • void shutdownOutput():禁用此套接字的输出流。 对于 TCP 套接字,将发送任何先前写入的数据,然后是 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 之后写入套接字输出流,则该流将抛出 IOException。
  • boolean isConnected():返回套接字的连接状态。如果套接字成功连接到服务器,则为 true。关闭套接字不会清除其连接状态,这意味着如果在关闭之前成功连接,则此方法将为关闭的套接字返回true。
    
  • boolean isBound():返回套接字的绑定状态。如果套接字成功绑定到地址,则为 true。
    注意:关闭套接字不会清除其绑定状态,这意味着如果在关闭之前成功绑定,则此方法将为关闭的套接字返回true (请参阅isClosed() )。
    
  • boolean isClosed():返回套接字的关闭状态。如果套接字已关闭,则为真。
  • boolean isInputShutdown():如果套接字的输入流已关闭,则为 true。
  • boolean isOutputShutdown():如果套接字的输出流已关闭,则为 true。

其它方法:

  • InetAddress getInetAddress():返回套接字连接的地址。
  • InetAddress getLocalAddress():获取套接字绑定到的本地地址。
  • int getPort():返回此套接字连接到的远程端口号。
  • int getLocalPort():返回此套接字绑定到的本地端口号。
  • SocketAddress getRemoteSocketAddress():返回此套接字连接到的远程端点的地址,如果未连接,则返回null 。
  • SocketAddress getLocalSocketAddress():返回此套接字绑定到的本地端点,或SocketAddress代表的环回地址。
  • void setTcpNoDelay(boolean on):启用/禁用TCP_NODELAY (禁用/启用 Nagle 算法)
    
    boolean getTcpNoDelay():测试是否启用了TCP_NODELAY 。
  • void setSoLinger(boolean on, int linger):开启/关闭延迟关闭超时时间
        on – 是否超时关闭。
        linger – 超时多长时间(如果on为true)。
    
    int getSoLinger():返回SO_LINGER设置。返回 -1 意味着延迟关闭选项被禁用。
  • void sendUrgentData():在套接字上发送一字节的紧急数据。 要发送的字节是数据参数的最低八位。
  • void setOOBInline(boolean on):启用/禁用SO_OOBINLINE (接收 TCP 紧急数据)默认情况下,此选项处于禁用状态并且在套接字上接收到的 TCP 紧急数据将被静默丢弃。 如果用户希望接收紧急数据,则必须启用此选项。 启用后,紧急数据将与普通数据一起接收。
        请注意,仅为处理传入的紧急数据提供有限的支持。 特别是,除非有更高级别的协议提供,否则不提供传入紧急数据的通知并且没有能力区分正常数据和紧急数据。
    
    boolean getOOBInline():测试是否启用了SO_OOBINLINE 。
  • synchronized void setSoTimeout(int timeout):使用指定的超时时间(以毫秒为单位)启用/禁用SO_TIMEOUT 。 将此选项设置为非零超时后,与此 Socket 关联的 InputStream 上的 read() 调用将仅阻塞这段时间。 如果超时到期,则会引发java.net.SocketTimeoutException ,尽管 Socket 仍然有效。 该选项必须在进入阻止操作之前启用才能生效。 超时必须> 0 。 零超时被解释为永不超时。
    
    synchronized int getSoTimeout():返回SO_TIMEOUT值。 返回0意味着该选项被禁用(即永不超时)。
  • synchronized void setSendBufferSize(int size):设置底层网络 I/O 缓冲区建议大小,只是建议内核使用但不一定生效,想要验证缓冲区设置为多大的应用程序应该调用getSendBufferSize() 。
    
    synchronized int getSendBufferSize():获取此Socket的SO_SNDBUF选项的值,即平台用于此Socket上的输出的缓冲区大小
  • synchronized void setReceiveBufferSize(int size):增加加接收缓冲区(建议,不一定生效)大小可以提高大容量连接的网络 I/O 性能,而减少它可以帮助减少传入数据的积压。size值必须大于 0。
    
        1、对于从 ServerSocket 接受的套接字,这必须在 ServerSocket 绑定到本地地址之前通过调用ServerSocket.setReceiveBufferSize(int)来完成。
        2、对于客户端套接字,必须在将套接字连接到其远程对等方之前调用setReceiveBufferSize()。 
    
    synchronized int getReceiveBufferSize():获取此Socket的SO_RCVBUF选项的值,即平台用于此Socket上的输入的缓冲区大小。
  • void setKeepAlive(boolean on):是否打开套接字保持活动。
    
    boolean getKeepAlive():测试是否SO_KEEPALIVE 。
    
    public final static int SO_KEEPALIVE:当为 TCP 套接字设置了 keepalive 选项并且 2 小时内没有在任一方向上通过套接字交换数据时(注意:实际值取决于实现),TCP 会自动向对等方发送一个 keepalive 探测。 此探测是对等方必须响应的 TCP 段。 预期三个响应之一: 
    1. 对等方以预期的 ACK 响应。 不会通知应用程序(因为一切正常)。 TCP 将在另外 2 小时不活动后发送另一个探测。 
    
    2. 对端用 RST 响应,它告诉本地 TCP 对端主机已经崩溃并重新启动。 插座已关闭。 
    
    3. 对端无响应。 插座已关闭。 此选项的目的是检测对等主机是否崩溃。 仅对 TCP 套接字有效:SocketImpl。
  • void setTrafficClass(int tc):为从此 Socket 发送的数据包设置 IP 标头中的流量类别或服务类型八位字节。 由于底层网络实现可能会忽略此值,应用程序应将其视为一个提示。
    
    tc必须在0 <= tc <= 255范围内,否则将抛出 IllegalArgumentException。
    
    int getTrafficClass():获取从此 Socket 发送的数据包的 IP 标头中的流量类别或服务类型。由于底层网络实现可能会忽略使用setTrafficClass(int)设置的流量类或服务类型,因此此方法可能返回与之前在此 Socket 上使用setTrafficClass(int)方法设置的值不同的值。
  • void setReuseAddress(boolean on):启用/禁用SO_REUSEADDR套接字选项。(SO_REUSEADDR仅用于 java 中的 MulticastSockets,默认情况下为 MulticastSockets 设置。)设置是否开启2MSL的时间等待计时器。
    
    boolean getReuseAddress():查看是否开启2MSL的时间等待计时器。

 类方法:

  • static synchronized void setSocketImplFactory(SocketImplFactory fac):为应用程序设置客户端套接字实现工厂。 

四、基于心跳包的一对一的socket通信设计


 

おすすめ

転載: blog.csdn.net/qq_40100414/article/details/120590614