服务器端:
package chatSystem; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.HashMap; import java.util.Map; /** * Service : 服务器端 * * @author xuejupo [email protected] create in 2015-12-14 下午3:21:13 */ public class Service { private InetSocketAddress socketAddress = null; // 服务监听地址 private DatagramSocket datagramSocket = null; // 连接对象 // 所有用户的连接端口 private static Map<Integer, Integer> chatUsers = new HashMap<Integer, Integer>(); // 服务器端的端口 private static int port = 80; public void init() { socketAddress = new InetSocketAddress("localhost", port); System.out.println("--------服务器端已启动---------"); try { datagramSocket = new DatagramSocket(socketAddress); } catch (SocketException e) { e.printStackTrace(); } byte[] buffer = new byte[1024]; // 缓冲区 while (true) { DatagramPacket packet = new DatagramPacket(buffer, buffer.length); receive(packet); this.manage(packet); } } public static void main(String[] s) { Service ss = new Service(); ss.init(); } /** * manage: 管理方法 规定获取的byte数组各位的意义: 第0位是状态位,除用户登录状态,第1位和第2位表示用户id * * @param packet * void 返回类型 */ private void manage(DatagramPacket packet) { byte[] info = packet.getData(); // 状态为1表示登录 // 状态为1时,1到18位表示用户的username(18位),19到40位表示用户的密码(22位) if (info[0] == 49) { int logStatus = this.log(info); if (logStatus != -1) { this.loginSuccess(packet, logStatus); } else { this.loginFalse(packet); } } else if (info[0] == 48) { // 状态为0表示退出 // 将是否在线状态置为false Storage.onLineUsers.set(getUserId(info), false); System.out.println("用户"+Storage.ID_NAME.get(getUserId(info))+"退出!"); // 从群聊天里去掉 chatUsers.remove(this.getUserId(info)); return; } else if (info[0] == 50) { // 状态为2表示发送群消息 String userName = Storage.ID_NAME.get(this.getUserId(info)); String msg = this.getInfo(info, 3).trim(); System.out.println(userName+"对大家说:"+msg); this.sendAll(packet, userName, msg); } else if (info[0] == 51) { // 状态为3表示发送个人消息 // 当状态为3时,第4位第5为表示目标用户id this.send(this.potPacket(info, packet)); } } /** * potPacket: 发送私人消息时,封装发送数据包 * * @return DatagramPacket 返回类型 */ private DatagramPacket potPacket(byte[] info, DatagramPacket packet) { String userName = Storage.ID_NAME.get(this.getUserId(info)); int toUserId = this.getToUserId(info); String msg = this.getInfo(info, 5); byte[] messageByte = this.getBytes(userName + "对你说:" + msg); packet.setData(messageByte); packet.setLength(messageByte.length); try { packet = new DatagramPacket(messageByte, messageByte.length, InetAddress.getByName("localhost"), chatUsers.get(toUserId)); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(userName+"对"+Storage.ID_NAME.get(toUserId)+"说:"+msg); return packet; } /** * getInfo: 获取byte数组中的string消息offset 起始位置,当群发消息时,起始位置应该是3,当发送私人消息时,起始位置应该是5 * * @return String 返回类型 */ private String getInfo(byte[] info, int offset) { String s = new String(info); byte[] infoB = new byte[info.length - offset]; System.arraycopy(info, offset, infoB, 0, info.length - offset); return new String(infoB).trim(); } /** * log: 登录方法 * * @param info * 传过来的包所包含的信息 * @return boolean 返回类型 */ private int log(byte[] info) { byte[] nameB = new byte[18]; byte[] passwdB = new byte[22]; System.arraycopy(info, 1, nameB, 0, 18); System.arraycopy(info, 19, passwdB, 0, 22); return LoginModule.login(new String(nameB).trim(), new String(passwdB).trim()); } /** * loginSuccess: 登录成功后的处理方法 * * @param packet * void 返回类型 */ private void loginSuccess(DatagramPacket packet, int userid) { byte[] message = getBytes("登录成功"); packet.setData(message); packet.setLength(message.length); this.send(packet); // 将登录成功的客户端端口加入到群聊天端口 chatUsers.put(userid, packet.getPort()); System.out.println("用户"+Storage.ID_NAME.get(userid)+"登录成功"); } /** * loginFalse: 登录失败后的处理方法 * * @param packet * void 返回类型 */ private void loginFalse(DatagramPacket packet) { byte[] message = getBytes("登录失败"); packet.setData(message); packet.setLength(message.length); this.send(packet); System.out.println("登录失败"); } /** * getBytes: 获取字符串的byte数组表示 * * @param msg * void 返回类型 */ private byte[] getBytes(String msg) { return msg.getBytes(); } /** * getUserId: 获取用户id * * @param b * @return int 返回类型 */ private int getUserId(byte[] b) { byte[] id = new byte[2]; id[0] = b[1]; id[1] = b[2]; String idStr = new String(id).trim(); return Integer.parseInt(idStr); } /** * getUserId: 发送私人消息时,获取目的用户id * * @param b * @return int 返回类型 */ private int getToUserId(byte[] b) { byte[] id = new byte[2]; id[0] = b[3]; id[1] = b[4]; String idStr = new String(id).trim(); return Integer.parseInt(idStr); } /** * receive: 接收数据包 * * @param packet * @return * @throws Exception * DatagramPacket 返回类型 */ public DatagramPacket receive(DatagramPacket packet) { try { datagramSocket.receive(packet); } catch (Exception e) { System.out.println(e.getLocalizedMessage()); } return packet; } /** * 将信息发送给客户端 response: * * @param packet * void 返回类型 */ public void send(DatagramPacket packet) { try { datagramSocket.send(packet); } catch (Exception e) { e.printStackTrace(); } } /** * sendAll: 将信息发给所有的在线客户端 * * @param packet * void 返回类型 */ private void sendAll(DatagramPacket packet, String username, String msg) { byte[] info = (username + "说:" + msg).getBytes(); for (int iaddr : chatUsers.values()) { try { packet = new DatagramPacket(info, info.length, InetAddress.getByName("localhost"), iaddr); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.send(packet); } } }
客户端:
package chatSystem; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Scanner; /** * Client : * * @author xuejupo [email protected] create in 2015-12-14 下午3:47:37 */ public class Client extends Thread { private static final int PORT = 80; private DatagramSocket dataSocket; private DatagramPacket dataPacket; private String sendStr; private int clinePort; private String name; private String id; private String toId; public Client(int port,String name,String id,String toId){ this.clinePort = port; this.name = name; this.id = id; this.toId = toId; } public void run() { new receiveThread().start(); this.init(); } public void init() { try { // 指定端口号,避免与其他应用程序发生冲突 dataSocket = new DatagramSocket(clinePort); System.out.println("用户登录----"); this.log(this.name, "123456"); Thread.sleep(1000); System.out.println("发送群消息----"); Thread.sleep(1000); this.sendAll(); System.out.println("发送私人消息"); Thread.sleep(1000); this.sendPrient(); } catch (SocketException se) { se.printStackTrace(); } catch (IOException ie) { ie.printStackTrace(); } catch(Exception e){ e.printStackTrace(); } } /** * log: 登录 * * @param username * @param pwd * void 返回类型 */ public void log(String username, String pwd) { byte[] name = this.getLenthBytes(username, 18); byte[] passwd = this.getLenthBytes(pwd, 22); byte[] message = new byte[44]; message[0] = 49; System.arraycopy(name, 0, message, 1, 18); System.arraycopy(passwd, 0, message, 19, 22); this.send(message); } /** * getLenthBytes: 获得定长的byte数组 * * @return byte[] 返回类型 */ private byte[] getLenthBytes(String msg, int length) { byte[] result = msg.getBytes(); byte[] returnByte = new byte[length]; if (result.length == length) { return result; } for (int i = 0; i < length; i++) { if (i < result.length) { returnByte[i] = result[i]; } else { returnByte[i] = ' '; } } return returnByte; } /** * sendDate: 发送数据包 * * @param packet * void 返回类型 */ public void sendDate(DatagramPacket packet) { try { dataSocket.send(packet); } catch (Exception e) { e.printStackTrace(); } } /** * sendDate: 发送群消息 * * @param packet * void 返回类型 */ public void sendAll() { String msg = "我是"+this.name; byte[] msgByte = new byte[msg.getBytes().length + 3]; msgByte[0] = 50; System.arraycopy(this.id.getBytes(), 0, msgByte, 1, 2); System.arraycopy(msgByte, 0, msgByte, 3, msg.getBytes().length); try { dataPacket = new DatagramPacket(msgByte, msgByte.length, InetAddress.getByName("localhost"), PORT); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.sendDate(dataPacket); } /** * sendDate: 发送私人消息 * * @param packet * void 返回类型 */ public void sendPrient() { String msg = "我是"+this.name; byte[] msgByte = new byte[msg.getBytes().length + 5]; msgByte[0] = 50; System.arraycopy(this.id.getBytes(), 0, msgByte, 1, 2); System.arraycopy(this.toId.getBytes(), 0, msgByte, 3, 2); System.arraycopy(msgByte, 0, msgByte, 5, msg.getBytes().length); try { dataPacket = new DatagramPacket(msgByte, msgByte.length, InetAddress.getByName("localhost"), PORT); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.sendDate(dataPacket); } /** * send: 发送数据包 * * @param packet * void 返回类型 */ public void send(byte[] msg) { try { dataPacket = new DatagramPacket(msg, msg.length, InetAddress.getByName("localhost"), PORT); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.sendDate(dataPacket); } /** * sendDate: 接收数据包 * * @param packet * void 返回类型 */ public void receive(DatagramPacket packet) { try { dataSocket.receive(packet); } catch (Exception e) { e.printStackTrace(); } } // 接收服务器端数据的线程 class receiveThread extends Thread { public void run() { byte[] sendDataByte = new byte[10000]; try { dataPacket = new DatagramPacket(sendDataByte, sendDataByte.length, InetAddress.getByName("localhost"), PORT); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } while (true) { receive(dataPacket); System.out.println(new String(dataPacket.getData()).trim()); } } } }
存储端:
package chatSystem; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.BitSet; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import jxl.read.biff.BiffException; /** * Storage : * * @author xuejupo [email protected] create in 2015-12-14 上午10:59:01 */ public class Storage { private static Storage single = new Storage(); // 构造函数设为私有 private Storage() { } // 单例模式 public static Storage getSingle() { return single; } // 存储所有的用户信息(用户名-key,密码:value) public static Map<String, User> allUser = new ConcurrentHashMap<String, User>(); // 存储所有的用户信息(用户名-key,密码:value) public static Map<Integer, String> ID_NAME = new ConcurrentHashMap<Integer, String>(); // 位图,存储所有的用户在线状态(二位数用户规模) public static BitSet onLineUsers = new BitSet(100); // 静态块,初始化静态信息 static { File f = new File("D:\\gongjiao\\用户信息.xls"); if (!f.exists()) { System.out.println("ERROR!!!"); } try { Workbook book = Workbook.getWorkbook(f);// Sheet sheet = book.getSheet(0); // 获得第一个工作表对象 for (int i = 0; i < sheet.getRows(); i++) { User user = new User(); for (int j = 0; j < sheet.getColumns(); j++) { Cell cell = sheet.getCell(j, i); // 获得单元格 String info = cell.getContents(); if (j == 0) { user.setUserId(Integer.parseInt(info)); } else if (j == 1) { user.setUserName(info); } else if (j == 2) { user.setUserName(info); } else if (j == 3) { user.setPasswd(info); } else if (j == 4) { user.setAge(Integer.parseInt(info)); } else if (j == 5) { user.setSex(Integer.parseInt(info)); } else if (j == 6) { user.setNoFriendChat("1".equals(info)); } else if (j == 7) { List<String> userids = new ArrayList<String>(); for (String userid : info.split(",")) { userids.add(userid); } user.setFriends(userids); } else if (j == 8) { List<String> userids = new ArrayList<String>(); if (info != null && !"".equals(info.trim())) { for (String userid : info.split(",")) { userids.add(userid); } } user.setBlackList(userids); } else if (j == 9) { user.setDetail(info); } else if (j == 10) { user.setBlackNum(Integer.parseInt(info)); } } ID_NAME.put(user.getUserId(), user.getUserName()); allUser.put(user.getUserName(), user); user = new User(); } } catch (BiffException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
user类:
package chatSystem; import java.util.List; /** * User : 用户bean * * @author xuejupo [email protected] create in 2015-12-14 下午1:28:31 */ public class User { // 用户id private int userId; // 用户名 private String userName; // 用户的真实姓名 private String tureName; // 用户密码 private String passwd; // 用户年龄 private int age; // 用户的性别,1为男,2为女 private int sex; // 用户是否接受非好友私人聊天 private boolean noFriendChat; // 用户好友的id列表 private List<String> friends; // 用户的黑名单列表 private List<String> blackList; // 用户的简单描述 private String detail; //标记该用户为黑名单的人数 private int blackNum; public final int getBlackNum() { return blackNum; } public final void setBlackNum(int blackNum) { this.blackNum = blackNum; } public final int getUserId() { return userId; } public final void setUserId(int userId) { this.userId = userId; } public final String getUserName() { return userName; } public final void setUserName(String userName) { this.userName = userName; } public final String getTureName() { return tureName; } public final void setTureName(String tureName) { this.tureName = tureName; } public final String getPasswd() { return passwd; } public final void setPasswd(String passwd) { this.passwd = passwd; } public final int getAge() { return age; } public final void setAge(int age) { this.age = age; } public final int getSex() { return sex; } public final void setSex(int sex) { this.sex = sex; } public final boolean isNoFriendChat() { return noFriendChat; } public final void setNoFriendChat(boolean noFriendChat) { this.noFriendChat = noFriendChat; } public final List<String> getFriends() { return friends; } public final void setFriends(List<String> friends) { this.friends = friends; } public final List<String> getBlackList() { return blackList; } public final void setBlackList(List<String> blackList) { this.blackList = blackList; } public final String getDetail() { return detail; } public final void setDetail(String detail) { this.detail = detail; } public final void addFriend(String userName){ this.friends.add(userName); } public final void addBlack(String userName){ this.blackList.add(userName); } /** * 重写tostring方法,用于展示用户信息 */ public String toString() { return "name:" + this.tureName + "\tage:" + this.age + "\tsex:" + (this.sex == 1 ? "MEN" : (this.sex == 2 ? "WOMEN" : "OTHER")) + "\r\n" + "the detail:\r\n" + this.detail; } }
user操作公共类:
package chatSystem; import java.util.List; /** * UserUtil : 与用户相关的公共类 * @author xuejupo [email protected] * create in 2015-12-15 下午5:50:20 */ public class UserUtil { /** * getFriends: 获取用户的朋友列表 * @param username * @return * List<String> 返回类型 */ public static List<String> getFriends(String username){ return Storage.allUser.get(username).getFriends(); } /** * getBlackList: 获取用户的黑名单列表 * @param username * @return * List<String> 返回类型 */ public static List<String> getBlackList(String username){ return Storage.allUser.get(username).getBlackList(); } /** * isOnLine: 获取用户是否在线 * @param username * @return * boolean 返回类型 */ public static boolean isOnLine(String username){ return Storage.onLineUsers.get(Storage.allUser.get(username).getUserId()); } }
PS:没有界面的聊天系统实在是太不美观了。。。 聊天系统暂时先做到这,等自己能写客户端了(不管什么语言吧,至少要有客户端)再回头来把这个更新一下。注释很多,相信不难看懂。
写这个系统的时候,用到一个单例模式,明天开始把设计模式结合代码实例好好看看。