Java团队课程设计-socket聊天室(个人总结)
一、团队课程设计博客链接
https://www.cnblogs.com/haijie-wrangler/p/12169314.html
二、本人负责模块或任务说明
任务1 | 服务端对socket线程的接受以及对客户端的数据转发操作 |
---|---|
任务2 | 数据库的查找,添加,删除,更改操作 |
三、自己代码的提交记录
注:只有部分提交记录,详细提交记录请访问团队博客
四、自己负责模块或任务详细说明
1、服务端需要定义定义一个服务端控制对象来处理用户的登录和注册,并执行创建用户线程的任务以完成消息的存取和转发
首先,用户登录界面需要发送用户注册信息或者用户登录信息给服务端,服务端需要进行判断,如果是接收的是用户登录信息,则进行数据库信息匹配,返回是否成功登录(封装成Object发送),返回离线消息,将以发送的信息置为历史消息,并把该连接线程加入到一个HashMap中(方便转发时查找接收方线程),并创建用户线程进行传输在线聊天消息服务。若接收的是用户注册信息,则在数据库的用户表中插入新注册的用户信息(包括账号和密码),返回注册成功(封装成Object)。
main方法中循环监听是否有用户连接,并判断新连接的用户是需要注册还是登录
public static void main(String[] args) throws Exception {
ServiceControllerImpl serviceController = new ServiceControllerImpl();
ServerSocket serverSocket = null;
Socket socket;
try {
serverSocket = new ServerSocket(20);
} catch (IOException e) {
e.printStackTrace();
}
Object o = null;
while (true) {
try {
//assert关键字,返回一个boolean,如果为true,则程序继续进行,否则,抛出异常阻塞程序
assert serverSocket != null;
socket = serverSocket.accept();
if (socket != null) {
inputStream = socket.getInputStream();
objectInputStream = new ObjectInputStream(inputStream);
o = objectInputStream.readObject();
//获取新连接的socket所需的输出流
outputStream = socket.getOutputStream();
objectOutputStream = new ObjectOutputStream(outputStream);
if("class user.User".equals(o.getClass().toString())){
User user = (User) o;
if (serviceController.checkIfTheAccountMatches(user)) {
objectOutputStream.writeObject("Login successful!");
objectOutputStream.flush();
serviceController.sendPersonName();
serviceController.sendOfflineMessage( user);
serviceController.creatThread(user,objectInputStream,objectOutputStream);
} else {
objectOutputStream.writeObject("Login error!");
objectOutputStream.flush();
}
}else{
UserInformation userInformation=(UserInformation)o;
try {
serviceController.userRegister(userInformation);
objectOutputStream.writeObject("user created successfully!");
objectOutputStream.flush();
}catch (SQLException e){
objectOutputStream.writeObject("Find error in user regist!");
objectOutputStream.flush();
}
}
}
}catch (SQLException e){
System.out.println("数据库异常,不能进行对"+((User) o).getUserName()+"查找匹配");
} catch (IOException | ClassNotFoundException e) {
System.out.println("线程意外退出!");
}
}
}
2.服务端需要创建服务线程以对客户端发送的要求进行回应
该线程需要循环监听客户端的请求(封装成Object发送),对可请求的数据类型进行判断
若客户端请求转发在线消息,则:
public boolean sendMessage(OnlineMessage onlineMessage) throws IOException {
if(threadMap.containsKey(onlineMessage.getReceivingEnd())){
System.out.println(onlineMessage.getReceivingEnd());
//发送给对方信息 threadMap.get(onlineMessage.getReceivingEnd()).forwardMessage(onlineMessage);
return true;
}else{
return false;
}
}
//转发功能
public void forwardMessage(OnlineMessage onlineMessage) throws IOException {
oos.writeObject(new OnlineMessage(null,null,null,null));
oos.flush();
oos.writeObject(onlineMessage);
oos.flush();
}
在已连接的线程中查找接收方对应的线程,进行信息的转发。同时,将相应的信息插入数据库,返回一个boolean变量表示转发是否成功。
若客户端请求转发文件,则:
public boolean sendDocument(UserDocument userDocument) throws IOException {
if(threadMap.containsKey(userDocument.getReceiver()))
{
threadMap.get(userDocument.getReceiver()).forwardDocument(userDocument);
return true;
}
return false;
}
private void forwardDocument(UserDocument userDocument) throws IOException {
oos.writeObject(new UserDocument(null,null,null));
oos.flush();
oos.writeObject(userDocument);
oos.flush();
}
在已连接的线程中查找接收方的线程,进行文件的转发。返回一个boolean变量表示转发是否成功(这里的转发文件做不到离线转发,需要发送方和接收方同时在线)
3、数据库相应操作
首先是建表:
User表:包含所有已注册过的用户的账号和密码信息,若用户注册成功则将改用户账号信息插入到数据库中
每个用户的个人消息表,包含各个其他用户与该用户的聊天信息记录
包含两个标记字段,放置一个Flag字段,Flag的值为1表示是对方发来的信息,值为2表示是该用户发给对方的信息。还有一个Type字段,其值为1表示该信息还没有被改用户读取,为离线消息,值为2表示该信息已被查看,为历史消息
其次,使用jdbc对数据库的操作:
如果对方在线,转发为在线消息,在自己的消息表中将该信息设置为自己为发送方(Flag为2)历史消息(Type为2)。在对方的消息表中将该条信息设置为接收方(Flag为1),并置为历史消息(Type为2)
if(flag==1) {
//自己的表
//Flag=1是别人给我的消息 Type=1是离线消息
preparedStatement=connection.prepareStatement(SQL);
preparedStatement.setString(1,onlineMessage.getReceivingEnd());
System.out.println(onlineMessage.getReceivingEnd());
System.out.println(onlineMessage.getSender());
preparedStatement.setString(2,"2");
preparedStatement.setString(3,onlineMessage.getMessage());
preparedStatement.setString(4,"2");
preparedStatement.setString(5,onlineMessage.getTime());
preparedStatement.executeUpdate();
//对方的表
stringBuffer = new StringBuffer("Insert into ");
stringBuffer.append(onlineMessage.getReceivingEnd());
stringBuffer.append(" values(?,?,?,?,?)");
SQL = new String(stringBuffer);
preparedStatement=connection.prepareStatement(SQL);
preparedStatement.setString(1,onlineMessage.getSender());
preparedStatement.setString(2,"1");
preparedStatement.setString(3,onlineMessage.getMessage());
preparedStatement.setString(4,"2");
preparedStatement.setString(5,onlineMessage.getTime());
preparedStatement.executeUpdate();
}
如果对方不在线,则转发为离线消息,在自己的消息表中将该信息设置为自己为发送方(Flag为2)历史消息(Type为2)。在对方的消息表中将该条信息设置为接收方(Flag为1),并置为历史消息(Type为1)
else if(flag==2) {
//自己的表
//Flag=1是别人给我的消息 Type=1是离线消息
preparedStatement=connection.prepareStatement(SQL);
preparedStatement.setString(1,onlineMessage.getReceivingEnd());
preparedStatement.setString(2,"2");
preparedStatement.setString(3,onlineMessage.getMessage());
preparedStatement.setString(4,"2");
preparedStatement.setString(5,onlineMessage.getTime());
preparedStatement.executeUpdate();
//对方的表
stringBuffer = new StringBuffer("Insert into ");
stringBuffer.append(onlineMessage.getReceivingEnd());
stringBuffer.append(" values(?,?,?,?,?)");
SQL = new String(stringBuffer);
preparedStatement=connection.prepareStatement(SQL);
preparedStatement.setString(1,onlineMessage.getSender());
preparedStatement.setString(2,"1");
preparedStatement.setString(3,onlineMessage.getMessage());
preparedStatement.setString(4,"1");
preparedStatement.setString(5,onlineMessage.getTime());
preparedStatement.executeUpdate();
}
五、课程设计感想
在本次课程设计中,项目设计的阶段困难重重,socket使用阻塞式的传输方式,一开始,消息传输总有接收方的获取操作和发送方的传送操作对接不上的问题,最后通过使用线程的等待来确保发送和接收方的对接
本次团队合作让我对JAVA面向对象的设计有了更加深刻的理解,项目实现了类似QQ的聊天和传送文件功能,虽然在实现过程出现了很多问题,通过不断的调试最终都解决了