Android Socket通信(三) -- TCP 详细配置和传递基础数据

系列文章:
Android Socket 系列更新计划
Android Socket通信(一) – 初识与相遇
Android Socket通信(二) --UDP,单播,广播和多播(组播)
Android Socket通信(三) – TCP 配置和传递基础数据
Android Socket通信(四) – UDP与TCP结合传输数据
Android Socket通信(五) – 实现一个多人聊天室

工程连接 : https://github.com/LillteZheng/SocketDemo

上一章中,我们学习了 UDP 的基本概念,而第一张中,则学习了TCP的基本发送功能,这章,我们来学习 TCP 的基础配置和发送基础数据,如byte,char。

在这篇文章中,你将学习到:

  • socket tcp 的配置
  • tcp 传递byte,char,float 等基础数据

一、tcp 设置

首先,先看看客户端,在一般的设置中,我们可以使用 Socket socket = new Socket() 去创建客户端,而实际中个,创建 socket 可以还有配置代理,固定端口等,如下:

private static Socket createSocket() throws IOException {
         /*
        // 无代理模式,等效于空构造函数
        Socket socket = new Socket(Proxy.NO_PROXY);

        // 新建一份具有HTTP代理的套接字,传输数据将通过www.baidu.com:8080端口转发
        Proxy proxy = new Proxy(Proxy.Type.HTTP,
                new InetSocketAddress(Inet4Address.getByName("www.baidu.com"), 8800));
        socket = new Socket(proxy);

        // 新建一个套接字,并且直接链接到本地20000的服务器上
        socket = new Socket("localhost", PORT);

        // 新建一个套接字,并且直接链接到本地20000的服务器上
        socket = new Socket(Inet4Address.getLocalHost(), PORT);

        // 新建一个套接字,并且直接链接到本地20000的服务器上,并且绑定到本地20001端口上
        socket = new Socket("localhost", PORT, Inet4Address.getLocalHost(), LOCAL_PORT);
        socket = new Socket(Inet4Address.getLocalHost(), PORT, Inet4Address.getLocalHost(), LOCAL_PORT);
        */

        Socket socket = new Socket();
        // 绑定到本地20000端口,在 connect 连接服务器之前,这样 客户端的端口就不会再变动了
        socket.bind(new InetSocketAddress(Inet4Address.getLocalHost(), 20000));

        return socket;
    }

这里讲一下 bind 的属性,比如,我们在第一章中,socket 客户端的端口是一直变动的,这是因为我们并没有制定,而端口由系统去指定;当我们使用了 bind 之后,则客户端的端口就指定好了,不会再变动了;接着继续配置 socket 的其他属性:

 private static void configSocket(Socket socket)  throws IOException{
        // 设置读取超时时间为2秒
        socket.setSoTimeout(2000);

        // 是否复用未完全关闭的Socket地址,需要再 bind 之前有效
        socket.setReuseAddress(true);

        /**
         * 是否开启 nagle 算法,这里是关闭
         * tcp 每次发送数据都需要 ack 应答,如果关闭 nagle 算法,并不会每次新数据到来就立刻
         * 回送 ack,可能等到第二次或第三次再返回 ack,客户端在拿到 ack 之前,会一直等待,等
         * ack 回来之后,再把堆积的数据一次性发送给服务端,这样有助于弱网的情况
         */
        socket.setTcpNoDelay(true);

        // 是否需要在长时无数据响应时发送确认数据(类似心跳包),时间大约为2小时
        socket.setKeepAlive(true);

        // 对于close关闭操作行为进行怎样的处理;默认为false,0
        // false、0:默认情况,关闭时立即返回,底层系统接管输出流,将缓冲区内的数据发送完成
        // true、0:关闭时立即返回,缓冲区数据抛弃,直接发送RST结束命令到对方,并无需经过2MSL等待
        // true、200:关闭时最长阻塞200毫秒,随后按第二情况处理
        socket.setSoLinger(true, 200);

        // 是否让紧急数据内敛,默认false;紧急数据通过 socket.sendUrgentData(1);发送
        socket.setOOBInline(true);

        // 设置接收发送缓冲器大小
        socket.setReceiveBufferSize(64 * 1024 * 1024);
        socket.setSendBufferSize(64 * 1024 * 1024);

        // 设置性能参数:短链接,延迟,带宽的相对重要性,这里采用的权重的意思
        socket.setPerformancePreferences(1, 1, 0);
    }

