实现分析、
1.开启服务端
客户端选择‘登录以后’后,提示输入用户名和密码,验证成功则进入好友列表界面
2.用户聊天
双击好友,进入好友聊天界面。在信息框编辑信息 点击发送
当客户端向服务端发送数据时 根据指令的不同来处理不同的业务。
如果是登录,服务端向数据库查询用户信息。然后把处理后的信息发送给服务端。
如果是聊天业务,首先判断当前好友是否在线
如果好友不在线,处理后的信息发送给发送者,如果好友在线 处理后的信息发送给接收者(朋友)
服务端界面:
简单介绍一下 服务端的运行流程
当点击开启服务后 新建一个线程开启服务器循环监听客户端的连接
try { ServerSocket ss = new ServerSocket(4096); Socket socket = null; // 循环监听客户端的连接 每连接一个客户端 就为其实例化一个线程 // 在该线程 I/O阻塞监听客户端发送的信息 不然你只能收到一次信息~ while (true) { socket = ss.accept(); ServiceThread thread = new ServiceThread(socket); thread.start(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
至于为何要新建线程开启服务,归结于while循环里面的ss.accept(); 如果一直没接收到客户端的连接,会导致线程阻塞。
如果你使用的还是main线程,那么关闭服务会无法点击。
而为了实现多客户端的连接 所以在上面我们为每一个客户端都开启一个线程。
线程的run方法为
@Override public void run() { ObjectInputStream ois = null; ObjectOutputStream oos = null; // 时刻监听 客户端发送来的数据 while (socket != null) { try { ois = new ObjectInputStream(socket.getInputStream()); CommandTranser msg = (CommandTranser) ois.readObject(); // 处理客户端发送来的信息 msg = execute(msg); if ("message".equals(msg.getCmd())) { /* * 如果 msg.ifFlag即 服务器处理成功 可以向朋友发送信息 如果服务器处理信息失败 信息发送给发送者本人 */ if (msg.isFlag()) { oos = new ObjectOutputStream(SocketList.getSocket( msg.getReceiver()).getOutputStream()); } else { oos = new ObjectOutputStream(socket.getOutputStream()); } } // 如果是登录请求 发送给发送者本人 if ("login".equals(msg.getCmd())) { oos = new ObjectOutputStream(socket.getOutputStream()); } oos.writeObject(msg); } catch (IOException e) { socket = null; } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }也是一个while循环 I/O阻塞接收客户端的信息。因为你不能只接收客户端一次的信息吧
如果你发现你只能接受一次信息 问题就出在这里了 因为我也在这里错了~~~~~~
在这里出现了CommandTranser类,execute(CommandTranser msg)方法和SocketList类里面的静态方法getSocket
SocketList类很简单 主要是保存已经和服务器成功连接的客户端
package cn.edu.xynu.util; import java.net.Socket; import java.util.HashMap; import cn.edu.xynu.entity.SocketThread; /** * @author scx * 所有已经成功登录服务器的socket和昵称 */ public class SocketList { private static HashMap<String, Socket> map=new HashMap<String, Socket>(); //将SocketThread入集合 public static void addSocket(SocketThread socketThread){ map.put(socketThread.getName(), socketThread.getSocket()); } //通过昵称返回socket public static Socket getSocket(String name){ return map.get(name); } }SocketThread实体类
所有成功连接客户端的socket实体类 包括一个socket (客户端)和昵称
package cn.edu.xynu.entity; import java.net.Socket; /** * @author scx 所有成功连接客户端的socket实体类 包括一个socket 一个昵称 */ public class SocketThread { private Socket socket; private String name; public SocketThread() { super(); // TODO Auto-generated constructor stub } public SocketThread(Socket socket, String name) { super(); this.socket = socket; this.name = name; } public Socket getSocket() { return socket; } public void setSocket(Socket socket) { this.socket = socket; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
CommandTranser这个类主要负责服务端和客户端的通信数据。
具体代码及注释如下:
package cn.edu.xynu.util; import java.io.Serializable; /** * @author scx * 客户端与服务器交互的数据 */ public class CommandTranser implements Serializable{ private static final long serialVersionUID = 1L; private String sender=null;//发送者 private String receiver=null;//接受者 private Object data=null;//传递的数据 private boolean flag=false;//指令的处理结果 private String cmd=null;//服务端要做的指令 private String result=null;//处理结果 public String getSender() { return sender; } public void setSender(String sender) { this.sender = sender; } public String getReceiver() { return receiver; } public void setReceiver(String receiver) { this.receiver = receiver; } public Object getData() { return data; } public void setData(Object data) { this.data = data; } public boolean isFlag() { return flag; } public void setFlag(boolean flag) { this.flag = flag; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } public String getCmd() { return cmd; } public void setCmd(String cmd) { this.cmd = cmd; } }而execute(CommandTranser msg)方法主要处理客户端向服务端发送的数据
由于我们只实现了两个功能 即登录 和聊天
所以execute方法主要代码如下
// 如果是登录请求 if ("login".equals(msg.getCmd())) { UserService userService = new UserService(); User user = (User) msg.getData(); msg.setFlag(userService.checkUser(user)); /* * 如果登陆成功,将该客户端加入已经连接成功的map集合里面 并且开启此用户的接受线程 */ if (msg.isFlag()) { // 将该线程加入连接成功的map集合 SocketThread socketThread = new SocketThread(); socketThread.setName(msg.getSender()); socketThread.setSocket(socket); SocketList.addSocket(socketThread); msg.setResult("登陆成功"); } else { msg.setResult("登录失败"); } } // 如果是发送消息的指令 判断当前用户是否在线 if ("message".equals(msg.getCmd())) { // 如果要发送的用户在线 发送信息 if (SocketList.getSocket(msg.getReceiver()) != null) { msg.setFlag(true); } else { msg.setFlag(false); msg.setResult("当前用户不在线"); } } return msg; }
当用户为login请求时,通过userservice查询数据库中是否有此用户,并返回结果
package cn.edu.xynu.service; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import cn.edu.xynu.entity.User; import cn.edu.xynu.util.DBHelper; /** * @author scx 客户的业务处理 只查询用户是否在数据库中存在 */ public class UserService { public boolean checkUser(User user) { PreparedStatement stmt = null; Connection conn = null; ResultSet rs = null; conn = DBHelper.getConnection(); String sql = "select * from user where username=? and password =?"; try { stmt = conn.prepareStatement(sql); stmt.setString(1, user.getUsername()); stmt.setString(2, user.getPassword()); rs = stmt.executeQuery(); if (rs.next()) { return true; } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { try { if (rs != null) rs.close(); if (stmt != null) stmt.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return false; } }User类用户的实体类,只包括帐号 密码信息
package cn.edu.xynu.entity; import java.io.Serializable; /** * @author scx * 用户信息的实体类 */ public class User implements Serializable{ private static final long serialVersionUID = 1L; private String username; private String password; public User() { super(); } public User(String username, String password) { super(); this.username = username; this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
而DBhelpher为连接数据库工具类
package cn.edu.xynu.util; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; /** * @author scx * 数据库 */ public class DBHelper { private static final String driver="com.mysql.jdbc.Driver"; private static final String url="jdbc:mysql://localhost:3306/myqquser?useUnicode=true&charcterEncoding=UTF-8"; private static final String username="root"; private static final String password=""; private static Connection con=null; //静态块代码负责加载驱动 static { try { Class.forName(driver); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static Connection getConnection(){ if(con==null){ try { con=DriverManager.getConnection(url, username, password); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return con; } }下一篇
JAVA进阶案例 TCP编程之网络聊天工具(客户端)
作者:su20145104009 发表于2016/10/22 21:04:45 原文链接
阅读:115 评论:0 查看评论