Java干货之Socket自定义传输协议,可用于一般即时通讯

1 篇文章 0 订阅
1 篇文章 0 订阅

原型

客户端 Client

package me.mxzf;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 
 * @Title: Client
 * @Dscription: 客户端原型
 * @author Deleter
 * @date 2017年3月12日 下午1:32:13
 * @version 1.0
 */
public class Client {
    public static final int STREAM_TEXT = 1;// 文本信息
    public static final int STREAM_FILE = 2;// 一般文件
    public static final int STREAM_IMG = 3;// 图片文件
    public static final int STREAM_VOICE = 4;// 音频文件

    public static void main(String[] args) {

        Socket socket;
        BufferedOutputStream bufferedOutput;
        BufferedInputStream bufferedInput;
        DataOutputStream outputStream;
        DataInputStream inputStream;
        String responseMsg; // 响应内容
        StringBuilder content = new StringBuilder();// 测试内容
        try {
            socket = new Socket("localhost", 1234);// 连接地址localhost,端口1234
            bufferedOutput = new BufferedOutputStream(socket.getOutputStream());// 包装流
            bufferedInput = new BufferedInputStream(socket.getInputStream());
            outputStream = new DataOutputStream(bufferedOutput);
            inputStream = new DataInputStream(bufferedInput);
            /*
             * 发送文本信息
             */
            for (int i = 0; i < 100; i++) {
                content.append("测试文本");// 追加文本x100
            }

            Thread.sleep(5000);
            responseMsg = writeContent(content.toString(), inputStream,
                    outputStream);
            System.out.println(responseMsg);
            /*
             * 发送文件
             */
            responseMsg = writeFile(new File("C:/DB.zip"), STREAM_FILE,
                    inputStream, outputStream);
            System.out.println(responseMsg);
            Thread.sleep(5000);
            /*
             * 发送图片
             */
            responseMsg = writeFile(new File("C:/DB.jpg"), STREAM_IMG,
                    inputStream, outputStream);
            System.out.println(responseMsg);
            Thread.sleep(5000);
            /*
             * 发送音频
             */
            responseMsg = writeFile(new File("C:/DB.wma"), STREAM_VOICE,
                    inputStream, outputStream);
            System.out.println(responseMsg);
            socket.close();// 关闭套接字
        } catch (IOException e) {
            e.printStackTrace();// IO异常
        } catch (InterruptedException e) {
            e.printStackTrace();// 线程中断异常
        }
    }

    /*
     * 文本信息传输协议
     */
    public static String writeContent(String content,
            DataInputStream inputStream, DataOutputStream outputStream)
            throws IOException {

        outputStream.writeInt(STREAM_TEXT);// 写类型
        outputStream.writeInt(content.length());// 写长度
        outputStream.writeUTF(content.toString());// 写数据
        outputStream.flush();
        return inputStream.readUTF();
    }

    /*
     * 文件传输协议
     */
    public static String writeFile(File file, int dataType,
            DataInputStream inputStream, DataOutputStream outputStream)
            throws IOException {
        int len;
        byte[] buff = new byte[2048];
        FileInputStream fis = new FileInputStream(file);// 读文件
        outputStream.writeInt(dataType);// 写类型
        outputStream.writeLong(file.length());// 写总长度
        outputStream.writeUTF(file.getName());// 写文件名
        while ((len = fis.read(buff)) != -1) { // 循环写
            outputStream.writeInt(len);// 数据长度
            outputStream.write(buff, 0, len);// 数据
        }
        fis.close();// 关闭文件流
        outputStream.flush();
        String responseMsg = inputStream.readUTF();
        return responseMsg;
    }
}

服务端 Server

package me.mxzf;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 
 * @Title: Server
 * @Dscription: 服务器原型
 * @author Deleter
 * @date 2017年3月12日 下午1:32:11
 * @version 1.0
 */
public class Server {
    public static void main(String[] args) {
        Socket socket = null;
        ServerSocket serverSocket = null;
        BufferedOutputStream bufferedOutput;
        BufferedInputStream bufferedInput;
        DataOutputStream outputStream;
        DataInputStream inputStream;
        try {
            serverSocket = new ServerSocket(1234);
            socket = serverSocket.accept();
            bufferedOutput = new BufferedOutputStream(socket.getOutputStream());
            bufferedInput = new BufferedInputStream(socket.getInputStream());
            outputStream = new DataOutputStream(bufferedOutput);
            inputStream = new DataInputStream(bufferedInput);
            int dataType;
            while (true) {
                dataType = inputStream.readInt();// 读类型
                switch (dataType) {
                case Client.STREAM_TEXT:
                    System.out
                            .println(receiveContent(inputStream, outputStream));
                    break;

                case Client.STREAM_FILE:
                    receiveFile("d:/", Client.STREAM_FILE, inputStream,
                            outputStream);
                    break;
                case Client.STREAM_IMG:
                    receiveFile("d:/", Client.STREAM_IMG, inputStream,
                            outputStream);
                    break;
                case Client.STREAM_VOICE:
                    receiveFile("d:/", Client.STREAM_VOICE, inputStream,
                            outputStream);
                    break;
                }
            }
        } catch (IOException e) {
            // 忽略
        }
    }

