前言:哎,说来话长,那个地图编辑器做不下去了,自己也很懒,那书也看了才一半。前几天又开始了新的历程,想做一个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
...
{
//在线用户列表
privateUserData[] userDataList=null;
//记录在线用户所有输出,方便群发
privatePrintStream[] toClientOut=null;
//指明当前的数组下标
privateintcurrent=-1;
//指明当前实际在线用户数量
privateintcurrentUserNo=0;
privateintmaxUsr=0;
/** *//**
*
* UserData:内部类,定义了单个用户的信息
* property:
* 名称,标识号码,用户自己连接成功的socket,双人聊天客户端数组
* Method:
* 添加双人聊天客户端
* 移除双人聊天客户端
* 调整方法暂且不用
*
*/
classUserData...{
String name=null;//记录用户的名称
longuserID=0;//记录用户唯一一个标记数字
Socket clientSoc=null;//用户的socket
Socket[] toTalkClientSocArr=null;//正在和用户双人交谈的数组
long[] toTalkClientID=null;//对应的userID
privateintcurrent=0;//交谈中的人在数组中的下标
privateintcurrentNo=0;//记录双人交谈的人数
publicUserData (String name,longid, Socket soc)...{
this.name=name;
this.userID=id;
this.clientSoc=soc;
//设定最多一个人只能和20个人双人对话
toTalkClientSocArr=newSocket[20];
toTalkClientID=newlong[20];
/**//*初始化
for (int i = 0; i<20; i++) {
toTalkClientSocArr[i] = null;
}
//*/
}
publicUserData (String name,longid, Socket soc,intnum)...{
this.name=name;
this.userID=id;
this.clientSoc=soc;
toTalkClientSocArr=newSocket[num];
toTalkClientID=newlong[num];
/**//*初始化
for (int i = 0; i
toTalkClientSocArr[i] = null;
}
//*/
}
publicsynchronizedbooleanaddTalkClient (Socket soc,longid)...{
//判断是否超出长度
if(toTalkClientSocArr.length<20)...{
//0开始数组下标,后自加
toTalkClientSocArr[current++]=soc;
toTalkClientID[current-1]=id;
currentNo++;
returntrue;
}
returnfalse;
}
publicsynchronizedbooleanremoveTalkClient (intindex)...{
if(toTalkClientSocArr.length
returnfalse;
}
//移除
toTalkClientSocArr[index]=null;
toTalkClientID[index]=0;
//客户数目减少
currentNo--;
//把current移动到最后一个非null数据上
//如果刚好删除的是最后一个数据
if(index==current)...{
//判断当当前数据为空且不是数组首位时自减1,并判再判断是否为空或者到首位
while(toTalkClientSocArr[current]==null&¤t!=0)...{
current--;
}
}
else...{
//不是当前被删除,那么直接自减,后有排列方法
current--;
}
returntrue;
}
//整理成没有空隙(null)的数组,重新调整后可能会改变当初插入的位置
publicsynchronizedSocket[] coordinateTalkClientSocArr()...{
Socket[] newArr=newSocket[toTalkClientSocArr.length];
intj=0;
for(inti=0; i
if(toTalkClientSocArr[i]!=null)...{
newArr[j++]=toTalkClientSocArr[i];
}
}
current=j-1;//移到末尾
toTalkClientSocArr=newArr;
returntoTalkClientSocArr;
}
//返回当前数组下标
publicintgetCurrent ()...{
returnthis.current;
}
//返回该用户当前双人聊天的数目
publicintgetTalkClientNo ()...{
returnthis.currentNo;
}
/** *//**
* Method getUserID
*
*
*@return
*
*/
publiclonggetUserID()...{
//TODO: 在这添加你的代码
returnthis.userID;
}
/** *//**
* Method searchTalkClient
*
*
*@paramid
*
*@return
*
*/
publicintsearchTalkClient(longid)...{
//TODO: 在这添加你的代码
for(inti=0; i
if(id==toTalkClientID[i])...{
returni;
}
}
return-1;
}
}
/** *//**
*
* get name by index
*
*
*/
publicString getNameAt (intindex)...{
if(index<=userDataList.length)...{
returnuserDataList[index].name;
}
return"out of length!";
}
/** *//**
*
* 返回指定的UserData
*
*/
publicUserData getUserDataAt (intindex)...{
if(index<=userDataList.length)...{
returnuserDataList[index];
}
returnnewUserData("",-1,null);
}
/** *//**
*
* get total user number
*
*
*/
publicintgetTotalUserNo ()...{
returncurrentUserNo;
}
/** *//**
*
* get current position
*
*
*/
publicintgetCurrentIndex ()...{
returnthis.current;
}
/** *//**
*
* add a name to names[]
* 添加一个名字到数组中,如果数组满了就扩容为原来的两倍
* 判断方法见文档说明
*
*/
publicsynchronizedvoidaddUserData (String name,longid, Socket soc, PrintStream out)...{
//假满,真满,未满
if(current==maxUsr-1&¤t>currentUserNo-1)...{
//数组其实未满,需调整,中间有空隙
this.coordinateUserList ();
//调整后再增加
}
if(currentUserNo==maxUsr)...{
//数组真实填满并扩大数组两倍原数量
this.addMoreUserNo ();
}
//游标指针后移一位
current++;
//总数增加一个
currentUserNo++;
//赋值
userDataList[current]=newUserData(name, id, soc);
toClientOut[current]=out;
}
/** *//**
*
* append more space to store name
* 必须数据容量满了并且没有null后才能调用
* 扩容为两倍 maxUsr *= 2;
*/
publicsynchronizedvoidaddMoreUserNo ()...{
UserData[] bigArray=null;
bigArray=newUserData[maxUsr*2];
for(inti=0; i
bigArray[i]=userDataList[i];
}
userDataList=bigArray;
maxUsr*=2;
}
/** *//**
*
* set names to no null element and return it
*
*
*/
publicString[] getNameArray ()...{
String[] newStr=null;
if(current>currentUserNo-1)...{
this.coordinateUserList();
}
newStr=newString[currentUserNo];
for(inti=0; i
newStr[i]=userDataList[i].name;
}
returnnewStr;
}
/** *//**
*
* 整理后current指针被影响,要修改回到正确位置
* 所有用户的out也需要被重定向
*
*
*/
publicsynchronizedvoidcoordinateUserList()...{
UserData[] ud=newUserData[maxUsr];
PrintStream[] ps=newPrintStream[maxUsr];
//整理成数据之间没有null的数组
intj=0;
for(inti=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
*
*
*/
publicintsearch(longid)...{
for(inti=0; i<=current; i++)...{
if(userDataList[i].userID==id)...{
returni;
}
}
return-1;
}
/** *//**
*
* userDataList toClientOut
*
*
*/
publicsynchronizedbooleanremoveUserData(intindex)...{
if(index>=current)...{
System.out.println ("数组越界......");
returnfalse;
}
this.userDataList[index]=null;
this.toClientOut[index]=null;
returntrue;
}
/** *//**
*
* Construction
*
*
*/
publicUserListData (intnum)...{
userDataList=newUserData[num];
this.maxUsr=num;
}
/** *//**
* Method getOutPutArr
* 得到所有成员的输出句柄
*
*@return
*
*/
publicPrintStream[] getOutPutArr()...{
//TODO: 在这添加你的代码
if(this.current>this.currentUserNo-1)...{
//数组下标大于现有用户数量,需要调整
this.coordinateUserList();
}
returnthis.toClientOut;
}
}