需要注意的是 socket.setPerformancePreferences(1, 1, 0); 是权重的意思,就算设置成100💯100 也是1:1:1 的关系,这个可以根据业务需求去改,如果想要延迟低,则提高这个权重即可,其他的属性都说明得很清楚了。
需要注意的是,这些配置都需要在 connect 连接服务器之前,不然设置了无效;

接着看发送基础数据,这里采用 ByteBuffer 来实现,如下:

		System.out.println("开始传递基础数据");
        OutputStream outputStream = socket.getOutputStream();
        InputStream inputStream = socket.getInputStream();
        byte[] buffer = new byte[256];
        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
        byteBuffer.put((byte) 1);
        byteBuffer.putChar('a');
        byteBuffer.putInt(12345);
        byteBuffer.putLong(23422345);
        byteBuffer.putDouble(123435465);
        byteBuffer.putFloat(234.234234f);
        byteBuffer.put("hello 你好".getBytes());

        outputStream.write(buffer,0,byteBuffer.position()+1);

至于 ByteBuffer 的使用,建议先了解,后面讲到 NIO 的时候,我们再来详细分析。

这样客户端就写好了;

接着看服务器端的配置:
首先是创建 serverSocket:

private static ServerSocket createServerSocket() throws IOException {
        // 创建基础的ServerSocket
        ServerSocket serverSocket = new ServerSocket();

        // 绑定到本地端口20000上,并且设置当前可允许等待链接的队列为50个
        //serverSocket = new ServerSocket(PORT);

        // 等效于上面的方案,队列设置为50个
        //serverSocket = new ServerSocket(PORT, 50);

        // 与上面等同
        // serverSocket = new ServerSocket(PORT, 50, Inet4Address.getLocalHost());

        return serverSocket;
    }

接着配置 serversocket,这里跟 客户端差不多:

   private static void configServerSocket(ServerSocket serverSocket) throws SocketException {
        // 是否复用未完全关闭的地址端口
        serverSocket.setReuseAddress(true);

        // 设置接收buf,当大于64MB,则需要分片
        serverSocket.setReceiveBufferSize(64 * 1024 * 1024);

        // 设置serverSocket#accept超时时间,加入设置了2000,在 2s 内,没客户端接入,则 accept 报错
        // serverSocket.setSoTimeout(2000);

        // 设置性能参数:短链接,延迟,带宽的相对重要性,这里采用的权重的意思
        serverSocket.setPerformancePreferences(1, 1, 1);
    }

上面看到 backlog 的意思,其实就是接入丢列大小,所以一般我们可以这样配置,当然也可以直接配置:

ServerSocket serverSocket = createServerSocket();
configServerSocket(serverSocket);
//backlog 表示可以等待的队列大小,加入有第51个客户端接入,则客户端会提示错误,一般不设置
serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), Constans.PORT),50);

接着就是读取基础数据了:

//拿到数据流
        InputStream inputStream = accept.getInputStream();
        byte[] buffer = new byte[256];
        int read = inputStream.read(buffer);
        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer,0,read);
        byte b = byteBuffer.get();
        char aChar = byteBuffer.getChar();
        int anInt = byteBuffer.getInt();
        long aLong = byteBuffer.getLong();
        double aDouble = byteBuffer.getDouble();
        float aFloat = byteBuffer.getFloat();
        
        //拿到现在的  position
        int pos = byteBuffer.position();
        String msg = new String(buffer,pos,read - pos - 1);

结果如下:
在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值