    /*
     * 文本信息传输协议
     */
    public static String receiveContent(DataInputStream inputStream,
            DataOutputStream outputStream) throws IOException {
        int dataLength = inputStream.readInt();// 读长度
        if (dataLength == 0)
            return "txt_err";
        String content = inputStream.readUTF();
        outputStream.writeUTF("txt_ok");
        outputStream.flush();
        return content;
    }

    /*
     * 文件传输协议
     */
    public static void receiveFile(String dirPath, int dataType,
            DataInputStream inputStream, DataOutputStream outputStream)
            throws IOException {
        String responseMsg = "";
        long totalLength = 0;
        int getLength = 0;
        byte[] buff = new byte[2048];
        switch (dataType) {
        case Client.STREAM_FILE:
            responseMsg = "file_ok";
            break;
        case Client.STREAM_IMG:
            responseMsg = "img_ok";
            break;
        case Client.STREAM_VOICE:
            responseMsg = "voice_ok";
            break;
        }
        long dataLength = inputStream.readLong();// 读总长度
        String fileName = inputStream.readUTF();// 读文件名
        FileOutputStream fos = new FileOutputStream(new File(dirPath, fileName));// 读文件
        while (true) {
            if (totalLength == dataLength) {
                break;
            }
            getLength = inputStream.readInt();
            totalLength += getLength;
            inputStream.read(buff, 0, getLength);
            fos.write(buff, 0, getLength);
        }
        fos.close();// 关闭文件流
        outputStream.writeUTF(responseMsg);
        outputStream.flush();
    }
}

重构、多线程、代理

客户端 MiniClient

package me.mxzf.rebuild;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Hashtable;

/**
 * 
 * @Title: MiniClient
 * @Dscription: 客户端
 * @author Deleter
 * @date 2017年3月15日 下午12:52:58
 * @version 1.0
 */
public class MiniClient {

    public static final int STREAM_TEXT = 1;
    public static final int STREAM_FILE = 2;
    public static final int STREAM_IMG = 3;
    public static final int STREAM_VOICE = 4;
    public static final int STREAM_ASYNC = 5;

    private String hostName;
    private Integer port;
    private Socket socket;
    private DataOutputStream outputStream;
    private DataInputStream inputStream;

    private String title;
    private Hashtable<String, ArrayList<File>> asyncList;

    /**
     * 构造函数
     * 
     * @param hostName
     *            服务器地址
     * @param port
     *            端口
     */
    public MiniClient(String hostName, Integer port) {
        this.hostName = hostName;
        this.port = port;
    }

    /**
     * 初始化
     * 
     * @throws UnknownHostException
     * @throws IOException
     */
    public void init() throws UnknownHostException, IOException {
        this.socket = new Socket(this.hostName, this.port);
        this.outputStream = new DataOutputStream(new BufferedOutputStream(
                this.socket.getOutputStream()));
        this.inputStream = new DataInputStream(new BufferedInputStream(
                this.socket.getInputStream()));
        this.asyncList = new Hashtable<String, ArrayList<File>>();
    }

    /**
     * 提交文件
     * 
     * @param dataType
     *            文件类型
     * @param file
     *            文件
     * @return 响应内容
     * @throws IOException
     */
    public String postFile(int dataType, File file) throws IOException {
        int len;
        byte[] buff = new byte[2048];
        FileInputStream fis = new FileInputStream(file);// 读文件
        this.outputStream.writeInt(dataType);// 写类型
        this.outputStream.writeLong(file.length());// 写总长度
        this.outputStream.writeUTF(file.getName());// 写文件名
        while ((len = fis.read(buff)) != -1) { // 循环写
            this.outputStream.writeInt(len);// 数据长度
            this.outputStream.write(buff, 0, len);// 数据
        }
        fis.close();// 关闭文件流
        this.outputStream.flush();
        return this.inputStream.readUTF();
    }

