Socket类的使用

绑定bind与connect以及端口生成的时机

public void bind (SocketAddress bindpoint)方法的作用是将套接字绑定到本地地址。如果地址为null,则系统将随机挑选一个空闲的端口和一个有效的本地地址来绑定套接字。

在Socket通信的过程中,服务端和客户端都需要端口来进行通信。而在前面的示例中,都是使用“new ServerSocket (8888)”的代码格式来创建Socket服务端,其中8888就是服务端的端口号。使用代码“new Socket (“localhost”, 8888)” 来创建客户端的Socket并连接服务端的8888端口,客户端的端口并没有指定,而是采用自动分配端口号的算法。当然,在客户端的Socket中是可以指定使用某个具体的端口的,这个功能就由bind()方法提供。

bind()方法就是将客户端绑定到指定的端口,上,该方法要优先于connect()方法执行,也就是先绑定本地端口再执行连接方法。

public void connect (SocketAddress endpoint)方法的作用就是将此套接字连接到服务端。

public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            socket.close();
            serverSocket.close();
            System.out.println("server end!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


public static void main(String[] args) {
        try {
            Socket socket = new Socket();
            socket.bind(new InetSocketAddress("localhost", 7777));//使用7777端口去连接服务端的8888
            socket.connect(new InetSocketAddress("localhost", 8888));
            System.out.println(socket.getLocalPort());
            socket.close();
            System.out.println("client end!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
连接与超时

public void connect (SocketAddress endpoint, int timeout)方法的作用是将此套接字连接到服务端,并指定一个超时值。超时值是0意味着无限超时。在Windows操作系统中,默认的超时时间为20s。

若时间超过timeout还没有连接到服务端,则出现异常。

public static void main(String[] args) {
        long beginTime = 0;

        try {
            Socket socket = new Socket();
            socket.bind(new InetSocketAddress("192.168.0.101", 7777));
            beginTime = System.currentTimeMillis();
            socket.connect(new InetSocketAddress("1.1.1.1", 8888), 2000);
            socket.close();
            System.out.println("client end!");
        } catch (IOException e) {
            System.out.println(System.currentTimeMillis() - beginTime);
            e.printStackTrace();
        }

    }

Socket类的使用_第1张图片

获得远程端口与本地端口

public int getPort()方法的作用是返回此套接字连接到的远程端口。
public int getLocalPort()方法的作用是返回此套接字绑定到的本地端口。

获得本地InetAddress地址与本地SocketAddress地址

public InetAddress getLocalAddress()方法的作用是获取套接字绑定的本地InetAddress地址信息。

public SocketAddress getLocalSocketAddress()方法的作用是返回此套接字绑定的端点的Socket-Address地址信息。如果尚未绑定,则返回null。

获得远程InetAddress与远程SocketAddress地址

public InetAddress getInetAddess()方法的作用是返回此套接字连接到的远程的Inet-Address地址。如果套接字是未连接的,则返回null。
public SocketAddress getRemoteSocketAddress()方法的作用是返回此套接字远程端点的SocketAddress地址,如果未连接,则返回null。

套接字状态的判断

public boolean isBound()方法的作用是返回套接字的绑定状态。如果将套接字成功地绑定到一个地址,则返回true。

public boolean isConnected()方法的作用是返回套接字的连接状态。如果将套接字成功地连接到服务端,则为true。

public boolean isClosed()方法的作用是返回套接字的关闭状态。如果已经关闭了套接字,则返回true。

public synchronized void close()方法的作用是关闭此套接字。所有当前阻塞于此套接字上的I/O操作中的线程都将抛出SocketException。 套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接或重新绑定),如果想再次使用套接字,则需要创建新的套接字。

关闭此套接字也将会关闭该套接字的InputStream和OutputStream。如果此套接字有一个与之关联的通道,则关闭该通道。

开启半读与半写状态

public void shutdownInput()方法的作用是将套接字的输人流置于“流的末尾EOF”,也就是在套接字上调用shutdownInput()方法后从套接字输人流读取内容,流将返回EOF (文件结束符)。发送到套接字的输入流端的任何数据都将在确认后被静默丢弃。调用此方法的一端进入半读状态( read-half),也就是此端不能获得输入流,但对端却能获得输人流。一端能读,另一端不能读,称为半读。

public void shutdownOutput()方法的作用是禁用此套接字的输出流。对于TCP套接字,任何以前写人的数据都将被发送,并且后跟TCP的正常连接终止序列。如果在套接字上调用shutdownOutput()方法后写人套接字输出流,则该流将抛出IOException。调用此方法的一端进人半写状态(write-half),也就是此端不能获得输出流。但对端却能获得输出流。一端能写,另一端不能写,称为半写。

public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8088);
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            System.out.println("A=" + inputStream.available());
            byte[] byteArray = new byte[2];
            int readLength = inputStream.read(byteArray);
            System.out.println("server取得的数据:" + new String(byteArray, 0, readLength));
            socket.shutdownInput();//屏蔽InputStream,到达流的结尾
            System.out.println("B=" + inputStream.available());//静默丢弃其他数据
            readLength = inputStream.read(byteArray);//-1
            System.out.println("readLength=" + readLength);
            //再次调用getInputStream方法出现异常:SocketException
            socket.getInputStream();

            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


public static void main(String[] args) {
        try {
            Socket socket = new Socket("localhost", 8088);
            OutputStream outputStream = socket.getOutputStream();
            outputStream.write("abcdefg".getBytes());
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();

        }
    }

服务端输出:
Socket类的使用_第2张图片

判断半读半写状态

public boolean isInputShutdown()方法的作用是返回是否关闭套接字连接的半读状态(read-half)。如果已关闭套接字的输人,则返回true。

public boolean isOutputShutdown()方法的作用是返回是否关闭套接字连接的半写状态(write-half)。如果已关闭套接字的输出,则返回true。

Socket选项TcpNoDelay

public void setTcpNoDelay(boolean on)方法的作用是启用/禁用TCP_NODELAY (启用/禁用Nagle算法)。参数为true,表示启用TCP_NODELAY;参数为false, 表示禁用。

public boolean getTcpNoDelay()方法的作用是测试是否启用TCP_ NODELAY。返回值为是否启用TCP_ NODELAY的boolean值。

  1. 如果要求高实时性,那么有数据发送时就要马上发送,此时可以将TCP_NODELAY选项设置为true,也就是屏蔽了Nagle算法。典型的应用场景就是开发一个网络格斗游戏,程序设计者希望玩家A每点击一次按键都会立即在玩家B的计算机中得以体现,而不是等到数据包达到最大时才通过网络–次性地发送全部数据,这时就可以屏蔽Nagle算法,传人参数true就达到实时效果了。

  2. 如果不要求高实时性,要减少发送次数达到减少网络交互,就将TCP_ NODELAY设置为false,等数据包累积- -定大小后再发送。

Socket选项SendBufferSize

Socket中的SO_RCVBUF选项是设置接收缓冲区大小的,而SO_SNDBUF选项是设置发送缓冲区的大小的。

public synchronized void setSendBufferSize(int size)方法的作用是将此Socket的SO_SNDBUF选项设置为指定的值。平台的网络连接代码将SO_SNDBUF选项用作设置底层网络I/O缓存的大小的提示。由于SO_ SNDBUF是一种提示,因此想要验证缓冲区设置大小的应用程序应该调用getSendBufferSize()方法。参数size用来设置发送缓冲区的大小,此值必须大于0。

public int getSendBufferSize()方法的作用是获取此Socket的SO_SNDBUF选项的值,该值是平台在Socket上输出时使用的缓冲区大小。返回值是此Socket的SO_ SNDBUF选项的值。

public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
            char[] charAyyay = new char[1024];
            int readLength = inputStreamReader.read(charAyyay);
            long beginTime = System.currentTimeMillis();
            while (readLength != -1) {
                System.out.println(new String(charAyyay, 0, readLength));
                readLength = inputStreamReader.read(charAyyay);
            }
            long endTime = System.currentTimeMillis();
            System.out.println(endTime - beginTime);
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


public static void main(String[] args) {
        try {
            Socket socket = new Socket();
            System.out.println("A client socket.getSendBufferSize()=" + socket.getSendBufferSize());
            socket.setSendBufferSize(1);
            System.out.println("B client socket.getSendBufferSize()=" + socket.getSendBufferSize());
            socket.connect(new InetSocketAddress("localhost", 8888));
            OutputStream outputStream = socket.getOutputStream();
            for (int i = 0; i < 500000; i++) {
                outputStream.write("123456789123456789123456789".getBytes());
                System.out.println(i + 1);
            }
            outputStream.write("end!".getBytes());
            outputStream.close();
            socket.close();
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

耗费2997毫秒。

将socket.setSendBufferSize(1024 * 1024);耗时2756毫秒。

Socket选项Linger

Socket中的SO_LINGER选项用来控制Socket关闭close()方法时的行为。在默认情况下,执行Socket的close()方法后,该方法会立即返回,但底层的Socket实际上并不会立即关闭,它会延迟一段时间。 在延迟的时间里做什么呢?是将“发送缓冲区”中的剩余数据在延迟的时间内继续发送给对方,然后才会真正地关闭Socket连接。

public void setSoLinger(boolean on, int linger) 方法的作用是启用/禁用具有指定逗留时间(以秒为单位)的SO_ LINGER。最大超时值是特定于平台的。该设置仅影响套接字关闭。参数on的含义为是否逗留,参数linger的含义为逗留时间,单位为秒。

public int getSoLinger()方法的作用是返回SO_LINGER的设置。返回-1意味着禁用该选项。该设置仅影响套接字关闭。返回值代表SO_LINGER的设置。

Socket类的使用_第3张图片

从源码中总结如下:

  1. on传人false, SO_LINGER功能被屏蔽,因为对代码语句
    getImpl ().setOpt ion (SocketOptions. SO_ LINGER,new Boolean(on) ) ;中的 new Boolean(传人了 false值。对参数 on传人false 值是close() 方法的默认行为,也就是close()方法立即返回,但底层Socket并不关闭,直到发送完缓冲区中的剩余数据,才会真正地关闭Socket的连接。

  2. on传人true, linger等于0,当调用Socket的close() 方法时,将立即中断连接,也就是彻底丢弃在缓冲区中未发送完的数据,并且发送一个RST标记给对方。此知识点是根据TCP中的SO_ LINGER特性总结而来的。

  3. on传人true, linger大于65535时,linger值就被赋值为65535。

  4. on传人true, linger 不大于65535时,linger 值就是传人的值。

  5. 如果执行代码“socket.setSoLinger(true, 5)”,那么执行Socket的close()方法时的行为随着数据量的多少而不同,总结如下。

    • 数据量小:如果将“发送缓冲区”中的数据发送到对方的时间需要耗时3s,则close()方法阻塞3s,数据会被完整发送,3s 后close()方法立即返回,因为3<5。
    • 数据量大:如果将“发送缓冲区”中的数据发送到对方的时间需要耗时8s,则close()方法阻塞5s,5s之后发送RST标记给对方,连接断开,因为8>5。
Socket选项Timeout

setSoTimeout(int timeout)方法的作用是启用/禁用带有指定超时值的SO_ TIMEOUT,以毫秒为单位。将此选项设为非零的超时值时,在与此Socket关联的InputStream.上调用read)方法将只阻塞此时间长度。如果超过超时值,就将引发java.net.SocketTimeoutException,尽管Socket仍旧有效。启用timeOut特性必须在进入阻塞操作前被启用才能生效。超时值必须是大于0的数。超时值为0被解释为无穷大超时值。

public int getSoTimeout()方法的作用是返回SO_TIMEOUT的设置。返回0意味着禁用了选项(即无穷大的超时值)。

Socket选项OOBInline

Socket的选项SO_OOBINLINE 的作用是在套接字上接收的所有TCP紧急数据都将通过套接字输人流接收。禁用该选项时(默认),将悄悄丢弃紧急数据。O0B ( Out Of Bound,带外数据)可以理解成是需要紧急发送的数据。

setOOBInline(true)方法的作用是启用/禁用OOBINLINB选项(TCP紧急数据的接收者),默认情况下,此选项是禁用的,即在套接字上接收的TCP紧急数据被静默丢弃。如果用户希望接收到紧急数据,则必须启用此选项。启用时,可以将紧急数据内嵌在普通数据中接收。注意,仅为处理传人紧急数据提供有限支持。特别要指出的是,不提供传人紧急数据的任何通知并且不存在区分普通数据和紧急数据的功能(除非更高级别的协议提供)。参数on传人true表示启用OOBINLINE,传入false 表示禁用。

public void setOOBInline(boolean on)方法在接收端进行设置来决定是否接收与忽略紧急数据。在发送端,使用public void sendUrgentData (int data)方法发送紧急数据。

Socket类的public void sendUrgentData (int data)方法向对方发送1个单字节的数据,但是这个单字节的数据并不存储在输出缓冲区中,而是立即将数据发送出去,而在对方程序中并不知道发送过来的数据是由OutputStream还是由sendUrgentData(int data)发送过来的。

Socket选项KeepAlive

Socket选项SO_KEEPALIVE 的作用是在创建了服务端与客户端时,使客户端连接上服务端。当设置SO_KEEPALIVE为true时,若对方在某个时间(时间取决于操作系统内核的设置)内没有发送任何数据过来,那么端点都会发送一个ACK探测包到对方,探测双方的TCP/IP连接是否有效(对方可能断电,断网)。如果不设置此选项,那么当客户端宕机时,服务端永远也不知道客户端宕机了,仍然保存这个失效的连接。如果设置了比选项,就会将此连接关闭。

public boolean getK eepAlive()方法的作用是判断是否启用SO_ KEEPALIVE选项。
public void setKeepAlive(boolean on)方法的作用是设置是否启用SO_ KEEPALIVE 选项。参数on代表是否开启保持活动状态的套接字。

Socket选项TrafficClass

IP规定了以下4种服务类型,用来定性地描述服务的质量:

  1. IPTOS_LOWCOST ( 0x02): 发送成本低。
  2. IPTOS_RELIABILITY ( 0x04):高可靠性,保证把数据可靠地送到目的地。
  3. IPTOS_THROUGHPUT ( 0x08 ):最高吞吐量,一次可以接收或者发送大批量的数据。
  4. IPTOS_ LOWDELAY ( 0x10):最小延迟,传输数据的速度快,把数据快速送达目的地。

这4种服务类型还可以使用“或” 运算进行相应的组合。

public void setTrafficClass(int tc)方法的作用是为从此Socket上发送的包在IP头中设置流量类别(traffic class)。
public int getTrafficClass()方法的作用是为从此Socket上发送的包获取IP头中的流量类别或服务类型。
当向IP头中设置了流量类型后,路由器或交换机就会根据这个流量类型来进行不同的处理,同时必须要硬件设备进行参与处理。

你可能感兴趣的:(Socket)