Socket编程、协议理解

RYP协议:基于TCP/IP协议之上的自定义协议Rocky Protocol。
协议用途:学习并理解顶层协议基础中的基础原理和理论说明。

    // 协议 RYP
    // 协议规定:第一个字节为类型判断,1为文件流 其他为文字流。
    // 除第一字节后的body体即为传输内容。

简单说明

为了更好的学习Socket,实现Socket的过程中,更好的理解什么是应用层协议,也为了进一步理解协议的概念。
目的:学习Socket编程,了解协议的概念。
语言:Java

协议: 经商讨、协商而制定的多方公共认可的、遵守的约定。

举例:我们中国人讲的话被称作中文,这个就是世界共同认可的,达成共识的。
若你自己创建了一个语言发音,但是只是自己知道不成体系,这不被他人认可的就不能称作有协议的概念。

Socket编程

所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。 1

Socket编程,通常是一对套接字之间的通信交互。在下面的实例中,体现出来的双端分别为:服务端、客户端。

服务端:提供接入服务,并提供特有的服务和业务处理,服务端负责消息的接收,业务的处理和结果回写。作为服务端不可自主选择客户端,只能等待客户端的接入来完成服务,类似一个特定功能的应用程序,只为客户端提供特有的服务。

客户端:接入服务端,获取服务端提供的特定功能的服务。客户端可以指定服务端,客户端与客户端之间无法通信。

Socket 常用接口

方法名称入参说明
getInputStreamvoid获取读流
getOutputStreamvoid获取写流
shutdownInputvoid说明当前读流已完成,不会在进行读流操作了。is流也会失效。
shutdownOutputvoid说明当前写流已完成,不会进行写流操作了。os流也会失效。
closevoid关闭当前连接

