哈哈,准备期末考试去了,项目停了一段时间。现在又忘的差不多了。所以专门写一篇博客总结前期项目的知识点。
Client软件包
代码加总结:
这段代码实现了一个简单的客户端程序,用于与服务器建立连接、发送登录信息并接收服务器的响应。它展示了如何使用 Java 的 Socket 和对象流来进行网络通信和对象传输,以及如何处理相关的异常情况。
package Client;import common.Message;
import common.MessageType;import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;public class CheckUser {private Socket socket;public boolean login(Message message) {//登录try {//创建一个新的Socket连接到指定的IP地址和端口号socket = new Socket("127.0.0.1", 10086);//获取Socket输出流,并包装成ObjectOutputStream以便可以发送对象ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());//发送登录消息到服务器oos.writeObject(message);//获取Socket的输入流,并包装成ObjectInputStream以便可以接收对象ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());//反序列化强转为String类型,提示信息//从服务器接收对象,并尝试将其强制转换为Message类型//这里假设服务器返回的对象是Message类型,并且包含了登录的结果信息Message message1 = (Message) ois.readObject();//检查返回的Message对象的MassageType属性是否为登录成功//注意:这里应该使用equals进行比较枚举值if(message1.getMessageType().equals(MessageType.loginsuccefull)){return true;}else return false;} catch (IOException e) {// 如果在Socket通信过程中发生IO异常,捕获该异常并抛出一个运行时异常// 这样做通常是为了简化异常处理,但也可能导致调用者难以处理特定类型的IO异常throw new RuntimeException(e);// 注释:捕获了与Socket通信相关的IO异常,并重新包装为运行时异常抛出} catch (ClassNotFoundException e) {// 如果在尝试反序列化对象时找不到类的定义,则捕获此异常// 这通常意味着服务器发送的对象类型在客户端的类路径中不存在throw new RuntimeException(e);// 注释:捕获了反序列化对象时找不到类的定义的情况,并重新包装为运行时异常抛出}}public boolean SignIn(Message message) {//注册try {//创建一个新的Socket连接到指定的IP地址和端口号socket = new Socket("127.0.0.1", 10086);//获取Socket输出流,并包装成ObjectOutputStream以便可以发送对象ObjectOutputStream oos=new ObjectOutputStream(socket.getOutputStream());//发送注册消息到服务器oos.writeObject(message);//获取Socket的输入流,并包装成ObjectInputStream以便可以接收对象ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());//反序列化强转为String类型,提示信息//从服务器接收对象,并尝试将其强制转换为Message类型//这里假设服务器返回的对象是Message类型,并且包含了注册的结果信息Message message1 = (Message) ois.readObject();//检查返回的Message对象的MassageType属性是否为登录成功//注意:这里应该使用equals进行比较枚举值if(message1.getMessageType().equals(MessageType.RegistrationSuccessfully)){return true;}else return false;} catch (IOException e) {// 如果在Socket通信过程中发生IO异常,捕获该异常并抛出一个运行时异常// 这样做通常是为了简化异常处理,但也可能导致调用者难以处理特定类型的IO异常throw new RuntimeException(e);// 注释:捕获了与Socket通信相关的IO异常,并重新包装为运行时异常抛出} catch (ClassNotFoundException e) {// 如果在尝试反序列化对象时找不到类的定义,则捕获此异常// 这通常意味着服务器发送的对象类型在客户端的类路径中不存在throw new RuntimeException(e);// 注释:捕获了反序列化对象时找不到类的定义的情况,并重新包装为运行时异常抛出}}
}
详细疏解知识点:
看懂代码的朋友们就可以不用看这段总结了,这段总结会非常基础。
1.private 是什么意思?
private是访问修饰符的一种。
JAVA的权限修饰符_java的访问修饰符权限-CSDN博客
2.Socket是什么意思?
- 定义:Socket是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个Socket就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。
- 功能:Socket作为网络通信的编程接口,允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。它是实现网络通信的基础,无论是浏览网页、发送电子邮件,还是流式传输视频,这些操作背后都有Socket在工作。
- 工作原理:一个应用程序通过创建一个Socket,并指定相应的网络协议(如TCP/IP)、IP地址和端口号等参数,然后通过这个Socket进行通信。通信过程包括建立连接、发送数据、接收数据以及关闭连接等步骤。
3.通信协议与网络协议
-
通信协议:是指双方实体完成通信或服务所必须遵循的规则和约定。它主要用于定义发送方和接收方如何发送和接收数据,以及如何处理发生的错误。通信协议还负责定义传输的数据的格式,以及数据的结构和语义。这些协议常用于点对点通信,特别是在传统的电话通信领域被广泛应用。常见的通信协议包括HTTP、SMTP、FTP、SSH、Telnet、IMAP和POP3等。
通信协议分层思想是将网络通信过程划分为多个独立的层次,每个层次负责不同的功能,并通过接口与相邻层次进行交互。这种分层结构使得网络通信过程更加模块化,每一层只关注自己的任务,从而简化了网络设计和实现的复杂性。
分层模型示例
1. OSI七层参考模型
OSI(Open Systems Interconnection,开放式系统互联)模型是国际标准化组织(ISO)在20世纪80年代制定的一种通信协议分层模型,它将网络通信划分为七个层次,从上到下依次为:
- 应用层:直接为用户的应用程序提供服务,如文件传输、电子邮件等。
- 表示层:负责数据的编码、转换和压缩,确保数据在不同系统间正确传输。
- 会话层:负责建立、管理和终止会话,保证数据在会话中的正确传输。
- 传输层:提供端到端的可靠数据传输服务,如TCP协议。
- 网络层:负责数据的路由选择和转发,如IP协议。
- 数据链路层:负责在物理链路上无差错地传输数据帧。
- 物理层:定义物理传输介质和信号格式,如双绞线、光纤等。
2. TCP/IP四层模型
TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)模型是互联网中广泛使用的通信协议分层模型,它将网络通信划分为四个层次:
- 应用层:与OSI模型的应用层相似,负责用户应用程序之间的数据通信。
- 传输层:提供端到端的数据传输服务,包括TCP和UDP两种协议。
- 网络层:负责数据的路由选择和转发,主要协议为IP。
- 网络接口层(有时也称为链路层):负责在物理网络上传输数据帧,包含各种硬件协议如以太网等。
- 网络协议:是指互联网上各个计算机之间进行数据传输和交换所必须共同遵循的规范。它包含了大量的细节和约定,如IP地址、TCP/UDP协议、ARP协议等。网络协议的设计目标是帮助计算机实现可靠、高效和安全的网络通信。网络协议是网络上所有设备之间通信规则的集合,它规定了通信时信息必须采用的格式和这些格式的意义。
4.TCP/UDP协议
java.net
包中提供了两种常见的网络协议的支持:TCP和UDP
TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是计算机网络中两种主要的传输层协议,它们各自具有独特的特点和适用场景。以下是TCP和UDP之间的详细比较:
连接性
TCP:面向连接的协议。在数据传输之前,TCP需要通过三次握手过程建立连接,确保双方通信的同步性。UDP:无连接的协议。UDP发送方直接将数据包发送给接收方,无需建立连接。
可靠性
TCP:提供可靠的数据传输。使用确认和重传机制来确保数据的完整性,如果数据包在传输过程中丢失或损坏,TCP会重新发送。UDP:不提供可靠的数据传输。数据包可能丢失、重复或乱序到达,接收方不会发送确认信号,发送方也不会重传丢失的数据包。
顺序性
TCP:保证数据按序到达。每个TCP数据包都有一个序列号,接收方会按照序列号重新排序数据包。UDP:不保证数据顺序。数据包可能以任何顺序到达接收方。
流量控制和拥塞控制
TCP:提供流量控制和拥塞控制机制。通过滑动窗口协议和拥塞控制算法来调整数据发送速率,以适应网络的拥塞情况。UDP:不提供流量控制和拥塞控制。发送方以恒定的速率发送数据包,不考虑网络的拥塞情况。
传输速度
TCP:相对较慢。由于连接管理、错误检测和恢复等机制,TCP的传输速度受到一定影响。UDP:相对较快。由于无需建立连接和进行确认操作,UDP的传输速度通常比TCP更快。
协议开销
TCP:协议开销较大。TCP数据包包含更多的头部信息,用于连接管理、确认和重传等机制。UDP:协议开销较小。UDP数据包头部信息较少,只包含必要的源端口号、目的端口号、长度和校验和等信息。
适用场景
TCP:适用于对数据传输可靠性要求较高的场景,如文件传输、电子邮件、Web浏览、远程登录等。UDP:适用于对实时性要求较高、可以容忍一定数据丢失的场景,如实时音视频传输、在线视频会议、实时游戏等。
5.TCP协议使用
服务端:
import java.net.*;
import java.io.*; public class TCPServer { public static void main(String[] args) { try { // 创建服务器端的Socket对象,绑定监听端口 ServerSocket serverSocket = new ServerSocket(8888); System.out.println("服务器启动,等待连接..."); // 等待客户端连接,连接后返回一个Socket对象 Socket socket = serverSocket.accept(); System.out.println("客户端已连接!"); // 创建输入流,读取客户端发送的数据 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String info = bufferedReader.readLine(); // 读取一行数据 System.out.println("收到客户端消息:" + info); // 创建输出流,向客户端发送数据 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream, true); printWriter.println("服务器响应:收到消息!"); // 关闭资源 printWriter.close(); bufferedReader.close(); socket.close(); serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } }
}
客户端:
import java.net.*;
import java.io.*; public class TCPClient { public static void main(String[] args) { try { // 创建客户端的Socket对象,指定服务器地址和端口号 Socket socket = new Socket("localhost", 8888); // 创建输出流,向服务器发送数据 OutputStream outputStream = socket.getOutputStream(); PrintWriter printWriter = new PrintWriter(outputStream, true); printWriter.println("你好,服务器!"); // 创建输入流,读取服务器发送的数据 InputStream inputStream = socket.getInputStream(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); String response = bufferedReader.readLine(); // 读取一行数据 System.out.println("收到服务器响应:" + response); // 关闭资源 printWriter.close(); bufferedReader.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } }
}
1. 创建连接
- 客户端:使用
Socket
类的构造方法创建一个Socket
对象,该对象代表与服务器的一个连接。构造方法通常需要服务器的IP地址和端口号作为参数。例如:Socket socket = new Socket("serverIP", port);
- 服务器端:使用
ServerSocket
类的构造方法创建一个ServerSocket
对象,并绑定到一个指定的端口上。然后,调用accept()
方法等待客户端的连接请求。例如:ServerSocket serverSocket = new ServerSocket(port); Socket socket = serverSocket.accept();
2. 数据传输
- 发送数据:在客户端和服务器端,都可以通过
Socket
对象获取输出流(OutputStream
),然后将其包装成更方便的流类型(如PrintWriter
),用于发送数据。例如:OutputStream outputStream = socket.getOutputStream();PrintWriter printWriter = new PrintWriter(outputStream, true); printWriter.println("Hello, Server!");
- 接收数据:同样,在客户端和服务器端,都可以通过
Socket
对象获取输入流(InputStream
),然后将其包装成更方便的流类型(如BufferedReader
),用于接收数据。例如:InputStream inputStream = socket.getInputStream();BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));String line = bufferedReader.readLine();
3. 关闭连接
- 完成数据传输后,需要关闭连接以释放资源。这可以通过调用
Socket
对象的close()
方法来实现。例如:socket.close();
如果服务器端还绑定了ServerSocket
对象,则还需要关闭该对象。例如:serverSocket.close();
4. 异常处理
- 在进行网络编程时,经常会遇到各种网络异常(如
IOException
)。因此,通常需要将网络操作放在try-catch
块中,以便捕获并处理这些异常。
注意事项
- 在进行TCP网络通信时,需要确保客户端和服务器端的端口号一致。
- TCP是面向连接的协议,因此在发送和接收数据之前,必须先建立连接。
- TCP协议保证了数据的可靠性和顺序性,因此在网络状况不佳的情况下,可能会出现数据传输延迟或重传的情况。
- 在实际开发中,还需要考虑多线程或多进程并发处理多个客户端连接的情况,以及异常处理和资源释放等问题。
6.序列化和反序列化
序列化:是将数据结构或对象状态转换为可以存储或传输的格式的过程。这个格式通常是平台无关的,以便在不同的上下文中重新创建对象的精确副本。序列化通常用于以下目的:
- 持久化:将对象的状态保存到磁盘上,以便在程序停止运行后能够重新加载。
- 网络传输:将对象转换为可以在网络上发送的格式。
在序列化过程中,对象的公共和私有字段(以及某些类型的元数据)都会被转换为字节流,以便存储或传输。
反序列化:是序列化的逆过程,它将序列化的数据(通常是字节流)转换回对象或数据结构。这个过程通常是在需要重新使用对象或数据结构时进行的,例如在从磁盘加载对象或在网络上接收对象后。
序列化和反序列化的重要性
- 在不同编程语言或平台之间共享数据。
- 将对象状态保存到磁盘,以便在程序重启后恢复。
- 通过网络发送和接收对象。
然而,需要注意的是,在序列化对象时要特别小心,因为某些信息(如方法、对象的内部状态等)可能无法被序列化或反序列化,这可能会导致数据丢失或程序行为异常。
7.ObjectOutputStream
ObjectOutputStream是Java中用于序列化对象的类,它属于java.io包。
-
定义:ObjectOutputStream类将Java对象的原始数据类型和图形写入OutputStream,实现对象的持久化存储。
-
功能:它允许程序员将Java对象转换为字节序列,以便可以将这些字节写入到输出流中,例如文件或网络连接。
-
public ObjectOutputStream(OutputStream out)
:创建一个写入指定OutputStream的ObjectOutputStream类对象。 -
void writeObject(Object obj)
:将指定的对象写入到ObjectOutputStream中。
序列化条件
要序列化的对象必须实现
java.io.Serializable
接口。如果没有实现这个接口,将会抛出NotSerializableException
异常。对象的所有属性(除了被
transient
修饰的)都必须是可序列化的。如果有一个属性需要参与序列化,但该属性是瞬态的,那么该属性必须用transient
关键字进行标记。
使用步骤
创建ObjectOutputStream对象:在构造方法中传递一个字节输出流对象(如FileOutputStream)。
写入对象:使用
writeObject
方法将对象写入到文件中或通过网络发送。释放资源:在完成序列化后,记得关闭ObjectOutputStream和底层的输出流。
注意事项
- 序列化过程中不会序列化静态变量和标记为
transient
的变量。- 如果一个对象的类在序列化后发生了变化(例如,添加或删除了字段),那么在反序列化时可能会抛出
InvalidClassException
。为了避免这种情况,可以为类指定一个serialVersionUID
。- 序列化一个对象时,对象的整个图(即对象引用的所有其他对象)也会被序列化,除非这些对象被标记为
transient
或它们的类没有实现Serializable
接口。
8.ObjectInputStream
ObjectInputStream是Java中用于反序列化对象的类,它属于java.io包。ObjectInputStream用于从输入流中读取已经序列化的对象,并将这些对象恢复为原来的状态。
public ObjectInputStream(InputStream in)
:创建一个从指定InputStream读取的ObjectInputStream对象。
Object readObject()
:从ObjectInputStream中读取一个对象。此方法将阻塞,直到对象可用。如果流在读取对象时到达文件末尾,则返回EOFException。如果流在读取对象时被关闭,则抛出IOException。
使用条件
- 被反序列化的对象必须实现了
java.io.Serializable
接口。- 如果对象的类在序列化后发生了变化(例如,属性被添加、删除或修改),可能会导致反序列化失败或抛出异常。为了避免这种情况,建议为类指定一个
serialVersionUID
。
使用步骤
- 创建ObjectInputStream对象:在构造方法中传递一个字节输入流对象(如FileInputStream)。
- 读取对象:使用
readObject
方法从输入流中读取对象。注意,由于此方法返回的是Object类型,你可能需要进行类型转换以获取具体的类型。- 释放资源:在完成反序列化后,记得关闭ObjectInputStream和底层的输入流。
注意事项
- 在使用readObject方法时,需要处理可能抛出的ClassNotFoundException异常,因为反序列化过程中需要加载对象的类定义。
- 如果对象的类在序列化后进行了不兼容的更改(如删除了一个必要的字段),那么反序列化可能会失败。在这种情况下,应该更新serialVersionUID以表示类的更改,并确保在反序列化时能够正确处理这些更改。
- 由于readObject方法可能会阻塞,因此应该在一个单独的线程中执行它,以避免阻塞主线程。
common软件包
代码加总结:
package common;import java.io.Serializable;public class Message implements Serializable {private static final long serialVersionUID = 1L;private String MessageType;public String getMessageType() {return MessageType;}public void setMessageType(String messageType) {MessageType = messageType;}
}
package common;import java.io.Serializable;public interface MessageType {String buy = "1";String login = "2";String loginsuccefull = "3";String longfail = "4";String SignIn = "5";//注册String RegistrationSuccessfully = "6";//注册成功String RegistrationFailed = "7";//注册失败
}
详细疏解知识点:
1.封装(get/set方法)
封装是面向对象编程(OOP)中的一个核心概念,它涉及到将数据(属性)和行为(方法)捆绑在一起形成一个紧密的单元,即“类”。
- 封装,即隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。
- 将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。
封装的作用
- 保护数据的完整性:通过封装,可以限制对类内部数据的直接访问,只允许通过类提供的公共方法进行访问和修改。这可以防止外部代码在不适当的时候更改类的内部状态,从而预防意外或恶意的篡改。
- 减少代码冗余:封装通过提供通用方法来减少代码冗余。设计良好的类可以被多处代码重用,因此不必在每个需要使用这些行为的地方编写相同的代码。这显著降低了重复的逻辑,并且当需要变更时,只需在一个地方修改。
- 提高软件的可维护性:封装提高了软件的可维护性,因为修改内部实现细节不会影响到使用它的外部代码。即使在内部逻辑发生较大变化的情况下,只要公共接口保持不变,代码的其余部分仍然可以正常运作。
- 隐藏实现细节:封装实现了信息隐藏,这意味着类的用户无需了解其内部的复杂性,只要知道如何通过公开的方法与之交互即可。这不仅保护了数据,还减少了外部代码对内部修改的依赖性,从而提高了代码的灵活性和可维护性。
- 增强安全性:封装增加了系统的整体安全性。通过隐藏类的内部状态,限制了外部对这些状态的直接操作,从而避免了未经授权的访问和潜在的安全风险。
封装的实现
- 在编程中,封装通常通过定义类、使用访问修饰符(如
public
、private
、protected
)来限制对属性和方法的访问级别,以及提供公共的get
和set
方法来实现。get
方法用于读取类的私有属性的值,而set
方法用于修改这些属性的值。
未完待续……