美文网首页java 编程
BIO TCP/IP协议 网络通信

BIO TCP/IP协议 网络通信

作者: 真海ice | 来源:发表于2018-02-09 14:42 被阅读0次

解释:BIO → Block input output(阻塞式的输入和输出)
使用场景:一般应用于传统的网络通信;请求 (客户端)、响应 (服务端)两方,约定好使用TCP/IP通信;双方约定好报文格式 (报文头+报文体)及编码格式 (这里用UTF-8),报文头内容 (约定好长度比如8位,不够前面补零)里面内容为报文体长度,再根据报文头内容,获取后面的报问体的内容。
例如:报文示例: 00000007aaaaaaa;报文体内容为7个a,所以报文头长度为7不够八位前面补零。

一、 作为服务端

SocketServerThread: 用来监听端口的服务类
ThreadOperate:处理每一个请求的新线程

/**
 *  serverSocket服务 
 *  
 *  此线程只负责接收客户端请求,接收到以后再创造新的线程去处理
 * @author zhb
 *
 */
public class SocketServerThread extends Thread{
    // 此服务端线程只会在启动时创建一个
    private ServerSocket serversocket;

    /**
     * 创建服务端线程  
     * @param serversocket 服务端的一个socket
     * @throws IOException
     */
    public SocketServerThread(ServerSocket serversocket) throws IOException{
        if(serversocket==null){
            this.serversocket = new ServerSocket(Constant.serverSocketPort);
        }
    }

    // 线程运行体
    @Override
    public void run(){
        // 如果此服务线程没有被中断
        while(!this.isInterrupted()){           
            try {   
                System.out.println("服务线程开启成功,正在等待客户端的请求");
                // 服务线程一直处于阻塞状态,直到有客户端请求
                Socket socket = serversocket.accept();
                System.err.println("请求来了");
                // 设置接收超时的时间 单位毫秒
                socket.setSoTimeout(Constant.reqTimeOut);   
                if(socket != null && !socket.isClosed()){
                    // 创建一个新线程去处理请求信息
                    new Thread(new ThreadOperate(socket)).start();
                }
            } catch (IOException e) {
                System.err.println("服务线程出现异常:" + e.getMessage());
            } 
        }   
    }

    // 开启线程主函数
    public static void main(String[] args) throws IOException {
        new SocketServerThread(null).start();
    }

}
/**
 * 处理每一个请求开的新的线程
 * @author zhb
 */
public class ThreadOperate implements Runnable {

    // 处理某个客户端请求的socket
    private Socket socket;
    public ThreadOperate(Socket socket){
        this.socket = socket;
    }
        
    public void run() {
        InputStream in = null;
        OutputStream out = null;    
        System.err.println("处理客户请求的新线程开启成功");
        try {
            in = socket.getInputStream();
            out = socket.getOutputStream();
            // 存放报文头的字节数组
            byte[] reqHeadByte = new byte[Constant.reqHeadLength];
            //读取报文头内容
            in.read(reqHeadByte);
            String reqHeadStr = new String(reqHeadByte, Constant.charset);

            //报文头内容是报问体的长度
            int reqBodylength = Integer.valueOf(reqHeadStr);
            
            // 创建容纳报文体的字节数组
            byte[] bodyByte = new byte[reqBodylength];
            in.read(bodyByte);
            
            // 将报问体数组转成报文字符串
            String reqBodyStr = new String(bodyByte, Constant.charset);
            System.err.print("接受报文内容为:" + reqBodyStr);
            
            // 这里要处理接收到的请求报文内容reqBodyStr,完后返回处理结果
            
            //返回响应内容体
            String resBodyStr = "这里是响应内容→ "+ reqBodyStr;
            //返回响应内容的字节数组
            byte[] respBodyByte = resBodyStr.getBytes(Constant.charset);
            
            //转成约定好的报文头长度,不足的前面补零
            StringBuilder format = new StringBuilder("%0").append(Constant.respHeadLength).append("d");
            byte[] respHeadByte = String.format(format.toString(), respBodyByte.length).getBytes();
            
            // 输出报文头信息
            out.write(respHeadByte);
            // 输出报文体内容
            out.write(respBodyByte);
            // 发送响应信息
            out.flush();
            
        } catch (IOException e) {
            System.err.println("读取报文内容报异常::" + e.getMessage());
        }finally{   
            //线程结束后的处理
            endHandler(in, out);
        }
    }
    
    /**
     * 线程结束后的处理
     * @param in
     * @param out
     */
    private void endHandler(InputStream in, OutputStream out) { 
        if(in != null){
            try {
                in.close();
            } catch (IOException e) {
                System.err.println("关闭输入流出现异常:" + e.getMessage());
            }
        }   
        if(out != null){
            try {
                out.close();
            } catch (IOException e) {
                System.err.println("关闭输出流出现异常:" + e.getMessage());
            }
        }
        if(socket != null){
            try {
                socket.close();
            } catch (IOException e) {
                System.err.println("关闭线程出现异常:" + e.getMessage());
            }
        }
    }


}

一、 作为客户端