    /**
     * 提交文本
     * 
     * @param content
     *            文本内容
     * @return 响应内容
     * @throws IOException
     */
    public String postText(String content) throws IOException {
        this.outputStream.writeInt(STREAM_TEXT);// 写类型
        this.outputStream.writeInt(content.length());// 写长度
        this.outputStream.writeUTF(content.toString());// 写数据
        this.outputStream.flush();
        return this.inputStream.readUTF();
    }

    /**
     * 提交图文(设置标题)
     * 
     * @param title
     *            标题
     */
    public void setTitle(String title) {
        this.title = title;
        this.asyncList.put(title, new ArrayList<File>());
    }

    /**
     * 提交图文(添加文件)
     * 
     * @param file
     *            文件
     */
    public void addFile(File file) {
        if (this.asyncList.get(this.title) != null)
            this.asyncList.get(this.title).add(file);
    }

    /**
     * 提交图文
     * 
     * @return String 响应信息
     * @throws IOException
     */
    public String postAsync() throws IOException {
        int len;
        int size;
        byte[] buff = new byte[2048];
        FileInputStream fis;

        ArrayList<File> files = this.asyncList.get(this.title);

        this.outputStream.writeInt(STREAM_ASYNC);// 写类型
        this.outputStream.writeUTF(this.title);// 写标题
        size = asyncList.get(this.title).size();
        this.outputStream.writeInt(size);// 写文件数量

        for (int i = 0; i < size; i++) {
            fis = new FileInputStream(files.get(i));// 读文件
            this.outputStream.writeLong(files.get(i).length());// 写总长度
            this.outputStream.writeUTF(files.get(i).getName());// 写文件名
            while ((len = fis.read(buff)) != -1) { // 循环写
                this.outputStream.writeInt(len);// 数据长度
                this.outputStream.write(buff, 0, len);// 数据
            }
            fis.close();// 关闭文件流
            this.outputStream.flush();
        }
        // 清理内存
        this.asyncList.clear();
        return this.inputStream.readUTF();
    }

    /**
     * 关闭套接字
     * 
     * @throws IOException
     */
    public void close() throws IOException {
        this.socket.close();
    }
}

服务端 MiniServer

package me.mxzf.rebuild;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import me.mxzf.rebuild.handler.impl.AsyncHandler;
import me.mxzf.rebuild.handler.impl.FileHandler;
import me.mxzf.rebuild.handler.impl.TextHandler;

/**
 * 
 * @Title: MiniServer
 * @Dscription: 服务器
 * @author Deleter
 * @date 2017年3月15日 下午12:53:07
 * @version 1.0
 */
public class MiniServer {
    private Integer port;
    private Socket socket;
    private ServerSocket serverSocket;

    private ThreadMiniServer threadMiniServer;

    public MiniServer(Integer port) {
        this.port = port;
    }

    public void init(String dirPath) throws IOException {
        serverSocket = new ServerSocket(this.port);
        while (true) {
            socket = serverSocket.accept();
            threadMiniServer = new ThreadMiniServer(socket, dirPath);
            threadMiniServer.setTextHandler(new TextHandler());
            threadMiniServer.setFileHandler(new FileHandler());
            threadMiniServer.setAsyncHandler(new AsyncHandler());
            threadMiniServer.start();
        }
    }
}

服务线程 ThreadMiniServer

package me.mxzf.rebuild;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Hashtable;

import me.mxzf.Client;
import me.mxzf.rebuild.handler.impl.AsyncHandler;
import me.mxzf.rebuild.handler.impl.FileHandler;
import me.mxzf.rebuild.handler.impl.TextHandler;

/**
 * 
 * @Title: ThreadMiniServer
 * @Dscription: 服务线程
 * @author Deleter
 * @date 2017年3月15日 下午2:37:20
 * @version 1.0
 */
public class ThreadMiniServer extends Thread {

    private Socket socket;
    private DataOutputStream outputStream;
    private DataInputStream inputStream;
    private String dirPath;
    private int dataType;
    private TextHandler textHandler;
    private FileHandler fileHandler;
    private AsyncHandler asyncHandler;

    public ThreadMiniServer(Socket socket, String dirPath) {
        this.socket = socket;
        this.dirPath = dirPath;
    }

    public TextHandler getTextHandler() {
        return textHandler;
    }

    public void setTextHandler(TextHandler textHandler) {
        this.textHandler = textHandler;
    }

    public FileHandler getFileHandler() {
        return fileHandler;
    }

    public void setFileHandler(FileHandler fileHandler) {
        this.fileHandler = fileHandler;
    }

    public AsyncHandler getAsyncHandler() {
        return asyncHandler;
    }

    public void setAsyncHandler(AsyncHandler asyncHandler) {
        this.asyncHandler = asyncHandler;
    }

