实现分析、
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编程之网络聊天工具(客户端)