我在写一个网络五子棋程序,服务器端设置了两个线程,都用DataInputStream和DataOutputStream数据留,一个用writeInt和readInt负责处理棋子信息,一个用writeUTF和readUTF来处理聊天信息,但我发现两个同时开启两个线程的话,数据接收就会很不准确....可以说基本就没收到过正确的,到底怎么回事啊??如果是线程设置的问题,请问怎么组织代码才合适呢??用两个内部类实现Runnable??
还有怎么样实现让连入的两个客户端分别使用不同颜色的棋子,并且当一个玩家没有落子时另一玩家不能落子呢??????急啊....

解决方案 »

  1.   

    一个流,你要传输不同的信息,你必须把信息作一下标识。比如第一个byte是1, 代表后面是棋子信息,是0为聊天信息
    当然,如果你用xml封装就更好了。建议,下子和聊天分开处理,2个流,各自调试和运行,互不干扰。类也分开。
      

  2.   

    首先提示你使用OOP思想中的多态性处理这些复杂的网络传输问题
    建立一个Message的父类,建议设置为abstrct类型,其中有一个虛函数叫AnswerMessage,表示解析这个消息的响应方法。
    继承两个子类,一个是ConnectMessage还有一个是ChessMessage,实现AnswerMessage方法,当然两种不同的消息对应不同的响应方法。
    A-B机通信
    A发送ConnectMessage的序列化对象给B(UDP或者TCP,参考:网络传输序列化对象)
    示例代码:
    ConnectMessage cm = new ConnectMessage(senderid, senderNickname, scretmsg);
        
            ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024);
            ObjectOutputStream os = null;
            byte sendBuf[] = (byte[])null;
            DatagramSocket ds = null;
            DatagramPacket dp = null;
            try
            {
                os = new ObjectOutputStream(byteStream);
                os.writeObject(cm);
                sendBuf = byteStream.toByteArray();
                ds = new DatagramSocket();
                dp = new DatagramPacket(sendBuf, sendBuf.length, InetAddress.getByName(ipAddress), portAddress);
                ds.send(dp);
                ds.close();
            }
            catch(IOException e)
            {
                e.printStackTrace();
            }
    //当然,你也可仿照这个ConnectMessage发包过程做出其他的MessageB接受到流以后反序列化:
    示例代码:
     ObjectInputStream s = null;
            Message msg = null;
            do
            {
                try
                {
                    byte buf[] = new byte[1024];
                    DatagramPacket dpReceive = new DatagramPacket(buf, 1024);
                    dsReceive.receive(dpReceive);
                    s = new ObjectInputStream(new ByteArrayInputStream(dpReceive.getData()));
                    msg = (Message)s.readObject();
                }
                catch(Exception e)
                {
                    e.printStackTrace();
                }
                AnswerMessage(msg);
            } while(true);
                private void AnswerMessage(Message msg)
        {
            msg.AnswerMessage();
        }这样做的好处在于你以后可以扩展出很多Message的子类,而不必到处修改源代码,因为所有Message的解析语句都是一个私有函数:AnswerMessage(msg)来处理,这个函数只是调用各种Message的AnswerMessage()方法,所以以后添加子Message类的时候,只需给每个Message写上响应的AnswerMessage()方法即可。这就是程序的多态性。建议你采用这样的方法,而不是多个线程同时监听的方法来处理传输流,这种方法只需要一个TCP或者UDP线程来循环监听,而且使用对象序列化的技术来封装所传输的流,保证了流的互不干扰性
      

  3.   

    你如何知道应该是readInt还是readUTF呢?需要设计一个简单的协议吧
      

  4.   

    谢谢上面的各位,下面是我的服务器端代码(已经实现了棋子同步显示):
    有两个私有类实现接口Runnable...注释掉的部分是用于聊天线程,如果把注释去掉后就会出现混乱,连下棋的线程都不行了....因为要交上去了很急啊...帮忙改改的高手高分相送
    public class GobangServer extends JFrame
    {
    private ServerSocket serversocket;
    private boolean isStart = false;
    private JTextArea 服务器 = new JTextArea(); 
    private JButton 清空=new JButton("清空服务器");
    private JButton 信息=new JButton("服务器信息");
    private int index = 0; //ArrayList<Client> clients = new ArrayList<Client>();
    Client[] clients=new Client[2];
    //Chat[] chats=new Chat[2];

            //服务器界面 \
            public GobangServer(String title)
    {
    this.setLayout(null);
    Container content=this.getContentPane();
    服务器.setEditable(false);
    服务器.setBackground(Color.BLACK);
    服务器.setForeground(Color.WHITE);
    服务器.setBounds(0,0,280,210);
    this.add(服务器);

    清空.addActionListener(new ButtonAction());
    清空.setBounds(150,215,100,25);
    this.add(清空);
    信息.addActionListener(new ButtonAction());
    信息.setBounds(20,215,100,25);
    this.add(信息);

    this.setResizable(false);
    this.setSize(280,280);
    this.setLocation(400,200);
    this.setTitle(title);
    this.setVisible(true);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    serverMonitor();
    }

            //接收客户端socket
    public void serverMonitor()
    {
    try 
    {
    serversocket = new ServerSocket(9222,2);
    isStart = true;
    }
    catch (IOException e) 
    { e.printStackTrace();
    } try
    {
                           //启动两个线程
    while(isStart)
    {
    Socket socket = serversocket.accept();
    Client c = new Client(socket,index);
    //clients.add(c);
    clients[index]=c;
    //Chat ch=new Chat(socket);
    //chats[index]=ch;
    index ++;
    服务器.append(socket.getInetAddress().getHostAddress()+"连接到"+index+"玩家\n");
    new Thread(c).start();
    //new Thread(ch).start();
    }
    }
    catch(IOException e)
    {
    e.printStackTrace();
    }
    finally
    {
    try
    {
    serversocket.close();

    catch (IOException e) 
    { e.printStackTrace();
    }
    } }

    public static void main(String args[])
    {
    GobangServer Server = new GobangServer("五子棋服务器");
    } private class ButtonAction implements ActionListener
    {
    public void actionPerformed(ActionEvent e) 
    {
    if(e.getSource()==清空)
    {
    服务器.setText("");
    }
    if(e.getSource()==信息)
    {
    try 
    {
    服务器.setText("服务器开始信息:\n"
    + "IP:"+InetAddress.getLocalHost() + "\n"
    + "端口"+serversocket.getLocalPort() + "\n");

    catch (UnknownHostException e1) 
    {
    e1.printStackTrace();
    }
    }

    }

    }
            //聊天线程
            /*private class Chat implements Runnable
    {
    DataInputStream input = null;
    DataOutputStream output = null;
    boolean isStart = false;
    Socket clientsocket = null; Chat(Socket clientsocket)
    {
    this.clientsocket = clientsocket;
    try
    {
    input = new DataInputStream(clientsocket.getInputStream());
    output = new DataOutputStream(clientsocket.getOutputStream());
    }
    catch(IOException e)
    {
    e.printStackTrace();
    } isStart = true;
    }
    public void sendToClient(String str)
    {
    try
    {
    output.writeUTF(str);
    output.flush(); }
    catch(IOException e)
    {
    //网络面板.textarea1.append(clientsocket.getInetAddress().getHostAddress()+" exited \n");
    }
    }
    public void run() 
    {
    try
    {
    while(isStart)
    {
    String str = input.readUTF();
    System.out.println(str);
    for(int i=0;i<chats.length;i++)
    {
    Chat ch = chats[i];
    ch.sendToClient(str);
    }
    }
    }
    catch(EOFException e)
    {
    chats[index]=null;
    //clients.remove(this);
    System.out.println("client closed");
    }
    catch(SocketException e)
    {
    System.out.println("client closed");
    }
    catch(IOException e)
    {
    e.printStackTrace();
    }
    finally
    {
    try
    {
    if(clientsocket != null) clientsocket.close();
    if( input!= null) input.close();
    if(output != null) output.close();
    }
    catch(IOException e)
    {
    e.printStackTrace();
    }
    } }
    }*/

            //棋子传输
    private class Client implements Runnable
    {

    DataInputStream in = null;
    DataOutputStream out = null; Socket socket = null;
    boolean isStart = false;

            int serverinfo;
    int clientinfo;
    int numX;
    int numY;
    char startcolor;
    int color;
    int curplayer;
    int[][] chesse=new int[15][15];


    Client(Socket socket,int num)
    {
                            //决定先手颜色,第一个连接进入的服务器是a,通过writeChar传递给客户端,客户端转为1代表黑
    startcolor=(num == 1 ? 'a' : 'b');
    this.socket = socket;
    try
    {
    in = new DataInputStream(socket.getInputStream());
    out = new DataOutputStream(socket.getOutputStream());
    }
    catch(IOException e)
    {
    e.printStackTrace();
    }
    isStart = true;
    }

    public synchronized void sendToEveryClient(int clientinfo)
    {
    try
    {
    out.writeInt(clientinfo);
    }
    catch(IOException e)
    {
    clients[index]=null;
    if(index>0)
    index--;
    else index=0;
    服务器.append(socket.getInetAddress().getHostAddress()+" 退出\n");
    }
    }

    public void run() 
    {
    try
    {
    out.writeChar(startcolor);

    while(isStart)
    {
    clientinfo = in.readInt();
    if(clientinfo < 2240)
    {
                                                /分解从客户端传上来的代表棋盘上棋子的二维数组以及棋子颜色,传递c公式是1000*color+row*15+col
    numX=clientinfo%1000/15;
    numY=clientinfo%1000%15;
    color=clientinfo/1000;
    chesse[numX][numY]=color;
    }

    for(int i=0;i<clients.length/*.size()*/;i++)
    {
    Client client = clients[i];//clients.get(i);
    client.sendToEveryClient(clientinfo);//str);
    }
    }
    }
    catch(EOFException e)
    {
    clients[index]=null; 服务器.append(socket.getInetAddress().getHostAddress()+"退出\n");
    }
    catch(SocketException e)
    {
    服务器.append(socket.getInetAddress().getHostAddress()+"退出\n");
    }
    catch(IOException e)
    {
    e.printStackTrace();
    }
    finally
    {
    try
    {
    if(socket != null) socket.close();
    if(in != null) in.close();
    if(out != null) out.close();
    }
    catch(IOException e)
    {
    e.printStackTrace();
    }
    }
    }
    }
    }
      

  5.   

    4楼是高手,OO大牛啊。我想楼主可能想错了 ,流的那些方法不能区别出读的东西的~~~~他只是按照方法对字节进行区分。对象序列化用对象来区分不错,或者设置2个tcp连接吧 呵呵~~不知道对不对
      

  6.   

    我已经弄好聊天和下棋了,都用WRITEUTF()传递,再每个前面标记一个符号,服务器端判断是聊天或者是棋盘信息并调用相应方法处理