Socket服务端业务编码

    // 协议 RYP
    // 协议规定:第一个字节为类型判断,1为文件流 其他为文字流。
    // 除第一字节后的body体即为传输内容

    public static final String FILE_RES = "服务端回复::文件接收成功 (~v~)";

    public static final String MSG_RES = "服务端回复::消息已成功接收!";

    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(55555);
            while (true) {
                System.out.println("服务端接收准备完成...");
                Socket client = serverSocket.accept();
                InputStream is = client.getInputStream();
                byte type = getType(is);
                if (type == TypeEnums.FILE_TYPE) {
                    fileServe(is);
                } else {
                    msgServe(is);
                }
                client.shutdownInput();
                writeResponse(client, type);
                client.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 文件服务
     *
     * @param inputStream
     * @throws IOException
     */
    private static void fileServe(InputStream inputStream) throws IOException {
        System.out.println("当前是文件类型!");
        String filePath = "C:\\log.txt";
        FileOutputStream fos = new FileOutputStream(filePath);
        OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
        char[] buff = new char[1024];
        BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        while (br.read(buff) != -1) {
            String str = new String(buff);
            osw.write(str);
        }
        osw.close();
        System.out.println("客户端发送文件已接收完成!");
    }
    
    /**
     * 消息服务
     *
     * @param is
     * @throws IOException
     */
    private static void msgServe(InputStream is) throws IOException {
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String temp = null;
        StringBuilder sb = new StringBuilder();
        while (null != (temp = br.readLine())) {
            sb.append(temp);
        }
        System.out.println("客户端请求:" + sb.toString());
    }
    
    /**
     * 消息写回
     *
     * @param client
     * @param type   业务类型 1 文件 其他:消息
     * @throws IOException
     */
    private static void writeResponse(Socket client, byte type) throws IOException {
        OutputStream outputStream = client.getOutputStream();
        PrintWriter writer = new PrintWriter(outputStream);
        writer.println(type == 1 ? FILE_RES : MSG_RES);
        writer.flush();
        writer.close();
    }
   
    /**
     * 获取业务类型
     *
     * @param is
     * @return
     * @throws IOException
     */
    private static byte getType(InputStream is) throws IOException {
        byte[] buf = new byte[1];
        is.read(buf);
        return buf[0];
    }

代码说明

ServerSocket serverSocket = new ServerSocket(55555)
用于定义服务端Socket,需定义一个端口,用于客户端的接入。
accept执行后,动服务端即会进入阻塞状态,当存在客户端接入是就会分配一个连接去响应客户端的请求服务。

在代码用例中可以看到,我们首先通过getType方法读流,当前协议的约定是第一个字节作为判断依据,“1:文件服务类型,其他:消息服务类型”。
在识别类型后作具体的业务判断是文件服务还是消息服务。

文件服务(fileServe)

文件服务实现的特定业务为文件存储,客户端将自己想要存储的文件写到流中,服务端读取到文件后,将文件转储到当前服务器中,类似于文件服务器的服务。(没有写读文件,大致理解就可,有兴趣的可以使用Socket编程写一个简易文件服务)

消息服务(msgServe)

消息服务实现的特定业务较为简单,接收客户端发送的信息,展示处理。

消息写会(writeResponse)

获取os流,回写需要返回给客户端的信息。

注意事项

  1. 文件使用字符流转储,也可使用字节流,但字节流对于存在中文的情况下,byte[]数组上限的情况下会存在乱码的情况且数据量大的情况下性能极低。
  2. 文件服务目前只适合传输txt类型的文件,若其他文件可以扩展业务类型使用字节流进行转储。

Socket客户端业务编码


    public static final byte FILE_TYPE = 1;

    public static final byte MSG_TYPE = 2;

    public static void main(String[] args) {
        try {
            long sl = System.currentTimeMillis();
            Socket socket = new Socket("127.0.0.1", 55235);
            act(socket, FILE_TYPE);
            long se = System.currentTimeMillis();
            System.out.println("总花费时间:" + (se - sl) + "ms");
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 执行器
     *
     * @param socket
     * @param type
     * @throws IOException
     */
    private static void act(Socket socket, byte type) throws IOException {
        if (type == FILE_TYPE) {
            transferFile(socket);
        } else {
            transferMsg(socket);
        }
    }

    /**
     * 文件传输
     *
     * @param socket 套接字
     * @throws IOException
     */
    public static void transferFile(Socket socket) throws IOException {
        System.out.println("客户端准备传输文件...");
        String filePath = "C:\\Users\\rock\\Desktop\\test.txt";
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(FILE_TYPE);
        outputStream.write(fileConvertToByteArray(new File(filePath)));
        socket.shutdownOutput();
        response(socket.getInputStream());
    }

    /**
     * 消息传输
     *
     * @param socket 套接字
     */
    public static void transferMsg(Socket socket) throws IOException {
        System.out.println("客户端准备传输消息...");
        String msg = "This is test msg";
        OutputStream outputStream = socket.getOutputStream();
        outputStream.write(MSG_TYPE);
        outputStream.write(msg.getBytes());
        socket.shutdownOutput();
        response(socket.getInputStream());
    }

    /**
     * 封装回复信息通知
     *
     * @param inputStream 读流
     * @throws IOException
     */
    public static void response(InputStream inputStream) throws IOException {
        InputStreamReader isr = new InputStreamReader(inputStream);
        BufferedReader br = new BufferedReader(isr);
        String temp = null;
        StringBuilder sb = new StringBuilder();
        while (null != (temp = br.readLine())) {
            sb.append(temp);
        }
        System.out.println(sb.toString());
    }

    /**
     * 文件转byte流
     *
     * @param file 文件信息
     * @return
     */
    private static byte[] fileConvertToByteArray(File file) {
        byte[] data = null;
        try {
            FileInputStream fis = new FileInputStream(file);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            int len;
            byte[] buffer = new byte[1024];
            while ((len = fis.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            data = baos.toByteArray();
            fis.close();
            baos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }

代码说明

Socket socket = new Socket(“127.0.0.1”, 55555);
客户端创建连接时,需要指定服务端的IP和端口,这也是套接字的组成。
使用os流将需要的信息写入到流中,传入服务端。(根据协议的约定组装流数据)
无论是文件还是字符串都将转为字节流进行传输。
客户端的处理比服务端来讲,过程简单,也容易理解。
因为客户端是使用功能的一方,只需要传入约定的数据,获得对应的服务即可。

总结

至此,我们就简单的实现了Socket编程,以及完成了一个基于TCP/IP协议实现了另一套协议的约定的内容。之后再次进行应用协议的学习可能有更好的理论基础,因底层原理基本类似,只是顶层设计更为复杂,约定内容更为精细,职能更为清晰。

希望各位对Socket和协议有一个更加清晰的认知


  1. 王雷,TCP/IP网络编程基础教程,北京理工大学出版社,2017.02,第4页 ↩︎

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值