⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serverframe.java

📁 一个实现联机对弈游戏服务器端的java程序
💻 JAVA
字号:
import javax.swing.JFrame;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.awt.BorderLayout;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JButton;
import java.awt.Color;
import java.net.ServerSocket;
import java.net.Socket;
import java.io.PrintStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.StringTokenizer;

public class ServerFrame extends JFrame implements ActionListener{
	JScrollPane scrollPane=new JScrollPane();
	JTextArea logTextArea=new JTextArea();  //记录服务器事务的文本编辑框
	JButton shutdownButton=new JButton("关闭服务器");  //关闭按钮
	//
	int serverInPort=6817;  //服务器的输入端口
	int serverOutPort=6818;  //服务器的输出端口
	ServerSocket sInSocket;  //输入套接字
	ServerSocket sOutSocket;  //输出套接字
	//
	SRThread srThread;
	//初始化对弈情况表(共两张台,每张台有两张椅子,初始状态为未入座,IP、Socket等暂时为null.)
	TableStatus[] tableStatus={new TableStatus(),new TableStatus()};

	public ServerFrame(){
		setTitle("互联网多人联机对弈(井字棋)游戏服务器");
		setSize(500,300);
		//添加带水平、垂直滚动条的滚动面板
		scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		getContentPane().add(scrollPane,BorderLayout.CENTER);
		//设置文本框属性
		logTextArea.setEditable(false);  //只读
		logTextArea.setBackground(Color.black);  //背景为黑色
		logTextArea.setForeground(Color.white);  //字体为白色
		scrollPane.getViewport().setView(logTextArea);  //使文本编辑框具有滚动条
		//添加"关闭服务器"按钮
		getContentPane().add(shutdownButton,BorderLayout.SOUTH);
		show();
		//
		try{
			sInSocket=new ServerSocket(serverInPort);  //创建用于服务器输入的套接字
			sOutSocket=new ServerSocket(serverOutPort); //创建用于服务器输出的套接字
		}catch(IOException e){
			System.out.println("创建服务器ServerSocket失败");
			System.exit(1);  //退出服务器
		}
		//启动读数据线程
		srThread=new SRThread();
		srThread.start();
		//添加"关闭服务器"按钮的单击事件监听器
		shutdownButton.addActionListener(this);
	}
	
	public void actionPerformed(ActionEvent e){
			dispose();
			System.exit(0);
	}	
	public void finalize(){
		try{
			sInSocket.close();
			sOutSocket.close();
		}catch(IOException e2){System.out.println("关闭套接字失败");}
		sInSocket=null;
		sOutSocket=null;
		srThread=null;
	}

	public static void main(String[] args){
		ServerFrame serverFrame=new ServerFrame();
	}
	
	public void log(String s){  //在文本框显示服务器工作日志
		logTextArea.append(s+"\n");  //主动换行
	}
	
	public void setChairIdle(int tableNum,int chairNum){
		tableStatus[tableNum].chairStatus[chairNum]=0;
		tableStatus[tableNum].ip[chairNum]=null;
		tableStatus[tableNum].sWriter[chairNum]=null;
	}
	
	public void setChairOn(int tableNum,int chairNum,String sIp,PrintStream writer){
		tableStatus[tableNum].chairStatus[chairNum]=1;  //已被坐上
		tableStatus[tableNum].ip[chairNum]=sIp;
		tableStatus[tableNum].sWriter[chairNum]=writer;
	}

	public void sendOnSeatMan(String msg){ //将msg消息发送到已坐下,但还未开始对弈的所有客户.
		for(int tNum=0;tNum<2;tNum++){
			for(int cNum=0;cNum<2;cNum++){
				if(tableStatus[tNum].chairStatus[cNum]==1 && tableStatus[tNum].playStatus!=3 && tableStatus[tNum].playStatus!=4){
					tableStatus[tNum].sWriter[cNum].println(msg);
				}
			}
		}
	}
	
	class TableStatus{
		int[] chairStatus={0,0};  //对应两张椅子的入座状态(0表示空出,1表示已入座)
		int playStatus=2;  //台面状态(2表示未开始对弈,0表示上方座位下先手棋,1则表示下方座位为先手棋,3表示正申请重新对弈,4表示有一方已离座)
		String[] ip={null,null};  //对应两张椅子的入座机器IP
		PrintStream[] sWriter={null,null};  //对应于两张椅子上服务器端的数据输出流
	}

	class SRThread extends Thread{  //Server Read 读线程
		public void run(){
			while(true){
				try{  //利用ServerSocket套接字监听客户端,并创建一个与客户端连接对应的套接字。
					synchronized(this){
						Socket cInSocket=sInSocket.accept();
						Socket cOutSocket=sOutSocket.accept();
						ChildThread childThread=new ChildThread(cInSocket,cOutSocket);
						childThread.start();
					}
				}catch(IOException e){
					System.out.println("创建与客户端连接的套接字失败.");
					System.exit(2);  //退出服务器
				}
			}
		}
	}
	
