前言:哎,说来话长,那个地图编辑器做不下去了,自己也很懒,那书也看了才一半。前几天又开始了新的历程,想做一个IM软件出来,如今服务器和客户端是基本做好了,就等了进一步的改进。这里先贴一个用于储存在线用户的类,当然也提供基本的获取添加的方法。还没试验过。
里面包含一个内部类。内部类包括名称,标识号码,用户自己连接成功的socket,双人聊天客户端数组等等的数据区域。名称当然是用户的昵称了,标识号码就是唯一的一个userID,连接成功的socket就是客户端socket的referrence,而双人聊天客户端只是一个设想。现在是基于聊天室。外部的基本类里面定义了一个内部类的数组,一个PrintStream的数组,这个是和UserData一一对应的,而把它独立出来纯粹为了方便全发的速度。剩下的也就是当前数组下标和最大人数以及当前用户数量了。其他的一些方法不一一列举了,主要就是增加用户,删除用户,查找用户这些基本操作。
现在讲讲主要的操作流程。当一个客户端和服务器建立连接后,首先查询数据库,看用户ID和密码是否匹配。如果是就把相关的数据储存到userDataList中,已当前current指针下标添加,添加一个current自加一。实际用户数量也自加一。当客户端离开后,首先用该用户的userID在userDataList中查找,并返回相应的数组下标,然后删除,用户数量自动减一,注意,current并不减一,它永远代表最后一个数据的数组下标。当部分用户登陆几次退出后,肯定会造成userDataList的current为最大数组下标,也就是说不能再添加用户了。这个时候如果没有用户离开过,那么这个数组是完全真实填满的。如果有用户推出,那么数组中间就有空闲,不是被数据填满的,还有位置可以添加。但是添加永远是对current+1做数据存放的,所以就分两种情况:1.真实填满,数组扩容。2.虚假填满,整理数组为无空隙且紧密排列,这样就能在current后空出位置添加用户。需要注意的是,扩容和整理数组都非常耗时。相对来说整理的耗费的时间小于扩容。
import
java.io.PrintStream;
import
java.net.Socket;


/** */
/**
*
* 用户列表数据结构
* 做了线程同步 synchronized
*
*
*/

public
class
UserListData
...
{
// 在线用户列表
private UserData[] userDataList = null;
// 记录在线用户所有输出,方便群发
private PrintStream[] toClientOut = null;
// 指明当前的数组下标
private int current = -1;
// 指明当前实际在线用户数量
private int currentUserNo = 0;
private int maxUsr = 0;

/** *//**
*
* UserData:内部类,定义了单个用户的信息
* property:
* 名称,标识号码,用户自己连接成功的socket,双人聊天客户端数组
* Method:
* 添加双人聊天客户端
* 移除双人聊天客户端
* 调整方法暂且不用
*
*/

class UserData ...{
String name = null; // 记录用户的名称
long userID = 0; // 记录用户唯一一个标记数字
Socket clientSoc = null; // 用户的socket
Socket[] toTalkClientSocArr = null; // 正在和用户双人交谈的数组
long[] toTalkClientID = null; // 对应的userID
private int current = 0; // 交谈中的人在数组中的下标
private int currentNo = 0; // 记录双人交谈的人数

public UserData (String name, long id, Socket soc) ...{
this.name = name;
this.userID = id;
this.clientSoc = soc;
// 设定最多一个人只能和20个人双人对话
toTalkClientSocArr = new Socket[20];
toTalkClientID = new long[20];

/**//* 初始化
for (int i = 0; i<20; i++) {
toTalkClientSocArr[i] = null;
}
//*/
}

public UserData (String name, long id, Socket soc, int num) ...{
this.name = name;
this.userID = id;
this.clientSoc = soc;
toTalkClientSocArr = new Socket[num];
toTalkClientID = new long[num];

/**//* 初始化
for (int i = 0; i<num; i++) {
toTalkClientSocArr[i] = null;
}
//*/
}

public synchronized boolean addTalkClient (Socket soc, long id) ...{
// 判断是否超出长度

if (toTalkClientSocArr.length < 20) ...{
// 0开始数组下标,后自加
toTalkClientSocArr[current++] = soc;
toTalkClientID[current - 1] = id;
currentNo ++;
return true;
}
return false;
}

public synchronized boolean removeTalkClient (int index) ...{

if (toTalkClientSocArr.length < index + 1 || toTalkClientSocArr[index] == null) ...{
return false;
}
// 移除
toTalkClientSocArr[index] = null;
toTalkClientID[index] = 0;
// 客户数目减少
currentNo --;
// 把current移动到最后一个非null数据上
// 如果刚好删除的是最后一个数据

if (index == current) ...{
// 判断当当前数据为空且不是数组首位时自减1,并判再判断是否为空或者到首位

while(toTalkClientSocArr[current] == null && current != 0) ...{
current --;
}
}

else ...{
// 不是当前被删除,那么直接自减,后有排列方法
current --;
}
return true;
}
// 整理成没有空隙(null)的数组,重新调整后可能会改变当初插入的位置

public synchronized Socket[] coordinateTalkClientSocArr() ...{
Socket[] newArr = new Socket[toTalkClientSocArr.length];
int j = 0;

for (int i = 0; i < toTalkClientSocArr.length; i++) ...{

if (toTalkClientSocArr[i] != null) ...{
newArr[j++] = toTalkClientSocArr[i];
}
}
current = j - 1; // 移到末尾
toTalkClientSocArr = newArr;
return toTalkClientSocArr;
}
// 返回当前数组下标

public int getCurrent () ...{
return this.current;
}
// 返回该用户当前双人聊天的数目

public int getTalkClientNo () ...{
return this.currentNo;
}


/** *//**
* Method getUserID
*
*
* @return
*
*/

public long getUserID() ...{
// TODO: 在这添加你的代码
return this.userID;
}


/** *//**
* Method searchTalkClient
*
*
* @param id
*
* @return
*
*/

public int searchTalkClient(long id) ...{
// TODO: 在这添加你的代码

for (int i = 0; i<maxUsr; i++) ...{

if (id == toTalkClientID[i]) ...{
return i;
}
}
return -1;
}
}

/** *//**
*
* get name by index
*
*
*/

public String getNameAt (int index) ...{

if (index <= userDataList.length) ...{
return userDataList[index].name;
}
return "out of length!";
}

/** *//**
*
* 返回指定的UserData
*
*/

public UserData getUserDataAt (int index) ...{

if (index <= userDataList.length) ...{
return userDataList[index];
}
return new UserData("", -1, null);
}

/** *//**
*
* get total user number
*
*
*/

public int getTotalUserNo () ...{
return currentUserNo;
}

/** *//**
*
* get current position
*
*
*/

public int getCurrentIndex () ...{
return this.current;
}

/** *//**
*
* add a name to names[]
* 添加一个名字到数组中,如果数组满了就扩容为原来的两倍
* 判断方法见文档说明
*
*/

public synchronized void addUserData (String name, long id, Socket soc, PrintStream out) ...{
// 假满,真满,未满

if (current == maxUsr - 1 && current > currentUserNo - 1) ...{
// 数组其实未满,需调整,中间有空隙
this.coordinateUserList ();
// 调整后再增加
}

if (currentUserNo == maxUsr) ...{
// 数组真实填满并扩大数组两倍原数量
this.addMoreUserNo ();
}
// 游标指针后移一位
current++;
// 总数增加一个
currentUserNo++;
// 赋值
userDataList[current] = new UserData(name, id, soc);
toClientOut[current] = out;
}

/** *//**
*
* append more space to store name
* 必须数据容量满了并且没有null后才能调用
* 扩容为两倍 maxUsr *= 2;
*/

public synchronized void addMoreUserNo () ...{
UserData[] bigArray = null;
bigArray = new UserData[maxUsr * 2];

for (int i = 0; i<maxUsr; i ++) ...{
bigArray[i] = userDataList[i];
}
userDataList = bigArray;
maxUsr *= 2;
}

/** *//**
*
* set names to no null element and return it
*
*
*/

public String[] getNameArray () ...{
String[] newStr = null;

if (current > currentUserNo - 1) ...{
this.coordinateUserList();
}
newStr = new String[currentUserNo];

for (int i = 0; i<currentUserNo; i++) ...{
newStr[i] = userDataList[i].name;
}
return newStr;
}

/** *//**
*
* 整理后current指针被影响,要修改回到正确位置
* 所有用户的out也需要被重定向
*
*
*/

public synchronized void coordinateUserList() ...{
UserData[] ud = new UserData[maxUsr];
PrintStream[] ps = new PrintStream[maxUsr];
// 整理成数据之间没有null的数组
int j = 0;

for (int i = 0; i<= current; i++) ...{

if (userDataList[i] != null) ...{
ud[j] = this.userDataList[i];
ps[j] = this.toClientOut[i];
j++;
}
}
// 指针移动到末尾
current = j - 1;
// 重新指向
this.userDataList = ud;
this.toClientOut = ps;
}

/** *//**
*
* search a user in the array
*
*
*/

public int search(long id) ...{

for (int i = 0; i <= current; i++) ...{

if (userDataList[i].userID == id) ...{
return i;
}
}
return -1;
}

/** *//**
*
* userDataList toClientOut
*
*
*/

public synchronized boolean removeUserData(int index) ...{

if (index >= current) ...{
System.out.println ("数组越界......");
return false;
}
this.userDataList[index] = null;
this.toClientOut[index] = null;
return true;
}

/** *//**
*
* Construction
*
*
*/

public UserListData (int num) ...{
userDataList = new UserData[num];
this.maxUsr = num;
}


/** *//**
* Method getOutPutArr
* 得到所有成员的输出句柄
*
* @return
*
*/

public PrintStream[] getOutPutArr() ...{
// TODO: 在这添加你的代码

if (this.current > this.currentUserNo - 1) ...{
// 数组下标大于现有用户数量,需要调整
this.coordinateUserList();
}
return this.toClientOut;
}
}