做了一个登陆系统。 服务器已做好  如图是一个登陆界面 无标题.png (27.88 KB)
 
其中我想实现这样一个功能: 当你输入你的昵称后 点击登陆 开始连接服务器 但是如果有别人用相同的昵称登陆服务器,服务器会向客户端发送一个消息"ReName"(重名)。客户端检测到这个信息后 系统会提醒 “你的昵称已被人使用!”要求重新输入新的昵称 如果检测到昵称没有被使用 则进入 登陆成功的界面。部分源码如下:
//实时接收服务器传来的消息
package CSsystem;import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;public class serverSocket extends Thread
{
    //OutputStream os=null;    //PrintStream ps=null;    InputStream is=null;    BufferedReader br=null;
    
    String serverInformation=null;
    
    boolean isHaveReName=false;
    
    Socket socket=null;
    
    public serverSocket(Socket socket)
    {
        try
        {
             this.socket=socket;
            
             is=socket.getInputStream();
             br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
             
            // os=socket.getOutputStream();
            // ps=new PrintStream(os);
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
    }
     
         
    public void run()
    {
        while(true)
        {
            try
            {
                serverInformation=getInformation();
                if(serverInformation.equals("ReName"))
                {
                    //System.out.println("收到重名信息!");
                    isHaveReName=true;
                                        
                }
                                            }
            catch(Exception ex)
            {
                ex.printStackTrace();
            }
            
            
        }
    }
    
    
    private String getInformation()
    {
        
        String str=null;
        try
        {
            str=br.readLine();
           
        }
        catch(Exception ex)
        {
            ex.printStackTrace();
        }
        return str;
    }
    
     
}点击登陆按钮时事件
/**
   登陆
**/
private void landingToServer()
{
     if(nameField.getText().trim().length()==0)
     {
         JOptionPane.showMessageDialog(null, "请输入你的昵称^_^!", "", 
                    JOptionPane.WARNING_MESSAGE); 
         return;
     }
     
     try
     {
         IP=serverIPAddrField.getText().trim();//这是要输入的服务器端 的IP地址
         
         sportNumber=Integer.parseInt(sportNumberField.getText().trim());//这是连接的端口号
         
         socket=new Socket();
         socket.connect(new InetSocketAddress(InetAddress.getByName(IP),sportNumber), 15*1000);
                  
         if(socket.isConnected())//如果连接服务器成功
         {
                        
           ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过) 
          ss.start();
           
           if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
           {
              
               JOptionPane.showMessageDialog(null, "你的昵称已被人使用!", "", 
                        JOptionPane.WARNING_MESSAGE);
           }
           else//否则进入登陆成功的界面
           {
               createChatPanel();
               resetPanel(chatPanel);
           }
                      
         }
         
         
     }catch()
{
   
}
经过测试 当如果重名的时候 服务器能向客户端发送"ReName"消息  客户端也能接收到该消息。我实现的方法是 在serverSocket类中定义一个boolean isHaveReName=false变量  当接收到"ReName"消息是说明有重名,此时isHaveReName=true;在登陆事件函数中通过检测 isHaveReName的值判断是否重名。  但是测试的时候isHaveReName的值总是false;  但是当接收到"ReName"消息时,在serverSocket类中打印该值的时候该值为true,正常。  为什么像我那样在外面定义一个serverSocket类的变量 然后打印该值时即使接收到了"ReName"消息 该值还是总是false呢? 很困惑 可能是我哪里犯糊涂了没想明白
求解答 

解决方案 »

  1.   

    问题就是在这几行
      1。ss.start();
       
      2。if(ss.isHaveReName==true)因为ss 是线程,1和2是并列进行的。1还没有修改isHaveReName=true的时候 ,2都执行了。所以一直都是false
      

  2.   

    貌似你这个想法就有问题
    不说你这个isHaveRename有没有必要  就算有的话该在客户端 
      

  3.   

    ss是个线程,跟你输入昵称的主线程是同时执行的,
    当ss.start()后,你不能假定一定是执行完ss.run()后才接着执行主线程后面的代码。
    举例来说明此时你的代码的实际执行顺序:
    ss.start()  -->  if(ss.isHaveReName==true)(主线程) --> if(serverInformation.equals("ReName")) isHaveReName=true(ss线程)这样看你应该很清楚是什么问题了
      

  4.   

      ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)  
      ss.start();
      ss.join(); //要等待线程执行完才能判断结果  
      if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
      

  5.   

    他的ss用了while(true)且没有跳出机制,是执行不完的
      

  6.   

    我实现的机制不是每点一次按钮都会生成一个无限循环线程
    当登陆成功的时候 用while(true)客户端一直监听从服务器传来的消息
    这个线程只负责接受消息
      

  7.   


    原因大致就是这样,主要是对线程的执行理解有点偏差
    当一个线程start后,它就脱离主线程独立运行了,你不能预期它的某条指令和主线程的某条指令的执行顺序。在这方面要做控制的话,就要用到线程类的其它方法了,你可以进一步地学习一下,根据实际需求灵活运用。
      

  8.   

    恩 我懂了。 问题也解决了
    ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)  
      ss.start();  Thread.sleep(xxx);//只要在这里让主线程睡眠会就可以了  if(ss.isHaveReName==true)//通过检测isHaveReName值判断是否重名
      {
        
      JOptionPane.showMessageDialog(null, "你的昵称已被人使用!", "",  
      JOptionPane.WARNING_MESSAGE);
      }
    谢谢各位了! 呵呵
      

  9.   

    ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)  
    你虽然定义为一个全局变量,但是你每次都是重新new一个,也就是说之前的那个还会存在,还会一直while循环,只是永远也不会收发消息了,即使socket关闭,你的try catch是在while里面,所以也不会退出while,这就是一个无限循环线程
    而且你这段代码是每点击一次执行一次,所以就会有很多这种无意义的线程存在。
    如果你希望只有一个线程,那么你也不必每次new一个socket,就用一个好了
    //按钮事件
    if (socket == null) { //socket是成员变量
      socket=new Socket();
      socket.connect(new InetSocketAddress(InetAddress.getByName(IP),sportNumber), 15*1000);
    }
    if(socket.isConnected())//如果连接服务器成功
      {
      if (ss == null) { //ss为成员变量,不必是全局
          ss=new serverSocket(socket);//ss为serverSocket类的全局变量(已在前面定义过)  
          ss.start();
       }
       ss.setFlag(false); //设置收信标志
        while (! ss.getFlag()) { //如果收信没结束,等待
            Thread.sleep(100);
        }
    ...//线程
    public class serverSocket extends Thread
    {
      boolean runnning = true; //结束标志
      boolean flag = true; //收信标志
      InputStream is=null;  BufferedReader br=null;
        
      String serverInformation=null;
        
      boolean isHaveReName=false;
        
      Socket socket=null;
        
      public serverSocket(Socket socket)
      {
      try
      {
      this.socket=socket;
        
      is=socket.getInputStream();
      br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
        
      // os=socket.getOutputStream();
      // ps=new PrintStream(os);
      }
      catch(Exception ex)
      {
      ex.printStackTrace();
      }
      }  public void run()
      {
      while(running) //这样好控制线程结束
      {
      try
      {
      serverInformation=getInformation();
      flag = true; //接受完消息改变flag
      if(serverInformation.equals("ReName"))
      {
      //System.out.println("收到重名信息!");
      isHaveReName=true;
        
      }
      }
      catch(Exception ex)
      {
      ex.printStackTrace();
      }
        
        
      }
      }  public boolean getFlag() {return flag;}
      public void setFlag(boolean b) {flag = b;}
      public boolean isRunning() {return running;}
      public void setRunning(boolean b) {running=b;}
      

  10.   

    你用Thread.sleep(xxx)的方式也不能完全保证每次都能正确的,因为线程的执行是不确定的,也就是说可能sleep很短时间就可以,可能sleep很长时间也不一定行,所以这个xxx你不好把握的,最好的方式还是设置标志
      

  11.   

    sleep只是暂时的。 它不能完全保证是正确的。10楼写的挺好的,顶一下~
      

  12.   

    你说的很对,“用Thread.sleep(xxx)的方式也不能完全保证每次都能正确的,因为线程的执行是不确定的,也就是说可能sleep很短时间就可以,可能sleep很长时间也不一定行,所以这个xxx你不好把握的,最好的方式还是设置标志

    试了你的方法的确很不错 呵呵~  不过用While(true)是必须的 因为当登陆成功之后就会产生一个客户端线程,这个线程必须要实时接收服务器的信息,所有要用While(true)一直监听。 而且只能登陆一次,并不要多次点击登陆按钮啊。只要登陆成功就会跳转到另一个界面,就像QQ登陆一样的。