	class ChildThread extends Thread{
		Socket cInSocket;  //用于读入客户端数据的服务器端套接字
		Socket cOutSocket;  //用于写入客户端数据的服务器端套接字
		PrintStream sWriter;
		BufferedReader sReader;
		String receiveMsg;
		StringTokenizer st;
		int tableNum,chairNum;  //此线程所对应的客户的台号、椅子号
		int cellIndex;  //棋格号
		String sIp;

		ChildThread(Socket cInSocket,Socket cOutSocket){
			this.cInSocket=cInSocket;
			this.cOutSocket=cOutSocket;
			//取得对应的输出流,发送入座信息到客户端,以初始化客户端界面。
			try{
				sWriter=new PrintStream(cOutSocket.getOutputStream()); 
				synchronized(tableStatus){ //同步该语句块
					for(int tNum=0;tNum<2;tNum++){  //发送格式"seat:台号,椅子号,入座状态"数据到客户端
						for(int cNum=0;cNum<2;cNum++){
							sWriter.println("seat:"+tNum+","+cNum+","+tableStatus[tNum].chairStatus[cNum]);
						}
					}
				}
				log(cInSocket.getInetAddress().getHostAddress()+"连接上来,但未入座.");  //写服务器日志  
				//初始化输入流sReader
				sReader=new BufferedReader(new InputStreamReader(cInSocket.getInputStream()));
			}catch(IOException e){
				System.out.println("线程创建输入输出流失败"+e);
			}
		}