    @Override
    public void run() {
        try {
            outputStream = new DataOutputStream(new BufferedOutputStream(
                    socket.getOutputStream()));
            inputStream = new DataInputStream(new BufferedInputStream(
                    socket.getInputStream()));
            while (true) {
                dataType = inputStream.readInt();// 读类型
                switch (dataType) {
                case MiniClient.STREAM_TEXT:
                    textHandler.onMessage(receiveContent(this.inputStream,
                            this.outputStream));
                    break;

                case MiniClient.STREAM_FILE:
                    fileHandler.onMessage(receiveFile(this.dirPath,
                            Client.STREAM_FILE, this.inputStream,
                            this.outputStream));
                    break;
                case MiniClient.STREAM_IMG:
                    fileHandler.onMessage(receiveFile(this.dirPath,
                            Client.STREAM_IMG, this.inputStream,
                            this.outputStream));
                    break;
                case MiniClient.STREAM_VOICE:
                    fileHandler.onMessage(receiveFile(this.dirPath,
                            Client.STREAM_VOICE, this.inputStream,
                            this.outputStream));
                    break;
                case MiniClient.STREAM_ASYNC:
                    asyncHandler.onMessage(receiveAsync(this.dirPath,
                            this.inputStream, this.outputStream));
                    break;
                }
            }
        } catch (IOException e) {
            // 忽略EOF
        }
    }

    /*
     * 文本信息传输协议
     */
    private String receiveContent(DataInputStream inputStream,
            DataOutputStream outputStream) throws IOException {
        int dataLength = inputStream.readInt();// 读长度
        if (dataLength == 0)
            return "text_err";
        String content = inputStream.readUTF();
        outputStream.writeUTF("text_ok");
        outputStream.flush();
        return content;
    }

    /*
     * 文件传输协议
     * @param dirPath 目录路径
     * @param dataType 数据类型
     */
    private String receiveFile(String dirPath, int dataType,
            DataInputStream inputStream, DataOutputStream outputStream)
            throws IOException {
        String responseMsg = "";
        long totalLength = 0;
        int getLength = 0;
        byte[] buff = new byte[2048];
        switch (dataType) {
        case Client.STREAM_FILE:
            responseMsg = "file_ok";
            break;
        case Client.STREAM_IMG:
            responseMsg = "img_ok";
            break;
        case Client.STREAM_VOICE:
            responseMsg = "voice_ok";
            break;
        }
        long dataLength = inputStream.readLong();// 读总长度
        String fileName = inputStream.readUTF();// 读文件名
        File file = new File(dirPath, fileName);
        FileOutputStream fos = new FileOutputStream(file);// 写文件
        while (true) {
            if (totalLength == dataLength) {
                break;
            }
            getLength = inputStream.readInt();
            totalLength += getLength;
            inputStream.read(buff, 0, getLength);
            fos.write(buff, 0, getLength);
        }
        fos.close();// 关闭文件流
        outputStream.writeUTF(responseMsg);
        outputStream.flush();
        return file.getAbsolutePath();
    }

    /*
     * 混合传输协议
     * @param dirPath 目录路径
     */
    private Hashtable<String, Object> receiveAsync(String dirPath,
            DataInputStream inputStream, DataOutputStream outputStream)
            throws IOException {

        int size;
        int index = 0;
        int getLength;
        long dataLength;
        long totalLength;
        String title;
        String fileName;
        File file;
        FileOutputStream fos;

        byte[] buff = new byte[2048];
        StringBuilder sb = new StringBuilder();
        Hashtable<String, Object> table = new Hashtable<>();
        ArrayList<String> filePaths = new ArrayList<>();

        title = inputStream.readUTF();// 读标题
        table.put("title", title);
        size = inputStream.readInt();// 读取图片数量
        table.put("size", size);

        while (index++ < size) {

            getLength = 0;
            dataLength = 0;
            totalLength = 0;

            dataLength = inputStream.readLong();// 读总长
            fileName = inputStream.readUTF();// 读文件名
            // 写文件
            file = new File(dirPath, System.currentTimeMillis() + fileName);
            fos = new FileOutputStream(file);
            while (true) {
                if (totalLength == dataLength) {
                    break;
                }
                getLength = inputStream.readInt();
                totalLength += getLength;
                inputStream.read(buff, 0, getLength);
                fos.write(buff, 0, getLength);
            }
            fos.close();// 关闭文件流
            sb.append(index);
            sb.append(":ok,");
            filePaths.add(file.getAbsolutePath());// 添加文件路径
        }
        table.put("paths", filePaths);
        outputStream.writeUTF(sb.substring(0, sb.length() - 1));
        outputStream.flush();
        return table;
    }
}

