Java网络学习笔记3:设置Socket选项(补充)

Wesley13
• 阅读 755

 接上一篇博文,在小小添加几个Socket选项, 

4.SO_LINGER选项:

//设置该选项:
public void setSoLinger(boolean on,int seconds) throws SocketException  // 注意第二个参数以秒为单位,并非毫秒
//读取该选项:
public int getSoLinger() throws SocketException

    SO_LINGER选项用来控制Socket关闭时的行为,默认情况下,当执行Socket的close()方法,该方法会立即返回,但底层的Socket实际上并不立即关闭,它会延迟一段时间知道发送完所有剩余的数据,才会真正关闭Socket,才断开连接。

但对于以下情况:  

socket.setSoLinger(true,0); //该方法也会立即返回,但底层的Socket也会立即关闭,所有未发送完的剩余数据被丢弃。
                                        //还在网络上传输,但是未被接收方接收的数据,
socket.setSoLinger(true,1000); 
// 而该方法不会立即返回,而是进入阻塞状态。 只有当底层的Soket发送完所有的剩余数据或阻塞时间已经超过了1000秒,也回返回,但是剩余未发送的数据被丢弃。

//对于该选项,尝试着让服务器端先睡眠一会,再开始接受数据:   

//服务器端程序:
public class SimpleServer {
    public static void main(String[] args) throws Exception {
        ServerSocket server = new ServerSocket(7777);
        Socket socket = server.accept();
        System.out.println("服务器困了,于是决定休息会...");
        Thread.sleep( 1000*10 );   // 睡眠10秒。
        System.out.println("服务器终于睡醒了,然后开始接受数据:");
        InputStream in  = socket.getInputStream();
        ByteArrayOutputStream buffer = new ByteArrayOutputStream();
        byte[] buff = new byte[1024];
        int length = -1;
        while( ( length = in.read(buff) ) != -1){
            buffer.write(buff,0,length);
        }
        System.out.println( new String( buffer.toByteArray() )); //把字节数组转换成字符串。
    }
}


//客户端程序:
public class SimpleClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost",7777);
        //socket.setSoLinger(true, 0);    //(1)  该条件下,会强行关闭底层的Socket,导致所有未发送数据丢失,而服务器算接受方睡醒后接受数据,由于底层Socket关闭,则会抛出SocketException: Connection reset.
        //socket.setSoLinger(true, 300);  //(2) 注意Socket关闭时间,实际上会等待一段时间在关闭
        OutputStream out = socket.getOutputStream();
        StringBuffer sb = new StringBuffer();
        String str = "关于对setSoLinger选项的设置:";
        sb.append(  str );
        for( int i=1;i<=10000;i++){  // 需注意jvm堆栈的设置。
            sb.append( i );
        }  
        System.out.println( sb );
        out.write( sb.toString().getBytes() );
        System.out.println("开始关闭Socket: ");
        long start = System.currentTimeMillis();
        socket.close();  // 注意不同条件下(1)与(2)的关闭的时间
        long end = System.currentTimeMillis();
        System.out.println("关闭Socket所用时间为: " + (end - start ) +"ms" );
    }

}

5.SO_RCVBUF选项: 该选项用于 输入数据 的缓冲区的大小。

//设置该选项:
public void setReceiveBufferSize(int size) throws SocketException
//读取该选项:
public int getReceiveBufferSize() throws SocketException

 6.SO_SNDBUF选项:该选项用于 输出数据的缓冲区的大小。

//设置该选项:
public void setSendBufferSize(int size) throws SocketException;
//读取该选项:
public void getSendBufferSize() throws SocketException;

public class SocketBufferDemo {
    public static void main(String[] args) throws SocketException {
        Socket socket = new Socket();//不带参的构造方法,不会试图建立与服务器端的连接.
        
        //默认情况下的输入输出缓冲区的大小:
        int rcvbuf = socket.getReceiveBufferSize();  // 输入缓冲区大小
        int sndbuf = socket.getSendBufferSize();  //输出缓冲区大小
        System.out.println( rcvbuf  + "\t" + sndbuf );
        
        //重新设置输入输出缓冲区的大小再输出结果:
        socket.setReceiveBufferSize( 1024*32 );
        socket.setSendBufferSize( 1024*32 );
        System.out.println( socket.getReceiveBufferSize()+"\t"+socket.getSendBufferSize() );
    }
}

