如何用Java实现网络中国象棋室

来源:岁月联盟 编辑:zhuzhu 时间:2008-04-21

Java语言的简洁和完美,以及java网络功能的优越性是每个java体验者所体会的感受。笔者在闲暇之余,开发出网络中国象棋(以下简称象棋)程序,在此愿与广大java编程爱好者共享,做以介绍供大家参考。如有问题可与我联系:
网络象棋程序主要功能有象棋室(多象棋桌)功能、观棋功能、悔棋功能、下棋聊天功能、调取残局文件对弈功能、方便的人性化图形界面操作等功能。由象棋服务和客户端Applet组成,Applet实现象棋室的显示,象棋桌的显示和走步判断以及用户的一些操作如悔棋、认输等功能的实现。为考虑资源使用情况和界面美观,象棋桌采用使用高效和优化的双缓冲图形界面处理技术,使用户操作更舒适,更方便。以下依次详细介绍象棋服务和客户端的实现过程。
首先介绍象棋服务,它是运行在服务器端的用于处理和转发用户登录到象棋室,进入离开游戏桌,走棋,悔棋,认输,退出等等应用。
1、登录处理:
//////////////////定义登录连接处理类
package sunstudio.chess.event;

import java.util.*;
import java.net.*;
import java.io.*;
import sunstudio.util.*;
import sunstudio.*;

public class ChessLogin implements Runnable{

ServerSocket ss=null;
boolean isrunning=true;
Vector listeners=new Vector();
int guestid=0;

public ChessLogin(ChessServer s){
ss=s.ss;
addLoginListener(s);
new Thread(this).start();
}
public void run(){
while(isrunning){
try{
Socket socket=ss.accept();
String un=chkLogin(socket.getInputStream());
if(un!=null)notifyListener(guestid++,un,socket);
}catch(IOException e){}
}
}
public void addLoginListener(LoginListener l){
listeners.addElement(l);
}
void notifyListener(int userid,String username,Socket sock){
LoginEvent evt=new LoginEvent(userid,username,sock);
for(Enumeration enu=listeners.elements();enu.hasMoreElements();){
((LoginListener)enu.nextElement()).onLoginEvent(evt);
}
}
public String chkLogin(InputStream is)throws IOException{
byte[] head=new byte[12];
HttpInputStream.readBytes(is,12,head);
int cmdtype=head[0];
int totalsize=Convert.byteToShort(head[1],head[2]);
if(totalsize==0)return null;
byte[] data=new byte[totalsize];
HttpInputStream.readBytes(is,totalsize,data);
//System.out.print("type="+cmdtype+",totalsize="+totalsize+",username="+parseLoginData(data));
return parseLoginData(data);
}
public String parseLoginData(byte[] d){
return new String(d);
}
public static int byteToInt(byte j,byte k,byte m,byte n){
int a =j&0xff;
int b =k&0xff;
int c =m&0xff;
int d =n&0xff;
return (d<<24|c<<16|b<<8|a);
}
}
//////////////////定义登录连接事件类
package sunstudio.chess.event;

import java.net.*;

public class LoginEvent{

public int userID;
public String username;
public Socket socket;

public LoginEvent(int userID,String username,Socket sock){
this.userID=userID;
this.username=username;
this.socket=sock;
}
}
//////////////////登录监听接口
package sunstudio.chess.event;

public interface LoginListener{
public void onLoginEvent(LoginEvent evt);
}

2、象棋服务主类,接收用户登录连接,和用户的数据处理和转发:
package sunstudio;

import sunstudio.event.*;
import Java.util.*;
import java.net.*;
import java.io.*;
import sunstudio.util.*;