		public void run(){
			while(true){
				try{
					receiveMsg=sReader.readLine();
				}catch(IOException e){
					System.out.println("读数据IO出错.");
					break;  //使线程死亡
				}
				if(receiveMsg.startsWith("logout:")){  //注销座位
					receiveMsg=receiveMsg.substring(7,receiveMsg.length()); 
					st=new StringTokenizer(receiveMsg,",");					
					try{
						while(st.hasMoreTokens()){
							tableNum=Integer.parseInt(st.nextToken());
							chairNum=Integer.parseInt(st.nextToken());
						}
					}catch(NumberFormatException e){}
					if(tableNum!=-1 && chairNum!=-1){  //不是首次入座位
						synchronized(tableStatus){  //同步该语句块
							sendOnSeatMan("seat:"+tableNum+","+chairNum+",0");  //刷新在坐的客户台面
							setChairIdle(tableNum,chairNum);  //设置座位空闲
						}
					}
				}
				else if(receiveMsg.startsWith("login:")){  //座位申请注册
					receiveMsg=receiveMsg.substring(6,receiveMsg.length());
					st=new StringTokenizer(receiveMsg,",");
					try{
						while(st.hasMoreTokens()){
							tableNum=Integer.parseInt(st.nextToken());
							chairNum=Integer.parseInt(st.nextToken());
						}
					}catch(NumberFormatException e){}
					sIp=cInSocket.getInetAddress().getHostAddress();
					synchronized(tableStatus){  //同步该语句块
						if(tableStatus[tableNum].chairStatus[chairNum]==0){  //该座位未被坐下
							//登记对弈情况表的tableNum号台、chairNum号椅子的信息
							setChairOn(tableNum,chairNum,sIp,sWriter);
							//立即刷新入坐者
							sendOnSeatMan("seat:"+tableNum+","+chairNum+",1");
							log(sIp+"申请座位成功.已坐下"+tableNum+"号台,"+chairNum+"号椅子.");
						}
						else{  //如果客户端申请一个已被占用的座位,login失败.
							sWriter.println("no seat!"); //向此客户端发送座位被占用信息
						}
					}
				}
				else if(receiveMsg.startsWith("play!")){  //处理对弈请求
					synchronized(tableStatus){  //同步该语句块
						//对方已入座,才可开始对弈,并且该台号仍为未开始对弈状态(这个判断可以阻止双方竞争先手棋).
						if(tableStatus[tableNum].chairStatus[1-chairNum]==1 && tableStatus[tableNum].playStatus==2){
							tableStatus[tableNum].playStatus=chairNum;  //设置正在对弈状态和下先手棋的一方
							//发送台号到对弈双方,表示对弈开始.
							sWriter.println("start:"+chairNum);  //发送给请求对弈者,chairNum表示先下棋的椅子号.
							tableStatus[tableNum].sWriter[1-chairNum].println("start:"+chairNum); //发送给对方
						}
					}
				}
				else if(receiveMsg.startsWith("go:")){  //处理对方棋步
					receiveMsg=receiveMsg.substring(3,receiveMsg.length());
					try{
						cellIndex=Integer.parseInt(receiveMsg);
					}catch(NumberFormatException e){}
					tableStatus[tableNum].sWriter[1-chairNum].println("chess:"+cellIndex);  //向对弈方转发落子所在的棋格号
				}
				else if(receiveMsg.startsWith("win:")){  //收到嬴棋信息
					receiveMsg=receiveMsg.substring(4,receiveMsg.length());
					try{
						cellIndex=Integer.parseInt(receiveMsg);
					}catch(NumberFormatException e){}
					tableStatus[tableNum].sWriter[1-chairNum].println("defeat:"+cellIndex);  //转发对弈方输棋信息
				}
				else if(receiveMsg.startsWith("peace:")){  //收到和局信息
					receiveMsg=receiveMsg.substring(6,receiveMsg.length());
					try{
						cellIndex=Integer.parseInt(receiveMsg);
					}catch(NumberFormatException e){}
					tableStatus[tableNum].sWriter[1-chairNum].println("chess:"+cellIndex);
					tableStatus[tableNum].sWriter[1-chairNum].println("peace!");  //通知这局棋和了
				}
				else if(receiveMsg.startsWith("talk:")){  //收到棋盘满子的信息
					receiveMsg=receiveMsg.substring(5,receiveMsg.length());
					tableStatus[tableNum].sWriter[1-chairNum].println("message:"+receiveMsg); //转发对弈方转发聊天话本
				}
				else if(receiveMsg.startsWith("restart!")){  //收到重新开棋对弈请求
					if(tableStatus[tableNum].playStatus!=3 && tableStatus[tableNum].playStatus!=4){  //对方并未申请重新开棋或对方未离开(避免双方竞争重新开棋)
						tableStatus[tableNum].playStatus=3;  //设置该台正被申请重新对弈的状态
						tableStatus[tableNum].sWriter[1-chairNum].println("again?"); //向对弈方发送重新开棋请求
					}
				}
				else if(receiveMsg.startsWith("agree!")){  //收到同意重新开棋信息
					receiveMsg=receiveMsg.substring(6,receiveMsg.length());
					tableStatus[tableNum].playStatus=1-chairNum;  //表明1-chairNum椅子执子先行的(实际使该状态不为3).
					tableStatus[tableNum].sWriter[1-chairNum].println("agree!");  //向请求者转发同意重新开局信息
				}
				else if(receiveMsg.startsWith("go back!")){  //收到返回游戏大厅信息
					tableStatus[tableNum].sWriter[1-chairNum].println("go back!");  //向对弈方也发送返回信息
					//该台号的两张椅子恢复可用,但不关闭Socket等资源.
					setChairIdle(tableNum,chairNum);
					setChairIdle(tableNum,1-chairNum);
					tableStatus[tableNum].playStatus=2; //恢复该台号状态为未开始对弈
					//通知在坐、但还未开棋的客户该台面(两张椅子)已可用
					sendOnSeatMan("seat:"+tableNum+","+chairNum+",0");
					sendOnSeatMan("seat:"+tableNum+","+(1-chairNum)+",0");
				}
				else if(receiveMsg.startsWith("refresh!")){  //收到刷新客户端台面请求
					synchronized(tableStatus){  //同步该语句块
						for(int tNum=0;tNum<2;tNum++){ //发送所有台面、椅子的入座信息
							for(int cNum=0;cNum<2;cNum++){
								sWriter.println("refresh:"+tNum+","+cNum+","+tableStatus[tNum].chairStatus[cNum]);
							}
						}
					}
				}
				else if(receiveMsg.startsWith("exit:")){  //收到客户退出信息
					receiveMsg=receiveMsg.substring(5,receiveMsg.length());
					st=new StringTokenizer(receiveMsg,",");
					try{
						while(st.hasMoreTokens()){
							tableNum=Integer.parseInt(st.nextToken());
							chairNum=Integer.parseInt(st.nextToken());
						}
					}catch(NumberFormatException e){}
					if(tableNum!=-1 && chairNum!=-1){  //此客户已坐下的情形
						if(tableStatus[tableNum].playStatus==0 || tableStatus[tableNum].playStatus==1){  //客户已开始对弈的情形
							//让同台的对弈方返回游戏大厅
							tableStatus[tableNum].sWriter[1-chairNum].println("she exit!");
						}
						//让其他已入座、但未开始对弈的客户刷新台面(不发给自己)
						for(int tNum=0;tNum<2;tNum++){
							for(int cNum=0;cNum<2;cNum++){
								if(((tNum!=tableNum)||(cNum!=chairNum)) && (tableStatus[tNum].chairStatus[cNum]==1 && tableStatus[tNum].playStatus!=3 && tableStatus[tNum].playStatus!=4)){
									tableStatus[tNum].sWriter[cNum].println("seat:"+tableNum+","+chairNum+",0");
								}
							}
						}
						//恢复该客户的台面、椅子未入座
						tableStatus[tableNum].sWriter[chairNum].close();
						setChairIdle(tableNum,chairNum);  //设置退出者座位为空
						tableStatus[tableNum].playStatus=2; //该台还未开始对弈
						log(sIp+"客户:("+tableNum+"号台,"+chairNum+"号椅子)退出了游戏");
						break; //退出客户线程
					}
					else{  //该客户并未取得座位
						break;  //直接退出客户线程
					}
				}
			}
		}

		public void finalize(){  //释放线程资源
			try{
				cInSocket.close();
				cOutSocket.close();
				sReader.close();
				sWriter.close();
			}catch(IOException e){
				System.out.println("关闭线程的资源出现错误"+e);
			}
			cInSocket=null;
			cOutSocket=null;
			sReader=null;
			sWriter=null;
		}
	}
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -