socket

https://www.cnblogs.com/yiwangzhibujian/p/7107785.html




package socket;

import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by Administrator on 2018/3/12.
 */
public class SocketServer {
    public static void main(String[] args) throws Exception {
        // 监听指定的端口
        int port = 55533;
        ServerSocket server = new ServerSocket(port);

        // server将一直等待连接的到来
        System.out.println("server将一直等待连接的到来");
        Socket socket = server.accept();
        // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
        InputStream inputStream = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len;
        StringBuilder sb = new StringBuilder();
        while ((len = inputStream.read(bytes)) != -1) {
            //注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
            sb.append(new String(bytes, 0, len,"UTF-8"));
        }
        System.out.println("get message from client: " + sb);
        inputStream.close();
        socket.close();
        server.close();
    }
}


package socket;

import java.io.OutputStream;
import java.net.Socket;

/**
 * Created by Administrator on 2018/3/12.
 */
public class SocketClient {
    public static void main(String args[]) throws Exception {
        // 要连接的服务端IP地址和端口
        String host = "127.0.0.1";
        int port = 55533;
        // 与服务端建立连接
        Socket socket = new Socket(host, port);
        // 建立连接后获得输出流
        OutputStream outputStream = socket.getOutputStream();
        String message="你好  yiwangzhibujian";
        socket.getOutputStream().write(message.getBytes("UTF-8"));
        outputStream.close();
        socket.close();
    }
}



    public static  void server5()throws Exception {
// 监听指定的端口
        int port = 55533;
        ServerSocket server = new ServerSocket(port);
        // server将一直等待连接的到来
        System.out.println("server将一直等待连接的到来");

        //如果使用多线程,那就需要线程池,防止并发过高时创建过多线程耗尽资源
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        while (true) {
            Socket socket = server.accept();

            Runnable runnable=()->{
                try {
                    // 建立好连接后,从socket中获取输入流,并建立缓冲区进行读取
                    InputStream inputStream = socket.getInputStream();
                    byte[] bytes = new byte[1024];
                    int len;
                    StringBuilder sb = new StringBuilder();
                    while ((len = inputStream.read(bytes)) != -1) {
                        // 注意指定编码格式,发送方和接收方一定要统一,建议使用UTF-8
                        sb.append(new String(bytes, 0, len, "UTF-8"));
                    }
                    Thread.sleep(5000);
                    System.out.println("get message from client: " + sb);
                    inputStream.close();
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            };
            threadPool.submit(runnable);
        }
    }




    public static  void clent3()throws Exception {
        // 要连接的服务端IP地址和端口
        String host = "127.0.0.1";
        int port = 55533;
        // 与服务端建立连接
        Socket socket = new Socket(host, port);
        // 建立连接后获得输出流
        OutputStream outputStream = socket.getOutputStream();
        String message = "你好  yiwangzhibujian";
        //首先需要计算得知消息的长度
        byte[] sendBytes = message.getBytes("UTF-8");
        //然后将消息的长度优先发送出去
        outputStream.write(sendBytes.length >> 8);
        outputStream.write(sendBytes.length);
        //然后将消息再次发送出去
        outputStream.write(sendBytes);
        outputStream.flush();
        //==========此处重复发送一次,实际项目中为多个命名,此处只为展示用法
        message = "第二条消息";
        sendBytes = message.getBytes("UTF-8");
        outputStream.write(sendBytes.length >> 8);
        outputStream.write(sendBytes.length);
        outputStream.write(sendBytes);
        outputStream.flush();
        //==========此处重复发送一次,实际项目中为多个命名,此处只为展示用法
        message = "the third message!";
        sendBytes = message.getBytes("UTF-8");
        outputStream.write(sendBytes.length >> 8);
        outputStream.write(sendBytes.length);
        outputStream.write(sendBytes);

        outputStream.close();
        socket.close();
    }


5.4 拆包和黏包

  使用Socket通信的时候,或多或少都听过拆包和黏包,如果没听过而去贸然编程那么偶尔就会碰到一些莫名其妙的问题,所有有这方面的知识还是比较重要的,至少知道怎么发生,怎么防范。

  现在先简单说明下拆包和黏包的原因:

  • 拆包:当一次发送(Socket)的数据量过大,而底层(TCP/IP)不支持一次发送那么大的数据量,则会发生拆包现象。
  • 黏包:当在短时间内发送(Socket)很多数据量小的包时,底层(TCP/IP)会根据一定的算法(指Nagle)把一些包合作为一个包发送。

  首先可以明确的是,大部分情况下我们是不希望发生拆包和黏包的(如果希望发生,什么都去做即可),那么怎么去避免呢,下面进行详解?

5.4.1 黏包

  首先我们应该正确看待黏包,黏包实际上是对网络通信的一种优化,假如说上层只发送一个字节数据,而底层却发送了41个字节,其中20字节的I P首部、 20字节的T C P首部和1个字节的数据,而且发送完后还需要确认,这么做浪费了带宽,量大时还会造成网络拥堵。当然它还是有一定的缺点的,就是因为它会合并一些包会导致数据不能立即发送出去,会造成延迟,如果能接受(一般延迟为200ms),那么还是不建议关闭这种优化,如果因为黏包会造成业务上的错误,那么请改正你的服务端读取算法(协议),因为即便不发生黏包,在服务端缓存区也可能会合并起来一起提交给上层,推荐使用长度+类型+数据模式。

  如果不希望发生黏包,那么通过禁用TCP_NODELAY即可,Socket中也有相应的方法:

void setTcpNoDelay(boolean on) 

  通过设置为true即可防止在发送的时候黏包,但是当发送的速率大于读取的速率时,在服务端也会发生黏包,即因服务端读取过慢,导致它一次可能读取多个包。

5.4.2 拆包

  这个问题应该引起重视,在TCP/IP详解中说过:最大报文段长度(MSS)表示TCP传往另一端的最大块数据的长度。当一个连接建立时,连接的双方都要通告各自的 MSS。客户端会尽量满足服务端的要求且不能大于服务端的MSS值,当没有协商时,会使用值536字节。虽然看起来MSS值越大越好,但是考虑到一些其他情况,这个值还是不太好确定,具体详见《TCP/IP详解 卷1:协议》。

  如何应对拆包,其实在上面2.3节已经介绍过了,那就是如何表明发送完一条消息了,对于已知数据长度的模式,可以构造相同大小的数组,循环读取,示例代码如下:

复制代码
int length=1024;//这个是读取的到数据长度,现假定1024
byte[] data=new byte[1024];
int readLength=0;
while(readLength<length){
    int read = inputStream.read(data, readLength, length-readLength);
    readLength+=read;
}
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值