public class ChessServer implements LoginListener{

int tablecount=20;
ChessTable[] tb=new ChessTable[tablecount];

Hashtable userlist=new Hashtable();

public ServerSocket ss=null;
ChessLogin cl=null;
public static ChessServer hwnd=null;

public static void main(String[] args){
ChessServer server=new ChessServer();
}
public ChessServer(){
for(int i=0;i try{
ss=new ServerSocket(2000);
cl=new ChessLogin(this);
}catch(IOException e){e.printStackTrace();}
hwnd=this;
}
public int addTableUser(int tableID,int userID,int state){
if(tb[tableID].contains(new Integer(userID)))return -1;
int st=state;
for(Enumeration enu=tb[tableID].elements();enu.hasMoreElements();){
ChessUser user=(ChessUser)enu.nextElement();
if(user.state<2&&user.state==st){
st+=2;
break;
}
}
ChessUser user=(ChessUser)userlist.get(new Integer(userID));
user.setState(st);
user.tableID=-1;
tb[tableID].put(new Integer(userID),user);
return st;
}
public void removeTableUser(int tableID,int userID){
if(tableID<0||tableID>19)return;
ChessUser user=(ChessUser)tb[tableID].remove(new Integer(userID));
if(user!=null){
user.tableID=-1;
user.state=-1;
}
}
public boolean addUser(ChessUser user){
if(userlist.contains(new Integer(user.userID)))return false;
userlist.put(new Integer(user.userID),user);
return true;
}
public void removeUser(int userID){
ChessUser user=(ChessUser)userlist.remove(new Integer(userID));
if(user!=null){
user.close();
user=null;
}
}
public void sendToRoom(byte[] h,int l,byte[] d){
for(Enumeration enu=userlist.elements();enu.hasMoreElements();){
try{
((ChessUser)enu.nextElement()).sender.sendData(h,d,l);
}catch(Exception e){}
}
}
public void sendToTable(byte[] h,int l,byte[] d){
for(Enumeration enu=tb[h[3]].elements();enu.hasMoreElements();){
try{
((ChessUser)enu.nextElement()).sender.sendData(h,d,l);
}catch(Exception e){}
}
}
public byte[] getUserListData(){
byte[] ul=new byte[userlist.size()*14];
int ptr=0;
for(Enumeration enu=userlist.elements();enu.hasMoreElements();){
ChessUser user=(ChessUser)enu.nextElement();
System.arraycopy(user.getBytes(),0,ul,ptr,14);
ptr+=14;
}
return ul;
}
public void onUsERPackageArrived(byte[] h,int l,byte[] d){
synchronized(this){
try{
int userID=Convert.byteToInt(h[5],h[6],h[7],h[8]);
ChessUser user=null;
user=(ChessUser)userlist.get(new Integer(userID));
switch(h[0]){
case 0://进入游戏桌
//user.setState(h[4]);
System.out.println("用户"+user.username+"进入"+h[3]+"号游戏桌"+(h[4]%2==0?"执红":"执黑")+"!");
h[4]=(byte)addTableUser(h[3],userID,h[4]);
break;
case 1:
//user.setState(-1);
System.out.println("用户"+user.username+"退出"+h[3]+"号游戏桌!");
removeTableUser(h[3],userID);
break;
/////////////////////////////////////
case 2://走步
sendToTable(h,l,d);
return;
case 3://求和
sendToTable(h,l,d);
return;
case 4://认输
sendToTable(h,l,d);
return;
case 5://悔棋
sendToTable(h,l,d);
return;
/////////////////////////////////////
case 6://用户列表
break;
case 7://新用户登录
break;
case 8://用户退出
if(user==null)return;
try{
removeTableUser(user.tableID,user.userID);
}catch(Exception ex){ex.printStackTrace();}
try{
removeUser(user.userID);
}catch(Exception exx){exx.printStackTrace();}
System.out.println("用户退出象棋室");
break;
case 9://聊天信息
sendToTable(h,l,d);
return;
}
sendToRoom(h,l,d);
}catch(Exception e){e.printStackTrace();}
}
}
public void onLoginEvent(LoginEvent evt){
System.out.println(evt.username+","+evt.userID);
ChessUser user=new ChessUser(evt.username,evt.userID,evt.socket);
if(user.init()){
System.out.println("用户进入象棋室");
sendToRoom(new byte[]{7,14,0,0,0,0,0,0,0,0,0,0},14,user.getBytes());//新用户登录
if(addUser(user)){
user.start();
try{
user.sender.sendUserListData(getUserListData());
}catch(Exception e){}
}
}
//else销毁用户对象
}
}
3、象棋用户类,实现用户信息的存储和用户数据的接收和转发:
package sunstudio;

import java.net.*;
import java.io.*;
import sunstudio.netlib.*;
import sunstudio.util.*;

public class ChessUser implements TcpDataListener{

public String username=null;
public int userID=-1,tableID=-1;
public int state=-1;//0:执红走棋1:执黑走棋2:看红走棋3:看黑走棋

byte[] data=new byte[14];

Socket socket=null;

TcpDataReceiver receiver=null;
TcpDataSender sender=null;

public ChessUser(String un,int ui,Socket st){
username=un;
userID=ui;
socket=st;
System.arraycopy(username.getBytes(),0,data,0,Math.min(8,username.getBytes().length));
System.arraycopy(Convert.intToBytes(ui),0,data,8,4);
data[12]=(byte)-1;
data[13]=(byte)-1;
}
public boolean init(){
try{
sender=new TcpDataSender(socket.getOutputStream());
receiver=new TcpDataReceiver(socket.getInputStream());
receiver.addTcpDataListener(this);
return true;
}catch(IOException e){}
return false;
}
public void setState(int stat){
state=stat;
data[13]=(byte)stat;
}
public void start(){
try{
sender.sendLoginBack(userID);
receiver.start();
}catch(Exception e){}
}
public void onTcpDataArrived(TcpDataEvent evt){
ChessServer.hwnd.onUserPackageArrived(evt.getHeader(),evt.getDataLen(),evt.getData());
}
public byte[] getBytes(){
return data;
}
public void close(){
try{
if(socket!=null){
socket.close();
socket=null;
}
}catch(Exception e){}
try{
if(receiver!=null){
receiver.setRunning(false);
receiver=null;
}
}catch(Exception e){}
try{
if(sender!=null)sender=null;
}catch(Exception e){}
}
}
4、TCP数据接收器:
package sunstudio.netlib;

import java.util.*;
import java.net.*;
import java.io.*;
import sunstudio.util.*;

public class TcpDataReceiver extends Thread{

Vector listeners=new Vector();
boolean isrunning=true;
InputStream is=null;

byte[] h=new byte[12];
byte[] d=new byte[1024];
int l=0;

public TcpDataReceiver(InputStream s){
is=s;
}
public void run(){
try{
while(isrunning){
HttpInputStream.readBytes(is,12,h);
l=Convert.byteToShort(h[1],h[2]);
if(l>0)HttpInputStream.readBytes(is,l,d);
if(l>0)notifyListeners(h,l,d);
else if(l==0)notifyListeners(h,0,null);
else throw new IOException();
}
}catch(IOException e){
System.out.println("读取流数据异常!");
//异常处理,关闭socket
}
}
public void addTcpDataListener(TcpDataListener hrl){
listeners.addElement(hrl);
}
synchronized void notifyListeners(byte[] hh,int ll,byte[] dd){
TcpDataEvent evt=new TcpDataEvent(hh,ll,dd);
for(Enumeration enumeration=listeners.elements();enumeration.hasMoreElements();((TcpDataListener)enumeration.nextElement()).onTcpDataArrived(evt));
}
public void setRunning(boolean rr){
this.isrunning=rr;
}
}
5、TCP数据监听接口:
package sunstudio.netlib;

public interface TcpDataListener{
public void onTcpDataArrived(TcpDataEvent evt);
}
6、TCP数据包接收事件:
package sunstudio.netlib;

import java.net.*;

public class TcpDataEvent{

byte[] head=null;
int datalen=-1;
byte[] datas=null;

public TcpDataEvent(byte[] h,int len,byte[] dt){
head=h;
datalen=len;
datas=dt;
}
public byte[] getHeader(){
return head;
}
public byte[] getData(){
return datas;
}
public int getDataLen(){
return datalen;
}
}
7、TCP数据发送器:
package sunstudio.netlib;

import java.io.*;
import sunstudio.util.*;

public class TcpDataSender{

OutputStream os;
byte[] d=new byte[1024];
int l;

public TcpDataSender(OutputStream ost){
os=ost;
}
public void sendLoginBack(int userID){
byte[] cd=new byte[9];
System.arraycopy(Convert.intToBytes(userID),0,cd,2,4);
sendeHeader((byte)10,cd);
}
public void sendeHeader(byte cmdType,byte[] cData){
try{
d[0]=cmdType;//登录
System.arraycopy(Convert.shortToBytes((short)0),0,d,1,2);
System.arraycopy(cData,0,d,3,9);
os.write(d,0,12);
os.flush();
}catch(Exception e){}
}
public void sendUserListData(byte[] ud){
byte[] hh=new byte[12];
hh[0]=6;
System.arraycopy(Convert.shortToBytes((short)ud.length),0,hh,1,2);
sendData(hh,ud,ud.length);
}
public void sendData(byte[] hh,byte[] dd,int ln){
try{
os.write(hh);
os.write(dd,0,ln);
}catch(Exception e){}
}
}
8、数据接收类(保证读取TCP字节数组数据长度正确)
package sunstudio.util;

import java.io.*;

public class HttpInputStream extends InputStream{
InputStream is=null;
public HttpInputStream(InputStream is1){
is=is1;
}
public int read() throws IOException{
return is.read();
}
public String readLine() throws IOException{
ByteArrayOutputStream b=new ByteArrayOutputStream();
int c;
while((c=read())!=-1){
if(c==10)break;
else b.write(c);
}
if(c==-1)return null;
if(b.size()==1){
if(b.toByteArray()[0]==13)return "";
}
return new String(b.toByteArray(),0,b.size()-1);
}
public static void readBytes(InputStream inputStream,int length,byte[] data) throws IOException{
if(length==0)return;
int n=0;
int read=0;
do{
read=inputStream.read(data,n,length-n);
n+=read;
if(n==-1)throw new IOException("网络连接异常断开,读取数据异常");
}while(n }
}