# TCP-based network programming **Repository Path**: ju-wenhui/tcp-based-network-programming ## Basic Information - **Project Name**: TCP-based network programming - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-06-28 - **Last Updated**: 2024-06-28 ## Categories & Tags **Categories**: Uncategorized **Tags**: TCP, Java, 多线程, Sqlite ## README

南京工程学院

课程设计说明书(论文)

| 题目 | 基于TCP的网络程序设计 | | :----------------: | :--------------------------: | | 课程名称 | 计算机工程项目训练 | | 院(系、部、中心) | 计算机工程学院 | | 专业 | 计算机科学与技术 | | 班级 | 计算机211 | | 学生姓名 | 鞠文慧 | | 学生学号 | 202210107 | | 设计地点 | 信息楼A109 | | 指导教师 | 赵群 | | 设计起止时间 | 2024年6月17日至2024年6月28日 |

基于TCP的网络程序设计

### 一、项目训练目的及意义 ​ 本课程综合数据结构、操作系统、程序设计、计算机网络、软件工程等相关专业课程的理论知识,通过项目驱动的方式,将理论知识与联系实际在一起,通过综合性的项目训练,培养学生独立思考、解决实际工程问题的能力;提高学生设计、实现、调试、测试软件系统的能力。 ### 二、设计任务 - [ ] 使学生掌握应用SOCKET编写网络程序的原理和方法; - [ ] 使学生掌握BASE64编码的原理和编码解码的方法; - [ ] 使学生掌握通过多线程(多进程)实现多任务的方法; - [ ] 使学生掌握程序自动升级的原理和实现方法(选做); - [ ] 使学生掌握使用LSB(Least Significant Bit)技术实现对24位位图进行信息隐写的原理和实现方法(选做); - [ ] 使学生掌握Git工具的使用方法(自学); - [ ] 使学生掌握使用Markdown标记语言撰写技术文档的方法(自学); - [ ] 撰写规范的设计总结报告,培养严谨的作风和科学的态度; - [ ] 使学生掌握Sqlite数据库的移植和使用; - [ ] 实现可视化。 ### 三、主要技术指标和要求 1. 实现客户端和服务端的TCP通信(选做:服务器实现多路转接); 2. Client程序运行后,启动多线程,一个主线程用于实现和用户的交互,另外根据需要启动工作线程; 3. Client启动时首先检测Server端是否有版本升级标志,如果有则自动下载客户端程序,下次启动时启动新的Client程序(选做); 4. Client上传Server的所有数据用Base64编码后上传,Server端接收后解码进行存储(文件直接存储,字段数据用sqlite数据库存储); 5. 文件以及数据的传输用CRC32进行校验(选做); 6. Server收到上传的BMP文件以及其文字信息,用LSB隐写技术把文字信息隐藏进图像文件,并且可以考虑用随机算法每次产生不同的隐写位置(选做); 7. Client收到Server返回的文件后,检查BMP是否采用了LSB隐写,如果有隐写信息,则解析出隐写信息(选做); 8. 所有代码必须用Git进行版本控制,代码和文档提交在Github或者Gitee; 9. 文档必须用Markdown编写,Markdown编辑器推荐使用MarkdownPad; 10. 建议做图形界面的应用程序; 11. Git、Markdown的教程自行学习。 ### 四、设计流程及设计思想说明 #### 框图 - 客户端 wps1.jpg wps2.jpg wps3.jpg - 服务器 wps4.jpg wps5.jpg #### 子模块设计 - 客户端 ​ 客户端设计两个界面,分别是Login界面和ChatView客户端个人聊天界面。 ​ Login界面:有“登录”按钮监听,“登录”按钮触发后会隐藏Login界面,显示聊天界面。 ​ ChatView界面:有“发送”、“发送文件”和关闭聊天界面按钮监听。“发送”按钮触发后,先判断是否获取到文本,若获取成功,编码后发送数据,若没有成功,系统提醒重新输入要发送的文本信息;“发送文件”按钮触发后,会进行文件的选择,获取选择文件的路径,发送文件的名称、大小、内容;关闭聊天界面按钮触发后,该Client程序终止,并向服务端发送客户端离开的信息。 - 服务端 ​ 服务端开启后,直接进入服务端界面并连接数据库,界面会显示客户端聊天的信息以及文件发送和接收的情况。 ​ 聊天信息的接收:服务端接收到客户端们的聊天信息之后,会将信息进行解码,展示在面板上并保存在数据库中,之后将信息再次编码转发给所有在线的客户端。 ​ 文件信息的接收:服务端接收到某一客户端上传的文件之后,判断其他在线客户端是否接收,若接收则将文件进行转发。 ### 五、程序清单 ```java public class ChatView { String userName; //由客户端登录时设置 JTextField text; JTextArea textArea; ClientReadAndWrite.ChatViewListen listener; // 构造函数 public ChatView(String userName) { this.userName = userName ; init(); } // 初始化函数 void init() { //窗口 JFrame JFrame = new JFrame("客户端"); JFrame.setBounds(500,200,400,330); //设置坐标和大小 JFrame.setResizable(false); // 缩放为不能缩放 //面板 JPanel JPanel = new JPanel(); JLabel Lable = new JLabel("用户:" + userName); //用户界面标题 //文本 textArea = new JTextArea("与服务器连接成功\n",12, 35); textArea.setEditable(false); //不可修改文本内容 //滚动面板 JScrollPane Scroll = new JScrollPane(textArea); Scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); // 显示垂直条 //面板上添加标题和滚动面板 JPanel.add(Lable); JPanel.add(Scroll); //编辑文本 text = new JTextField(20); JButton button = new JButton("发送"); JButton openFileBtn = new JButton("发送文件"); JPanel.add(text); JPanel.add(button); JPanel.add(openFileBtn); //“打开文件”按钮------ ----监听 openFileBtn.addActionListener(new ActionListener() {public void actionPerformed(ActionEvent e) {showFileOpenDialog(JFrame);}}); // “发送”按钮--------------监听 listener = new ClientReadAndWrite().new ChatViewListen(); listener.setJTextField(text); listener.setJTextArea(textArea); listener.setChatViewJf(JFrame); text.addActionListener(listener); // 文本框添加监听 button.addActionListener(listener); // 按钮添加监听 JFrame.add(JPanel); JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JFrame.setVisible(true); } // “打开文件”监听 void showFileOpenDialog(JFrame parent) { // 文件选择器,选择要发送的文件 JFileChooser fileChooser = new JFileChooser(); // 选择器默认打开D盘界面 fileChooser.setCurrentDirectory(new File("D:\\")); // 选择文件 int choose = fileChooser.showOpenDialog(parent); // 点击确定 if(choose == JFileChooser.APPROVE_OPTION) { // 获取路径 File file = fileChooser.getSelectedFile(); String path = file.getAbsolutePath(); ClientFileThread.outFileToServer(path); } } } ``` ```java public class Client { // 主函数,新建登录窗口 public static void main(String[] args) { new Login(); } } ``` ```java public class ClientFileThread extends Thread{ private Socket socket = null; private JFrame chatViewJFrame = null; static String userName = null; static PrintWriter out = null; static DataInputStream fileIn = null; static DataOutputStream fileOut = null; static DataInputStream fileReader = null; static DataOutputStream fileWriter = null; public ClientFileThread(String userName, JFrame chatViewJFrame, PrintWriter out) { ClientFileThread.userName = userName; this.chatViewJFrame = chatViewJFrame; ClientFileThread.out = out; } //客户端-------------------------------------------------接收文件 public void run() { try { InetAddress addr = InetAddress.getByName(null); // 获取主机地址 socket = new Socket(addr, 8090); fileIn = new DataInputStream(socket.getInputStream()); fileOut = new DataOutputStream(socket.getOutputStream()); //接收文件 while(true) { String textName = fileIn.readUTF(); long totalLength = fileIn.readLong(); int result = JOptionPane.showConfirmDialog(chatViewJFrame, "是否接受?", "提示", JOptionPane.YES_NO_OPTION); int length = -1; byte[] buff = new byte[1024]; long curLength = 0; //提示框选择结果,0确定,1取消 if(result == 0){ // 新建当前用户的文件夹 File userFile = new File("D:\\software_cache\\ideaProject\\EngineeringTraining\\src\\文件\\" + userName); if(!userFile.exists()) { userFile.mkdir(); } //文件 File file = new File("D:\\software_cache\\ideaProject\\EngineeringTraining\\src\\文件\\" + userName + "\\"+ textName); fileWriter = new DataOutputStream(new FileOutputStream(file)); // 把文件写进本地 while((length = fileIn.read(buff)) > 0) { fileWriter.write(buff, 0, length); fileWriter.flush(); curLength += length; if(curLength == totalLength) { // 强制结束 break; } } String msg = userName + "成功接收文件!"; String msgTrans = codeBase64(msg); out.println(msgTrans); out.flush(); // 提示文件存放地址 JOptionPane.showMessageDialog(chatViewJFrame, "文件存放于:\n" + "D:\\software_cache\\ideaProject\\EngineeringTraining\\src\\文件\\" + userName + "\\" + textName, "提示", JOptionPane.INFORMATION_MESSAGE); } // 不接受文件 else { while((length = fileIn.read(buff)) > 0) { curLength += length; if(curLength == totalLength) { // 强制结束 break; } } } fileWriter.close(); } } catch (Exception e) {} } // 客户端-----------------------------------------------------------发送文件 static void outFileToServer(String path) { try { File file = new File(path); fileReader = new DataInputStream(new FileInputStream(file)); fileOut.writeUTF(file.getName()); // 发送文件名字 fileOut.flush(); fileOut.writeLong(file.length()); // 发送文件长度 fileOut.flush(); int length = -1; byte[] buff = new byte[1024]; while ((length = fileReader.read(buff)) > 0) { // 发送内容 fileOut.write(buff, 0, length); fileOut.flush(); } String msg = userName + "已成功发送文件!"; String msgTrans = codeBase64(msg); out.println(msgTrans); out.flush(); } catch (Exception e) {} } //Base64编码 private static String codeBase64(String msg){ //1、字符串转为字节数组 byte[] bytes = msg.getBytes(); //2、编码 byte[] encodeBytes = Base64.getEncoder().encode(bytes); //3、将编码后的字节数组转为字符串 String msgTrans = new String(encodeBytes); return msgTrans; } } ``` ```java public class ClientReadAndWrite extends Thread{ static Socket mySocket = null; // 一定要加上static,否则新建线程时会清空 static JTextField textInput; static JTextArea textShow; static JFrame chatViewJFrame; static BufferedReader in = null; static PrintWriter out = null; static String userName; // 接收服务端-----------------------------------------------------发送来的消息 public void run() { try { in = new BufferedReader(new InputStreamReader(mySocket.getInputStream())); //不断获取服务端发来的信息 while (true) { String msgTrans = in.readLine(); String msg = decodeBase64(msgTrans); textShow.append(msg + '\n'); //显示在用户自己的界面里 textShow.setCaretPosition(textShow.getDocument().getLength()); //光标移动到文本末尾 } } catch (Exception e) {} } //登陆按钮--------------------------------------------------------监听 class LoginListen implements ActionListener { JTextField textField; //名字 JPasswordField pwdField; //密码 JFrame loginJFrame; //登录窗口 ChatView chatView = null; //用户聊天界面 public void setJTextField(JTextField textField) { this.textField = textField; } public void setJPasswordField(JPasswordField pwdField) { this.pwdField = pwdField; } public void setJFrame(JFrame jFrame) { this.loginJFrame = jFrame; } //登录按钮------------------监听 public void actionPerformed(ActionEvent event) { userName = textField.getText(); String userPwd = String.valueOf(pwdField.getPassword()); //char数组转为string //(!userName.isEmpty() && userPwd.equals("6666")) if(!userName.isEmpty()) { chatView = new ChatView(userName); //新建聊天窗口,设置聊天窗口的用户名 //连接 try { InetAddress addr = InetAddress.getByName(null); //获取主机地址 mySocket = new Socket(addr,8080); loginJFrame.setVisible(false); //登陆成功后,隐藏登录窗口 out = new PrintWriter(mySocket.getOutputStream()); // 输出流 String msg = "用户:" + userName + "成功连接服务器!"; String msgTrans= codeBase64(msg); out.println(msgTrans); // 发送用户名给服务器 out.flush(); } catch (IOException e) { e.printStackTrace(); } // 数据读写线程-----------------启动 ClientReadAndWrite readAndPrint = new ClientReadAndWrite(); readAndPrint.start(); // 文件读写线程-------------- -启动 ClientFileThread fileThread = new ClientFileThread(userName, chatViewJFrame, out); fileThread.start(); } else { JOptionPane.showMessageDialog(loginJFrame, "请输入用户姓名!", "提示", JOptionPane.WARNING_MESSAGE); } } } //聊天界面---------------------------------------------------设置监听 class ChatViewListen implements ActionListener{ public void setJTextField(JTextField text) { textInput = text; // 放在外部类,因为其它地方也要用到 } public void setJTextArea(JTextArea textArea) { textShow = textArea; // 放在外部类,因为其它地方也要用到 } //关闭聊天界面的--------------------------------------------监听 public void setChatViewJf(JFrame jFrame) { chatViewJFrame = jFrame; // 放在外部类,因为其它地方也要用到 chatViewJFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { String msg = "用户:" + userName + "已离开!"; String msgTrans = codeBase64(msg); out.println(msgTrans); out.flush(); System.exit(0); } }); } // 信息发送------------------------------------------------------监听 public void actionPerformed(ActionEvent event) { try { String str = textInput.getText(); // 文本框内容为空 if("".equals(str)) { textInput.grabFocus(); // 设置焦点(可行) // 弹出消息对话框(警告消息) JOptionPane.showMessageDialog(chatViewJFrame, "输入为空,请重新输入!", "提示", JOptionPane.WARNING_MESSAGE); return; } out.println(codeBase64(userName + ":" + str)); // 输出给服务端 out.flush(); textInput.setText(""); // 清空文本框 textInput.grabFocus(); // 设置焦点(可行) } catch (Exception e) {} } } //Base64编码 private static String codeBase64(String msg){ //1、字符串转为字节数组 byte[] bytes = msg.getBytes(); //2、编码 byte[] encodeBytes = Base64.getEncoder().encode(bytes); //3、将编码后的字节数组转为字符串 String msgTrans = new String(encodeBytes); return msgTrans; } //解码base64方法 private static String decodeBase64(String msgTrans){ //1、使用base64类的getDecoder()方法获取一个Decoder实例 Base64.Decoder decoder = Base64.getDecoder(); //2、解码字符串 byte[] decodedBytes = decoder.decode(msgTrans); //3、将解码后的字符转换成字符串 String msg = new String(decodedBytes); return msg; } } ``` ```java public class ForwardFile extends Thread { private Socket nowSocket = null; private DataInputStream input = null; private DataOutputStream output = null; public ForwardFile(Socket socket) { this.nowSocket = socket; } public void run() { try { input = new DataInputStream(nowSocket.getInputStream()); // 输入流 while (true) { // 获取文件名字和文件长度 String textName = input.readUTF(); long textLength = input.readLong(); // 发送文件名字和文件长度给其他在线用户 for(Socket socket: ServerFileThread.list) { output = new DataOutputStream(socket.getOutputStream()); // 输出流 if(socket != nowSocket) { // 发送给其它客户端 output.writeUTF(textName); output.flush(); output.writeLong(textLength); output.flush(); } } // 发送文件内容 int length = -1; long curLength = 0; byte[] buff = new byte[1024]; while ((length = input.read(buff)) > 0) { curLength += length; for(Socket socket: ServerFileThread.list) { output = new DataOutputStream(socket.getOutputStream()); // 输出流 if(socket != nowSocket) { // 发送给其它客户端 output.write(buff, 0, length); output.flush(); } } if(curLength == textLength) { // 强制退出 break; } } } } catch (Exception e) { ServerFileThread.list.remove(nowSocket); // 线程关闭,移除相应套接字 } } } ``` ```java public class Login { JTextField textField = null; JPasswordField pwdField = null; ClientReadAndWrite.LoginListen listener=null; // 构造函数 public Login() { init(); } void init() { //登录窗口,不缩放 JFrame JFrame = new JFrame("登录"); JFrame.setBounds(500, 250, 310, 210); JFrame.setResizable(false); //面板1 JPanel JPanel1 = new JPanel(); JLabel HeadJLabel = new JLabel("登录界面"); HeadJLabel.setFont(new Font(null, 0, 35)); // 设置文本的字体类型、样式 和 大小 JPanel1.add(HeadJLabel); //面板2 JPanel JPanel2 = new JPanel(); JLabel nameJLabel = new JLabel("用户名:"); textField = new JTextField(20); JLabel pwdJLabel = new JLabel("密码: "); pwdField = new JPasswordField(20); JButton loginButton = new JButton("登录"); JPanel2.add(nameJLabel); JPanel2.add(textField); JPanel2.add(pwdJLabel); JPanel2.add(pwdField); JPanel2.add(loginButton); //大面板,用于装面板1和面板2 JPanel JPanel = new JPanel(new BorderLayout()); //BorderLayout布局 JPanel.add(JPanel1, BorderLayout.NORTH); JPanel.add(JPanel2, BorderLayout.CENTER); //监听按钮 listener = new ClientReadAndWrite().new LoginListen(); //新建监听类 listener.setJTextField(textField); listener.setJPasswordField(pwdField); listener.setJFrame(JFrame); pwdField.addActionListener(listener); //密码回车可登录 loginButton.addActionListener(listener); //按钮添加监听 JFrame.add(JPanel); JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //关闭界面程序结束 JFrame.setVisible(true); } } ``` ```java public class MultiChat { JTextArea textArea; // 用于向文本区域添加信息 void setTextArea(String str) { textArea.append(str+'\n'); textArea.setCaretPosition(textArea.getDocument().getLength()); //光标放最后 } // 构造函数 public MultiChat() { init(); } void init() { //窗口 JFrame JFrame = new JFrame("服务器端"); JFrame.setBounds(500,100,450,500); // 设置窗口坐标和大小 JFrame.setResizable(false); // 不可缩放 //容器 JPanel JPanel = new JPanel(); JLabel Lable = new JLabel("服务器端存储的信息"); //文本 textArea = new JTextArea(23, 38); textArea.setEditable(false); // 文本内容不可修改 //滚动面板 JScrollPane Scroll = new JScrollPane(textArea); Scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); // 显示垂直条 JPanel.add(Lable); JPanel.add(Scroll); JFrame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); JFrame.add(JPanel); //容器添加到窗口 JFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 窗口关闭程序结束 JFrame.setVisible(true); } } ``` ```java public class Server{ static ServerSocket server = null; static Socket socket = null; static List list = new ArrayList(); //在线客户端存储,server转发信息 public static void main(String[] args) { MultiChat multiChat = new MultiChat(); // 服务端聊天界面 db db = new db(); try { // 文件传输的线程 ServerFileThread serverFileThread = new ServerFileThread(); serverFileThread.start(); server = new ServerSocket(8080); // 服务器端套接字(只能建立一次) while (true) { // 一直等待连接--------------开启客户端线程 socket = server.accept(); list.add(socket); // 读+转发线程 ServerReadAndWrite readAndPrint = new ServerReadAndWrite(socket, multiChat , db); readAndPrint.start(); } } catch (IOException e1) { e1.printStackTrace(); } } } ``` ```java public class ServerFileThread extends Thread{ ServerSocket server = null; Socket socket = null; static List list = new ArrayList(); // 存储所有在线的用户 public void run() { try { server = new ServerSocket(8090); while(true) { socket = server.accept(); list.add(socket); // 开启文件传输线程 ForwardFile forwardFile = new ForwardFile(socket); forwardFile.start(); } } catch (IOException e) { e.printStackTrace(); } } } ``` ```java //服务端------数据接收----发送----显示-----------线程 public class ServerReadAndWrite extends Thread{ private static final Base64.Decoder DECODE_64 = Base64.getDecoder(); Socket nowSocket = null; MultiChat multiChat = null; BufferedReader in =null; PrintWriter out = null; db db; // 构造函数 public ServerReadAndWrite(Socket s, MultiChat multiChat, db db) { this.multiChat = multiChat; // 获取服务器聊天界面 this.nowSocket = s; // 获取当前客户端 this.db = db; } public void run() { try { in = new BufferedReader(new InputStreamReader(nowSocket.getInputStream())); // 将收到的信息转发给所有在线用户(包括发送信息的用户) while (true) { String msgTrans = in.readLine(); String msg = decodeBase64(msgTrans); // 发送给所有客户端 for(Socket socket: Server.list) { out = new PrintWriter(socket.getOutputStream()); if(socket == nowSocket) { out.println(codeBase64("(你)" + msg)); } else { out.println(msgTrans); } out.flush(); // 清空out中的缓存 } // 将内容显示在服务端界面上 db.addInf(msg); multiChat.setTextArea(msg); } } catch (Exception e) { Server.list.remove(nowSocket); // 线程关闭,移除相应套接字 } } //Base64编码 private static String codeBase64(String msg){ //1、字符串转为字节数组 byte[] bytes = msg.getBytes(); //2、编码 byte[] encodeBytes = Base64.getEncoder().encode(bytes); //3、将编码后的字节数组转为字符串 String msgTrans = new String(encodeBytes); return msgTrans; } //解码base64方法 private static String decodeBase64(String msgTrans){ //1、使用base64类的getDecoder()方法获取一个Decoder实例 Base64.Decoder decoder = Base64.getDecoder(); //2、解码字符串 byte[] decodedBytes = decoder.decode(msgTrans); //3、将解码后的字符转换成字符串 String msg = new String(decodedBytes); return msg; } } ``` ```java public class db { private String sConnStr = "jdbc:sqlite:D:\\software\\sqlitedb\\jwh.db"; protected Connection conn = null; public db() { Connection con = null; try { Class.forName("org.sqlite.JDBC"); con = DriverManager.getConnection(sConnStr); System.out.println("连接成功!"); } catch (SQLException e) { System.err.println(e.getMessage()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } this.conn = con; } public void close() { try { if (conn != null) { conn.close(); } } catch (SQLException e) { System.err.println(e.getMessage()); } } public boolean addInf(String inf) { boolean flag = false; if (conn != null) { try { PreparedStatement pst = conn.prepareStatement("insert into server values (?)"); pst.setString(1, inf); flag = pst.executeUpdate() > 0; } catch (SQLException e) { throw new RuntimeException(e); } } return flag; } public static void main(String[] args) { db db=new db(); db.addInf("jw"); } } ``` ### 六、程序运行结果截图及性能分析 - 启动界面 wps6.jpg wps7.jpg - 登录界面 wps8.jpg wps9.jpg - 信息传输界面 wps10.jpg wps11.jpg wps12.jpg - 文件传输界面 wps13.jpg wps14.jpg wps15.jpg wps16.jpg - 上传代码 image-20240628124633978.png ### 七、项目训练的收获和体会 ​ 实训的任务是利用TCP协议进行网络程序设计。网络通信在当今数字化的时代已经成为生活和工作的重要组成部分。本次实训,我从TCP网络程序的编写,到Base64的编码、解码,再到字段数据和文件的传输,以及群聊和数据库的实现中,收获颇多。 ​ 我了解了网络通信的复杂性和挑战性,包括TCP的三次握手建立连接、数据传输、断开连接等过程,这些增强了我的网络通信理论基础,为我后续的编程实践提供了重要的指导。 ​ Base64编码的学习让我对二进制数据和文本数据之间的转换有了更清晰的认识,这种编码方式不仅保证了数据的完整性,还以高了数据传输的效率和安全性。 ​ 总之,通过本次实训,我不仅掌握了相关的理论知识和技术方法,还提高了自己的编程能力和解决问题的能力,这些收获将为我今后的学习和工作提供重要的支持和帮助。 ### 八、参考文献(含网络资源) ​ [1]李检辉.基于TCP的Java Socket网络连接过程要点分析[J].电脑知识与技术,2023,19(20):103-105.DOI:10.14004/j.cnki.ckt.2023.1017. ​ [2]缪志敏,刘莹,岳淑贞. TCP协议“三次握手”的误解和正确认识[C]//全国高等学校计算机教育研究会,中国计算机学会,教育部高等学校计算机类专业教学指导委员会.2023中国高校计算机教育大会论文集.[出版者不详],2023:5.DOI:10.26914/c.cnkihy.2023.109489. ​ [3]薛宾.嵌入式数据库SQLite在Java中的应用[J].天津职业院校联合学报,2008(04):123-126. ​ [4]姚峰.Java平台中Base64编码/解码算法的改进[J].计算机应用与软件,2008,25(12):164-165+176.