数据处理器

一般接口 BaseHandler

package me.mxzf.rebuild.handler;

import java.io.IOException;

/**
 * 
 * @Title: BaseHandler
 * @Dscription: 基本处理器
 * @author Deleter
 * @date 2017年3月15日 下午2:06:52
 * @version 1.0
 */
public interface BaseHandler<T> {

    /**
     * 当数据到达
     * 
     * @param t
     *            数据
     * @throws IOException
     */
    public void onMessage(T t) throws IOException;
}

字符串处理器 TextHandler

package me.mxzf.rebuild.handler.impl;

import java.io.IOException;

import me.mxzf.rebuild.handler.BaseHandler;

public class TextHandler implements BaseHandler<String> {

    /**
     * @param t
     *            文本内容
     */
    @Override
    public void onMessage(String t) throws IOException {
        // 处理数据
    }
}

文件处理器 FileHandler

package me.mxzf.rebuild.handler.impl;

import java.io.IOException;

import me.mxzf.rebuild.handler.BaseHandler;

public class FileHandler implements BaseHandler<String> {

    /**
     * @param t
     *            文件所在的路径
     */
    @Override
    public void onMessage(String t) throws IOException {
        // 处理数据
    }
}

图文混传处理器 AsyncHandler

package me.mxzf.rebuild.handler.impl;

import java.io.IOException;
import java.util.Hashtable;

import me.mxzf.rebuild.handler.BaseHandler;

/**
 * 
 * @Title: AsyncHandler
 * @Dscription: 混合处理器
 * @author Deleter
 * @date 2017年3月15日 下午2:17:17
 * @version 1.0
 */
public class AsyncHandler implements BaseHandler<Hashtable<String, Object>> {

    /**
     * @param title
     *            标题
     * @param size
     *            图片数量
     * @param paths
     *            文件路径
     */
    @Override
    public void onMessage(Hashtable<String, Object> table) throws IOException {
        // 处理数据
    }
}

测试、样例

客户端

package me.mxzf.rebuild.test;

import java.io.File;
import java.io.IOException;

import me.mxzf.rebuild.MiniClient;

public class ClientTest {
    public static void main(String[] args) {
        try {
            Thread.sleep(3000);
            MiniClient client = new MiniClient("localhost", 1234);
            client.init();
            client.setTitle("浙大科技园");
            client.addFile(new File("C:/DB.zip"));
            client.addFile(new File("C:/DB.wma"));
            client.addFile(new File("C:/DB.jpg"));
            String result = client.postAsync();
            System.out.println(result);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

服务端

package me.mxzf.rebuild.test;

import java.io.IOException;

import me.mxzf.rebuild.MiniServer;

public class ServerTest {
    public static void main(String[] args) {

        try {
            MiniServer miniServer = new MiniServer(1234);
            miniServer.init("D:/");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

就这么多了,没有使用框架,bug暂时没有发现,如果有测试出有什么问题的,请给我留言,谢谢。


源代码:Socket自定义传输协议源代码

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
Java 数组是存储相同类型数据的集合,它们具有固定大小并且在创建后大小不可更改。在Java中,数组通过声明和初始化来创建。声明数组的语法形式如下: ```java int[] arr; // 声明了一个 int 类型的数组 ``` 在声明数组之后,需要通过初始化该数组,也就是为数组分配内存和赋初值。初始化数组的方式有两种:静态初始化和动态初始化。静态初始化是在声明数组的同时给数组元素赋初值的方法,语法形式如下: ```java int[] arr = {1, 2, 3, 4, 5}; // 静态初始化数组 ``` 动态初始化是在声明数组后通过循环或用户输入等方式给数组元素赋值的方法,语法形式如下: ```java int[] arr = new int[5]; // 动态初始化数组 for (int i = 0; i < arr.length; i++) { arr[i] = i + 1; } ``` Java 数组还具有一些常用的属性和方法,如`length`属性用来获取数组的长度,`clone()`方法用来复制数组,`toString()`方法用来将数组转换为字符串等。 除了一维数组外,Java 还支持多维数组,如二维数组、三维数组等。多维数组的声明和初始化方式与一维数组类似,只是需要使用多个`[]`来表示维度。 值得注意的是,Java 中的数组是引用类型,因此在传递数组参数时,实际上传递的是数组的引用,而不是数组的副本。这意味着在方法中对数组的修改会影响到原数组。 总的来说,了解和掌握 Java 数组的声明、初始化、属性和方法,并能灵活运用,对于 Java 编程是非常重要的。希望本文能够为大家提供关于 Java 数组的全面解析和干货知识。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

凌 烨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值