7.SO_KEEPALIVE选项:

//设置该选项:
public void setKeepAlive(boolean on) throws SocketException
//读取该选项:
public int getKeepAlive() throws SocketException

    当该选项为true时,表示底层的TCP实现会监视该连接是否有效。当连接处于空闲状态(即连接的两端没有互相传送数据)超过2H,本地的TCP实现会发送一个数据包给远程的Socket。如果远程Socket没有发回响应,TCP实现就会持续尝试11分钟,直到接收到响应为止。如果在12分钟内未收到响应,TCP实现就会自动关闭本地Socket,断开连接。(在不同的网络平台上,TCP实现尝试与远程Socket对话的时限会有所差别)。

    该选项默认值为 false,即表示TCP不会监视连接是否有效,不活动的客户端可能会永久存在下去,而不会注意到服务器已经崩溃。

对于KeepAlive这种系统底层的机制(用于系统维护每一个tcp连接的),可在认识到另一种新的概念,即心跳线程:

链接网址:①; http://code.taobao.org/p/tfs/wiki/dataserver/background/  <心跳线程简单介绍>

                ②: http://blog.csdn.net/xuyuefei1988/article/details/8279812  《关于心跳包机制》

                 ③:http://blog.sina.com.cn/s/blog_616e189f0100s3px.html <这篇也不错,Socket缓冲区探讨>

8.OOBINLINE选项:

//设置该选项:
public void setOOBInline(boolean on ) throws SocketException
//读取该选项:
public void getOOBInline() throws SocketException


public void sendUrgentData(int data) throws IOException  
    //虽然sendUrgentData的参数data是int类型,但只有这个int类型的低字节被发送(即一个字节,8位),其它的三个字节被忽略。

    该选项为true时,表示支持发送一个字节的TCP紧急数据。Socket类的sendUrgentData( int data ) 方法用于发送一个字节(8位)的TCP紧急数据。

而为false时,表示当接收方收到紧急数据时不作任何处理,直接丢弃。

但是问题是接收方会把接收到的紧急数据与普通数据放在同样的队列中,除非使用一些更高层次的协议,否则接收方处理紧急数据的能力非常有限,当紧急数据到来时,接收方不会得到任何的通知,因此接收方很难区分普通数据与紧急数据,只好按同样的方式处理他们。

//服务端代码:
public class OOBInlineServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(7777);
            System.out.println("服务器已经启动,端口号:7777");
            while (true)
            {
                Socket socket = serverSocket.accept();
                // 须在服务端中也设置为true,否则无法接受到客户端发送过来的紧急数据:
                socket.setOOBInline(true); 
                InputStream in = socket.getInputStream();
                InputStreamReader inReader = new InputStreamReader(in);
                BufferedReader bReader = new BufferedReader(inReader);
                System.out.println(bReader.readLine());
                System.out.println(bReader.readLine());
                socket.close();
            }
    }

}

//客户端代码:
public class OOBInlineClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 7777);
            socket.setOOBInline(true);
            OutputStream out = socket.getOutputStream();
            OutputStreamWriter outWriter = new OutputStreamWriter(out);
            outWriter.write(67);              // 向服务器发送字符"C"
            outWriter.write(" Hello World\r\n ");
            socket.sendUrgentData(65);        // "A"
            socket.sendUrgentData(68);        // "D"
            outWriter.flush();
            socket.sendUrgentData(322);        // "B"  322分布在两个字节上,但是其低位为:0100 0010 即刚好跟 66 一样,
            socket.close();
    }
}

    猜测下上述输出结果: 猜测可能为: 

        C Hello World 

        A D   B

但正常结果输出为:

        Java网络学习笔记3:设置Socket选项(补充)

由此可见:使用sendUrgentData()方法发送数据后,系统会立即将这些数据发送出去,而使用write()(首先是将数据存放在了缓冲区)发送数据,必须要使用flush()方法才会真正发送数据。

另外注意的是:在使用 setOOBInline()方法时,要注意必须在客户端和服务端程序同时使用该方法,打开SO_OOBInline选项,否则无法命名sendUrgentData来发送数据。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
SOCKET选项
1. IP\_TRANSPARENT\1\socket设置该选项后,可以处理发往非本机的数据包。  \2\使用流程:    配置防火墙和路由:iptablestmangleAPREROUTING!d10.0.110.250ptcpjTPROXYonport10000on
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
10个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这