ClientTest : 模拟客户端请求开启线程
ClientThread:具体的某个客户端线程的请求和响应
Constant : 服务端和客户端都用到的常量参数

/**
 *  测试客户端发送请求
 * @author zhb
 */
public class ClientTest {

    // 用于测试并发用
    private static CountDownLatch cdl = new CountDownLatch(1);
    
    public static void main(String[] args) throws IOException, InterruptedException {
        
        for(int i=0; i < 10 ; i++){
            Socket socket = new Socket("localhost", Constant.serverSocketPort);
            new Thread(new ClientThread(socket, i, cdl)).start();
        }
        Thread.sleep(3000);
        cdl.countDown();
    }
    
}


/**
 * 客户端发送请求线程,并接受服务器响应信息 
 * @author zhb
 *
 */
public class ClientThread implements Runnable {
    
    // 客户端的socket
    private Socket socket;
    // 用于标记第几个请求
    private int i;
    // 用于模拟 客户端的并发
    private CountDownLatch cdl;

    public ClientThread(Socket socket) {
        this.socket = socket;
    }   
    
    public ClientThread(Socket socket, int i) {
        this.socket = socket;
        this.i =  i;    
    }

    public ClientThread(Socket socket, int i, CountDownLatch cdl) {
        this.socket = socket;
        this.i =  i;
        this.cdl = cdl;
    }

    public void run() {
    
        try {
            //并发测试信息号
            cdl.await();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }       
        
        OutputStream out = null;
        InputStream in = null;
        try {   
            // 客户端请求信息
            clientReqSend(out);
            // 接收服务端返回的信息
            clientRespReceive(in);  
    
        } catch (IOException e) {
            e.printStackTrace();
        }finally{   
            //线程结束后的处理
            endHandler(in, out);
        }
    }

    /**
     * 客户端发送请求信息
     * 
     * @param out
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    private void clientReqSend(OutputStream out) throws IOException, UnsupportedEncodingException {
        out = socket.getOutputStream();
        String reqBodyStr = "测试请求内容"+String.valueOf(i);
        System.err.println(reqBodyStr);
        // 请求报文体 字节数组
        byte[] reqBodyByte = reqBodyStr.getBytes(Constant.charset);
        // 请求报文头字节数组,不够位数前面补零
        StringBuilder format = new StringBuilder("%0").append(Constant.respHeadLength).append("d");
        byte[] reqHeadByte = String.format(format.toString(), reqBodyByte.length).getBytes(Constant.charset);
        // 输出报文头内容
        out.write(reqHeadByte);
        // 输出报文体内容
        out.write(reqBodyByte);
        // 发送
        out.flush();
    }   

    /**
     * 客户端接收响应信息
     * 
     * @param in
     * @throws IOException
     * @throws UnsupportedEncodingException
     */
    private void clientRespReceive(InputStream in) throws IOException, UnsupportedEncodingException {
        
        in = socket.getInputStream();       
        // 存放报文头的字节数组
        byte[] reqHeadByte = new byte[Constant.reqHeadLength];
        //读取报文头内容
        in.read(reqHeadByte);
        String reqHeadStr = new String(reqHeadByte, Constant.charset);
        
        //报文头内容是报问体的长度
        int reqBodylength = Integer.valueOf(reqHeadStr);
        
        // 创建容纳报文体的字节数组
        byte[] bodyByte = new byte[reqBodylength];
        
        int off = 0;
        int nReadLength = 0;
        //循环读取直到读取到报文体长度
        while(off < reqBodylength){
            nReadLength = in.read(bodyByte, off, reqBodylength - off);
            if(nReadLength > 0){
                off = off + nReadLength;
            }else{
                break;
            }
        }
        
        // 将报问体数组转成报文字符串
        String reqBodyStr = new String(bodyByte, Constant.charset);
        System.err.println("接受服务端的请求信息内容为:" +i+ reqBodyStr);
    }
    
    /**
     * 线程结束后的处理
     * @param in
     * @param out
     */
    private void endHandler(InputStream in, OutputStream out) { 
        if(in != null){
            try {
                in.close();
            } catch (IOException e) {
                System.err.println("关闭输入流出现异常:" + e.getMessage());
            }
        }
        if(out != null){
            try {
                out.close();
            } catch (IOException e) {
                System.err.println("关闭输出流出现异常:" + e.getMessage());
            }
        }   
        if(socket != null){
            try {
                socket.close();
            } catch (IOException e) {
                System.err.println("关闭线程出现异常:" + e.getMessage());
            }
        }
    }


}
/**
 * 系统常量
 * 
 * @author zhb
 *
 */
public class Constant {
    
    // 请求报文头的位数
    public static final int reqHeadLength = 8;
    // 返回响应报文头的长度
    public static final int resHeadLength = 8;  
    // 字节数组和字符创之间的转换时的编码 
    public static final String charset = "UTF-8";
    //接收请求的最长时间 单位毫秒
    public static final int reqTimeOut = 5000;
    //接收请求的服务端口
    public static final int serverSocketPort = 8080;    
}

相关文章

网友评论

    本文标题:BIO TCP/IP协议 网络通信

    本文链接:https://www.haomeiwen.com/subject/opcftftx.html