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

📄 chatserver.java

📁 JAVA
💻 JAVA
字号:
package chatServer;

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

/**
 * 这个类是服务程序的主类,用来与数据库连接,负责处理用户的注册,登录检测,下线删除等数据库的操作
 * 同时也是用户之间相互联系通信的桥梁.处理用户的各种请求,一对一单聊请求,群聊请求等的响应
 * 所以要运行客户程序必须保证这个程序先运行起来
 * @author 黄祖光
 */

public class ChatServer implements Runnable
{
	public static int clientnum=10000;
	public static int SERVERPORT=5566;
	Connection con=null;
	DatagramSocket socket=null;
	boolean read=false,quited=false,isSend=false,stop=false;//isSend是用来控制群聊的发送的
	Thread t=null;
	String sure,addname,sendInfo;//sendInfo是用来存放新登录用户的信息的,服务程序发给每个在线用户.
	InetAddress ad=null;
	GroupChat grch=null;
	ServerFrame sf=null;
	
	public ChatServer()
	{
			try
			{
				socket=new DatagramSocket(SERVERPORT);
				String driver="sun.jdbc.odbc.JdbcOdbcDriver";
		        Class.forName(driver);//建立JDBC:ODBC桥,与ACCESS数据库取得连接
		        t=new Thread(this);
		        t.start();//启动整个服务程序的主线程
		        sf=new ServerFrame(this);
			    sf.setVisible(true);
			    sf.setText("Server starting....");
			}
			catch(ClassNotFoundException ce)
			{
				sf.setText("Exception in sql driver");
			}
			catch(SocketException se)
			{
				se.printStackTrace();
				new WarmDialog("111","Exception in register socket");
			}
	       grch = new GroupChat();//启动群聊线程,监听用户的登录请求
	      
	}
	
    /**
     * 这个方法是主线程的方法
     * 用来接收识别用户发来的信息,并做出相应的处理,处理完后再发消息给用户来确认
     * 原理是通过接收用户发来的一个字符串,并把字符串分离,取出数据,然后再根据取出
     * 的数据做出相应的处理,如登录,注册等.
     */	
	public void run()
	{
		try
		{
			while(!stop)
			{
				byte[] by=new byte[800];
				DatagramPacket packet=new DatagramPacket(by,by.length);
				socket.receive(packet);//接收用户的信息
				String str=new String(packet.getData(),0,packet.getLength());//将其转化为字符串
				ad=packet.getAddress();//取得用户的IP地址
				int port=packet.getPort();//取得用户的端口号
				StringTokenizer st=new StringTokenizer(str,"#");//分离用户发来的信息
				String sign=st.nextToken();//取得标识(根据这个做出处理)
				String content=st.nextToken();//分离出用户个人信息
				if("REGISTER".equals(sign))//如果标识是REGISTER则是用户登录
				{
					SaveClient(content);//调用此方法将用户个人信息存入数据库
					byte[] send=sure.getBytes();
					DatagramPacket sen=new DatagramPacket(send,send.length,ad,port);
					socket.send(sen);//向用户发送注册成功信息
					sf.setText("a new client registered!");
				}
				else if("LOGIN".equals(sign))//如果标识是LOGIN则是用户登录
				{
					quited=false;
					String s=null;
					if((s=CheckInformation(content))!=null)//检测用户是否为合法用户,如果是则将其信息存入数据库并发回确认信息
					{
						byte[] se=("OK"+s).getBytes();
						DatagramPacket pa=new DatagramPacket(se,se.length,ad,port);
						socket.send(pa);//如果用户为合法用户发"OK"给用户来确认...
						if(!"".equals(sendInfo))
						{
							grch.sm.sendString(sendInfo);//使群聊线程发信息给每一个在线用户
						}
					}
					else//如果说为非法用户,则发回登录失败信息
					{
						byte[] se=("WRONG").getBytes();
						DatagramPacket pa=new DatagramPacket(se,se.length,ad,port);
						socket.send(pa);
					}
				}
				
				else if("QUIT".equals(sign))//如果标识是QUIT,则表示用户下线,在数据库在线用户表中删除该用户,并发各在线用户发送信息
				{
					quited=true;//quited控制CheckInformation()方法的执行
					CheckInformation(content);	
					byte[] sen="QUIT".getBytes();
					DatagramPacket pac=new DatagramPacket(sen,sen.length,ad,port);
					socket.send(pac);
					sf.setText("success in sending .."+new String(pac.getData(),0,pac.getLength()));
					if(!"".equals(sendInfo))
					{
						grch.sm.sendString(sendInfo);//使群聊线程发信息给每一个在线用户
					}
				}
			}
		}
		catch(IOException ioe)
		{
			ioe.printStackTrace();
		}
		
	}
	/**
	 * 这个方法用来在在线用户表中找到被请求方IP地址
	 * @param clientname
	 * @return ip
	 */
	public String findIP(String clientname)
	{
		String ip="";
		String select="select IP from 在线记录表 where 用户名='"+clientname+"'";
		ResultSet rs;
		try
		{
			con =DriverManager.getConnection("jdbc:odbc:mysun","ily","8866");
			Statement sql=con.createStatement();
			sf.setText("This is findIP :"+clientname);
			rs=sql.executeQuery(select);
			if(rs.next())
			{
				ip=rs.getString(1);
				ip=ip.substring(1,ip.length());
				rs.close();
				sql.close();
			}
		}
		catch(SQLException se)
		{
			se.printStackTrace();
		}
		finally
		{
			try 
			{
				
				con.close();
			} 
			catch (SQLException e) 
			{
				e.printStackTrace();
			}
		}
		return ip;
	}
	
	/**
	 * 这个方法是用来处理用户注册的,通过接收一个字符串,然后分离字符串取得
	 * 用户的个人信息,并将其全部存入数据库中的注册表中
	 * @param sr
	 */

	public void SaveClient(String sr)
	{
		//boolean flag=false;
		StringTokenizer stk=new StringTokenizer(sr,",");
		//取得客户的注册信息并存入数据库中(表的结构为:职业,姓名,密码,账号),其中账号由服务器++生成
	    String work=stk.nextToken();
		String name=stk.nextToken();
		String password=stk.nextToken();
		int num=(int) (Math.random()*clientnum);//账号可能会有重复,以后要改成用注册的时期来做为账号就不会有重复的了
		String inserted="insert into 注册表 values('"+work+"','"+name+"','"+password+"',"+num+")";
		try
		{
			con =DriverManager.getConnection("jdbc:odbc:mysun","ily","8866");
			Statement sql=con.createStatement();
			sql.executeUpdate(inserted);
			sure="OK";//如果存入注册信息成功则给sure="OK";
			sf.setText("success  inserted  "+num);
			sql.close();
			//flag=true;
		}
		catch(SQLException se)
		{
			sure="WRONG";//存入不成功则sure="WRONG";
			sf.setText("exception in inserting information!!");
			se.printStackTrace();
		}
		//return flag;
		finally
		{
			try 
			{
				con.close();//关闭数据库的连接
			} 
			catch (SQLException e) 
			{
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * 这个方法有两个作用
	 * (1) 用来检测登录的用户是不是合法用户,如果是则将其存入数据库中,并取得在线的用户的信息发送给此用户,如果不是则拒绝登录
	 * (2) 当在线用户下线时,从数据库的在线用户表中移除此用户
	 * @param received 存放用户的个人信息
	 * @return 返回一个字符串
	 */
	
	public String CheckInformation(String received)//返回在线用户人数
	{
		StringTokenizer stk=new StringTokenizer(received,",");
		int clientnum=0;
		String clinf="*";//将在线的用户信息全部放到这个字符串中发给用户
		ResultSet rs;
		try
		{
		  con =DriverManager.getConnection("jdbc:odbc:mysun","ily","8866");
		  Statement sql=con.createStatement();
		  if(!quited)
		  {	 
			   addname=stk.nextToken();
			   String cipher=stk.nextToken();
			   String select="select * from 注册表 where 姓名='"+addname+"' and 密码='"+cipher+"'";
			   rs=sql.executeQuery(select);
			   sf.setText("success  selected..  ");
			   if(rs.next())//如果rs.next()返回TRUE 则说呀用户为合法用户,否则为非法用户
			   {
				   if(rs.getString("密码").equals(cipher)&&rs.getString("姓名").equals(addname))//在数据库中提取信息检查用户是	                                                                    
				   {                                                                       //否则为合法用户
		              int num=rs.getInt("账号");
		              clinf=clinf+num+"#";
		              sendInfo="ADD^"+","+num+","+ad+","+addname;//给sendInfo赋值,也就是新登录用户的信息,用以发给在线的所有用户
		              String check="select 账号,IP,用户名 from 在线记录表 ";
		              ResultSet re=sql.executeQuery(check);
		              while(re.next())
		              {//获得在线用户的信息并存入clinf中,发送给登录的用户
		            	  clinf=clinf+re.getInt(1)+",";
		            	  clinf=clinf+re.getString(2 )+",";
		            	  clinf=clinf+re.getString(3)+"#";
		              }
			    	  String insert="insert into 在线记录表 values("+num+",'"+ad+"','"+addname+"')";
			    	  sql.executeUpdate(insert);
			    	  sql.close();
				   }
				   else 
				   {
					  System.out.println("密码或用户名错!");
				   }
			   }
			   else
			   {
				   clinf=null;//如果用户为非法用户则返回一个空的字符串
			   }
			}  
			else//如果quited为false则是用户下线,删除其在数据库的记录( 在线用户表)
			{
			   String name=stk.nextToken();
			   String select="select 账号 from 注册表 where 姓名='"+name+"'";
			   ResultSet r=sql.executeQuery(select);
			   if(r.next())
			   {   
			     int aount=r.getInt(1);
			     String delete="delete from 在线记录表 where 用户名='"+name+"'";
			     sql.executeUpdate(delete);//删除用户
			     sf.setText(name+" success in quiting");
			     sendInfo="REMOVE^"+aount;//取得其帐号
			   }
			  // r.close();
			   sql.close();
			}
		}
		catch(SQLException se)
		{
			System.out.println(se);
			sf.setText("exception in select clients information!!");
		}
		finally
		{
			try 
			{
				con.close();
			} 
			catch (SQLException e) 
			{
				e.printStackTrace();
			}
		}
		return clinf;
	}
	
	/**
	 * 这个线程类是用来处理用户群聊
	 * 利用了广播数据报接收所有用户发来的信息,并将其发送给所有用户,以达到一人发送消息,所有人都能收到.
	 * 除了能实现群聊功能外,还实现了给所有在线用户发送新登录或下线用户的消息
	 * @author 黄祖光
	 *
	 */
	
	private class GroupChat implements Runnable  //负责为客户提供群聊的类,开始....
	{
		String send;
		byte[] rec,sen;
		DatagramSocket resocket=null;
		boolean stop=false;
		Thread t=null;
		SendMessage sm=null;
		
		GroupChat()
		{
			rec=new byte[800];
			try 
			{
				resocket=new DatagramSocket(6688);
			} 
			catch (IOException ioe) 
			{
				ioe.printStackTrace();
				new WarmDialog("socket error!","");
			}
			sm=new SendMessage();
			sm.start();
			t=new Thread(this);
			t.start();
		}
		public void run()//利用这个线程来接收所有参加群聊的客户发来的信息
		{
			while(!stop)
			{
				try
				{
					DatagramPacket repacket=new DatagramPacket(rec,rec.length);
					resocket.receive(repacket);
					String se=new String(repacket.getData(),0,repacket.getLength());
					sf.setText("server received: "+se);
					StringTokenizer st=new StringTokenizer(se,"#");
					String s=st.nextToken();
					if("CHAT".equals(s))
					{	
					   send="CHAT^"+(st.nextToken()).trim();
					   isSend=true;//收到一个信息就让其为真,这样sendMessage就可以运行
					}
					else if("SINGLECHAT".equals(s))
					{
					   send="SINGLECHAT^"+st.nextToken();
					   isSend=true;
					}
				}
				catch(IOException e)
				{
					e.printStackTrace();
					new WarmDialog("error in receiving..","");
				}
			}
		}
		
		/**
		 * 利用这个内部类来将接收到的信息发给全部群聊的客户(也就是内部类的内部类)
		 * @author 55
		 *
		 */
		
		private class SendMessage extends Thread  
		{
		   MulticastSocket socket=null;
		   InetAddress address=null;
		   DatagramPacket packet=null;
		   int port=8866;
		   boolean stop=false;
		   String str;
		   SendMessage()
		   {
			 try
			 {
				 socket=new MulticastSocket(8866);
				 address=InetAddress.getByName("239.253.253.2");
			 }
			 catch (IOException e) 
			 {	
			   e.printStackTrace();
		     }
		   }
		   public void run()
		   {
			 while(!stop) 
			 {
			   try 
			   {
				 if(isSend)
			     {	
					byte[] by;
					by=send.getBytes();
				    packet=new DatagramPacket(by,by.length,address,port);
				    socket.send(packet);
				    sf.setText("groudchat sended:"+new String(packet.getData(),0,packet.getLength()));
				    isSend=false;//发完后就停
			     }
			    } 
			    catch (Exception e)
				{
					e.printStackTrace();
				}
			  }
		   }
		   
		   /**
		    * 这个方法用来给所有在线用户发送新登录用户的信息,其实也只是通过对两个变量赋值然后run()内的语句执行
		    * @param str
		    */
		   public void sendString(String str)
		   {
			   send=str;
			   isSend=true;
		   }
		}
	}
	
	public void close()
	{
	   quited=false;
	   stop=true;
	   System.exit(0);
	}
	
	public static void main(String[] args)
	{
		ChatServer server=new ChatServer();
	}
}

⌨️ 快捷键说明

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