程序功能:
从Client发送一个字符串到Server, Server获取到消息后打印出来,然后返回一个字符串给Client端。
Client读取Server返回的字符串。我写了一个例子,但是运行不成功。 麻烦找出错误,或写一个例子给我。package com.zf.test05;import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;public class Client { public static void main(String[] args) throws Exception {

final String message = "client message";

Socket socket = new Socket("127.0.0.1" , 8888);
InputStream is = socket.getInputStream() ;
OutputStream os = socket.getOutputStream() ;

//send msg
os.write(message.getBytes());  
os.flush();
System.out.println("client:send message success!");

// receive msg
ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
byte[] buf = new byte[1024];
int len = -1;
while((len = is.read(buf)) != -1){
baos.write(buf , 0 , len);
}

byte[] receiveDatas = baos.toByteArray() ;

System.out.printf("client:message:%s%n" ,
new String(receiveDatas));      

socket.close();

}

}package com.zf.test05;import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;public class Server {
public static void main(String[] args) throws Exception {

final String message = "server message";

ServerSocket serverSocket = new ServerSocket(8888) ;

System.out.println("server start success...");

Socket client = serverSocket.accept(); InputStream is = client.getInputStream() ;
OutputStream os = client.getOutputStream() ; // receive msg
ByteArrayOutputStream baos = new ByteArrayOutputStream() ;
byte[] buf = new byte[1024];
int len = -1;
while((len = is.read(buf)) != -1){   
baos.write(buf , 0 , len);
baos.flush();
} byte[] receiveDatas = baos.toByteArray() ; System.out.printf("server:message:%s%n"
,new String(receiveDatas));     


//send msg
os.write(message.getBytes());
os.flush();
System.out.println("server:send message success...");

client.close();
serverSocket.close();
}}

解决方案 »

  1.   

    请问那两个while循环你准备怎么跳出?
      

  2.   


    (len = is.read(buf)) != -1
      

  3.   

    楼主将客户端和服务器端中的代码
         byte[] buf = new byte[1024];
            int len = -1;
            while((len = is.read(buf)) != -1){
                baos.write(buf , 0 , len);
            }
    改为
         byte[] buf = new byte[1024];
    int len = is.read();
          
            baos.write(buf , 0 , len);
         
    试一下
      

  4.   


    这样是不行的。 
     int len = is.read(); 
    is.read(); 只读了一个字节, len是这一个字节的指 ,并不是读取的length
    后面就不能用
     baos.write(buf , 0 , len);了
      

  5.   


    个人愚见:我认为这两个while是没法跳出循环的,在socket未关闭之前,inputStream会一直尝试读取socket中的数据
      

  6.   

    服务端开流的顺序不对,你客户端既然是先开输入流等待服务端返回数据了,你服务端就应该先开输出流Socket client = serverSocket.accept();           
    InputStream is = client.getInputStream();//更换下顺序        
    OutputStream os = client.getOutputStream();
    变成
    OutputStream os = client.getOutputStream() ;
    InputStream is = client.getInputStream() ;        
      

  7.   


    这样是不行的。 
     int len = is.read(); 
    is.read(); 只读了一个字节, len是这一个字节的指 ,并不是读取的length
    后面就不能用
     baos.write(buf , 0 , len);了is.read()是一个字节一个字节的读,并不是读一个字节吧
      

  8.   


    蹭1分,多个java版裤衩。
      

  9.   


    我把Client段改成
    Socket socket = new Socket("127.0.0.1" , 8888);
    OutputStream os = socket.getOutputStream() ;
    InputStream is = socket.getInputStream() ;
    还是在下面这一步阻塞
    while((len = is.read(buf)) != -1)
      

  10.   


    这样是不行的。 
     int len = is.read(); 
    is.read(); 只读了一个字节, len是这一个字节的指 ,并不是读取的length
    后面就不能用
     baos.write(buf , 0 , len);了is.read()是一个字节一个字节的读,并不是读一个字节吧read()方法调用一次,只读取一个字节。 如果需要把内容读完,就要循环的调用read()方法。
    read()方法的返回值是读取的这个字节的值。
    read(byte[])方法读取指定最大长度的字节。返回值是读取的长度。
      

  11.   


    我把Client段改成
    Socket socket = new Socket("127.0.0.1" , 8888);
    OutputStream os = socket.getOutputStream() ;
    InputStream is = socket.getInputStream() ;
    还是在下面这一步阻塞
    while((len = is.read(buf)) != -1)

    读写应该在不同的线程中进行。如果你非要写单线程,那读写都要在循环中进行。不然你就一直死在循环里了
      

  12.   

    我现在改了一下方式。
    发送和接收消息使用DataOutputStream 与 DataInputStream
    现在的思路是
    发送时:先将消息的长度使用short类型发送  然后接着发送消息内容
    接收时:先读取一个short类型的数据,然后创建指定大小的byte数组,然后用readFull()方法一次性将数组填满。
    现在测试是成功了。package com.zf.test05;import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.Socket;public class Client02 { public static void main(String[] args) throws Exception {

    final String message = "client message";

    Socket socket = new Socket("127.0.0.1" , 8888);
    InputStream is = socket.getInputStream() ;
    OutputStream os = socket.getOutputStream() ;
    DataInputStream dis = new DataInputStream(is);
    DataOutputStream dos = new DataOutputStream(os);

    //send msg
    dos.writeShort(message.length());  //write msg length  
    dos.write(message.getBytes());     // write msg content
    dos.flush();
    System.out.println("client:send message success!");

    // receive msg
    int length = dis.readShort(); //read msg length
    byte[] receiveDatas = new byte[length];
    dis.readFully(receiveDatas); //read full message

    System.out.printf("client:message:%s%n" ,
    new String(receiveDatas));      
    socket.close();

    }

    }
    package com.zf.test05;import java.io.DataInputStream;
    import java.io.DataOutputStream;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.ServerSocket;
    import java.net.Socket;public class Server02 {
    public static void main(String[] args) throws Exception { final String message = "server message"; ServerSocket serverSocket = new ServerSocket(8888) ; System.out.println("server start success..."); Socket client = serverSocket.accept(); InputStream is = client.getInputStream() ;
    OutputStream os = client.getOutputStream() ;
    DataInputStream dis = new DataInputStream(is);
    DataOutputStream dos = new DataOutputStream(os); // receive msg
    int length = dis.readShort(); //read msg length
    byte[] receiveDatas = new byte[length];
    dis.readFully(receiveDatas); //read full message System.out.printf("server:message:%s%n"
    ,new String(receiveDatas));     
    //send msg
    dos.writeShort(message.length());  //write msg length  
    dos.write(message.getBytes());     // write msg content
    dos.flush();
    System.out.println("server:send message success..."); client.close();
    serverSocket.close();
    }}
      

  13.   

    我想弄清楚,第一种方式为什么在while((len = is.read(buf)) != -1)时阻塞。
      

  14.   


    这样是不行的。 
     int len = is.read(); 
    is.read(); 只读了一个字节, len是这一个字节的指 ,并不是读取的length
    后面就不能用
     baos.write(buf , 0 , len);了is.read()是一个字节一个字节的读,并不是读一个字节吧read()方法调用一次,只读取一个字节。 如果需要把内容读完,就要循环的调用read()方法。
    read()方法的返回值是读取的这个字节的值。
    read(byte[])方法读取指定最大长度的字节。返回值是读取的长度。
    刚查了一下文档,本人确实错了!刚测试了下
    将is.read();改为is.read(buf)也能实现其功能。
      

  15.   


    我用了16楼所示的方式之后 测试成功。没有出现阻塞的情况了。
    Client与Server都是先开InputStream再开OutputStream。 
    可能不是跟开流的顺序由关吧。我调试的时候程序不是在getInputStream() ;或getOutputStream() ;时阻塞。
    而是在while((len = is.read(buf)) != -1)时阻塞。
    不清楚为什么会这样。
      

  16.   


    这样是不行的。 
     int len = is.read(); 
    is.read(); 只读了一个字节, len是这一个字节的指 ,并不是读取的length
    后面就不能用
     baos.write(buf , 0 , len);了is.read()是一个字节一个字节的读,并不是读一个字节吧read()方法调用一次,只读取一个字节。 如果需要把内容读完,就要循环的调用read()方法。
    read()方法的返回值是读取的这个字节的值。
    read(byte[])方法读取指定最大长度的字节。返回值是读取的长度。
    刚查了一下文档,本人确实错了!刚测试了下
    将is.read();改为is.read(buf)也能实现其功能。
      

  17.   

    仔细看API文档。read就是阻塞的,直到连接断开才会返回-1
      

  18.   


    为什么已经已经向OutputStream中写入了数据。 InputStream的read还是继续阻塞呢。 
      

  19.   


    倒霉孩子 细心点么  while都写错了,那样是出不来的..
    byte[] buf = new byte[1024];
            int len = 0;
            while((len = is.read(buf)) > 0){
                baos.write(buf , 0 , len);
            }
      

  20.   


    read方法一次都没返回,循环体没进去过。
      

  21.   


    倒霉孩子 细心点么  while都写错了,那样是出不来的..
    byte[] buf = new byte[1024];
            int len = 0;
            while((len = is.read(buf)) > 0){
                baos.write(buf , 0 , len);
            }> 0 与 != -1 在这里都一样的。
      

  22.   

    那个循环你这样的代码是跳不出来的,你把while改成if,你这个代码就能运行出你想要的结果!
      

  23.   

    把服务端的这段代码改一下 int len = -1;         
     while((len = is.read(buf)) != -1){  
         baos.write(buf , 0 , len);
         baos.flush();
        } 
     改成
    len = is.read(buf);
    if(len != -1){
    baos.write(buf , 0 , len);
    baos.flush();

    }看看能不能出效果
      

  24.   


    倒霉孩子 细心点么  while都写错了,那样是出不来的..
    byte[] buf = new byte[1024];
            int len = 0;
            while((len = is.read(buf)) > 0){
                baos.write(buf , 0 , len);
            }> 0 与 != -1 在这里都一样的。问题提的很尖锐..  其实这只是为了避免你上班电影的buf出错的情况..  
    假如你一不小心把byte[] buf = new byte[1024];  写成byte[] buf = new byte[0];你就知道作用了
      

  25.   


    倒霉孩子 细心点么  while都写错了,那样是出不来的..
    byte[] buf = new byte[1024];
            int len = 0;
            while((len = is.read(buf)) > 0){
                baos.write(buf , 0 , len);
            }> 0 与 != -1 在这里都一样的。问题提的很尖锐..  其实这只是为了避免你上班电影的buf出错的情况..  
    假如你一不小心把byte[] buf = new byte[1024];  写成byte[] buf = new byte[0];你就知道作用了不好意思上边错了几个字:应该是上边定义
      

  26.   

    我猜客户端这句:client:send message success! 打印出来,然后就卡死了
      

  27.   

    你看看CPU占用,其实应该不是阻塞
      

  28.   

    只能用异步NIO实现了,mina/netty之类
      

  29.   

    问题解决。
    将while的条件改为while(is.available() > 0 && (len = is.read(buf)) != -1)
    读取之前先用available()获取可读的字节数(该方法可以不阻塞的获取可读字节数),问题就解决了。分析原因就是因为is.read(buf)阻塞,如果将inputstream里面的内容读完了,再次调用read方法时,将会一直阻塞,并不是返回-1,直到与该inputstream对应的outputstream关闭才会返回-1。
    可能从Socekt获取的Inputstream与FileInputStream不同,FileInputStream在读完文件后就再次读就返回-1.
    但是Socekt获取的InputStream。要等另一端的OutputStream关闭它才返回-1 否则一直阻塞。
      

  30.   

    这样也不完整。如果你的网络条件很差,数据没法一次性到达对方缓存,is.available()在读完第一次缓存之后会返回false,这样你就有可能收到半个消息。要建立健全的TCP客户端/服务器,不简单的
      

  31.   


    如你所料,还是有问题。
    我将缓冲区该成一个字节byte[] buf = new byte[1];
    结果打印的结果,只读取了一个字符。
      

  32.   

    public class TCPServer {
      private static AtomicInteger iID = new AtomicInteger(0);  private ExecutorService iExecutor;  private ServerSocketChannel iServerChannel;  private boolean iRunning = false;  private Thread iServerThread;  private IClientHandlerFactory iClientHandlerFactory;  private int iHeaderSize;  private IMessageLengthParser iMessageLengthParser;  private int iPort;  private IServerEventListener iServerEventListener;  private Logger iLogger;  public TCPServer(int port, IClientHandlerFactory clientHandlerFactory, int headerSize,
                       IMessageLengthParser messageLengthParser, Logger logger) {
        iPort = port;
        iLogger = logger.getClone("TCPServer Port:" + port);
        iExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
          @Override
          public Thread newThread(Runnable runnable) {
            return new Thread(runnable, "server-listener-thread-" + iID.incrementAndGet());
          }
        });    iClientHandlerFactory = clientHandlerFactory;    iHeaderSize = headerSize;
        iMessageLengthParser = messageLengthParser;    try {
          iServerChannel = ServerSocketChannel.open();
          iServerChannel.socket().bind(new InetSocketAddress(port));
        }
        catch (IOException e) {
          throw new RuntimeException("Cannot create simulator server");
        }
      }  public void setServerEventListener(IServerEventListener listener) {
        iServerEventListener = listener;
      }  public void start() {
        if (iRunning)
          return;    iLogger.info("Starting TCP Server on port " + iPort + "...");    iRunning = true;    iExecutor.execute(new Runnable() {
          @Override
          public void run() {
            iServerThread = Thread.currentThread();
            while (iRunning) {
              try {
                iLogger.info("Server started on port: " + iPort);            SocketChannel clientChannel = iServerChannel.accept();            if (iServerEventListener != null)
                  iServerEventListener.onClientConnected(clientChannel);            iLogger.info("Client connected to port: " + iPort);
                IClientHandler clientHandler = iClientHandlerFactory.getClientHandler(clientChannel);
                SocketReader reader = new SocketReader(clientHandler, clientChannel, iHeaderSize, iMessageLengthParser,
                        iServerEventListener, iLogger);
                reader.start();
              }
              catch (IOException e) {
                iLogger.error("Error waiting for connection", e);
                if (iServerEventListener != null)
                  iServerEventListener.onServerDown();
                break;
              }
            }
            iLogger.info("TCP server on port " + iPort + " stopped");
          }
        });
      }  public void stop() {
        iLogger.info("Stopping TCP server on port " + iPort);
        iRunning = false;
        if (iServerThread != null)
          iServerThread.interrupt();
      }  private static class SocketReader implements Runnable {
        private static final int INITIAL_BUFFER_SIZE = 5000;    private final IServerEventListener iServerListener;    private Logger iLogger;    private final SocketChannel iSocketChannel;    private int iHeaderSize;    private IMessageLengthParser iMessageLengthParser;    private ITCPConnectionListener iClientListener;    private ByteBuffer iMessageBuffer = ByteBuffer.allocate(INITIAL_BUFFER_SIZE);    private Thread iThread;    SocketReader(ITCPConnectionListener clientListener, SocketChannel socketChannel, int headerSize,
                     IMessageLengthParser messageLengthParser, IServerEventListener serverListener, Logger logger) {
          iClientListener = clientListener;
          iSocketChannel = socketChannel;
          iMessageBuffer.order(ByteOrder.LITTLE_ENDIAN);
          iHeaderSize = headerSize;
          iMessageLengthParser = messageLengthParser;
          iThread = new Thread(this, "socket-reader-thread");
          iServerListener = serverListener;
          iLogger = logger;
        }    public void start() {
          iLogger.info("Starting reader thread");
          iThread.start();
        }    public void stop() {
          try {
            if (iServerListener != null)
              iServerListener.onClientDisconnect(iSocketChannel);
            iSocketChannel.close();
          }
          catch (IOException e) {
            // ignore
          }
          iThread.interrupt();
        }    @Override
        public void run() {
          try {
            while (iSocketChannel.isOpen()) {
              if (iSocketChannel.read(iMessageBuffer) < 0)
                throw new EOFException("Remote initiated close.");          if (iMessageBuffer.position() < iHeaderSize)
                continue;          iMessageBuffer.flip();          int initialLimit = iMessageBuffer.limit();          int currentPos;
              for (int messageCount = 0; true; messageCount++) {
                iMessageBuffer.limit(initialLimit);
                currentPos = iMessageBuffer.position();            // buffer left does not contain a complete packet header
                if (currentPos + iHeaderSize > initialLimit)
                  break;            int messageLength = iMessageLengthParser.parseLength(currentPos, iMessageBuffer);            // Received incomplete message
                if (messageLength + currentPos > initialLimit) {
                  // buffer not big enough to hold a complete message
                  if (messageCount == 0 && messageLength + currentPos > iMessageBuffer.capacity()) {
                    ByteBuffer doubleSizedBuffer = ByteBuffer.allocate(iMessageBuffer.capacity() * 2);
                    doubleSizedBuffer.order(ByteOrder.LITTLE_ENDIAN);
                    iMessageBuffer.flip();
                    doubleSizedBuffer.put(iMessageBuffer);
                    iMessageBuffer = doubleSizedBuffer;
                  }
                  break;
                }            if (messageLength == 0) {
                  iLogger.error(SimulatorUtils.toHex(iMessageBuffer));
                }            iMessageBuffer.limit(currentPos + messageLength);            try {
                  iClientListener.onMessage(iMessageBuffer);
                }
                catch (Exception e) {
                  iLogger.error("Listener thrown unhandled exception.", e);
                  iMessageBuffer.clear();
                }
                iMessageBuffer.position(currentPos + messageLength);
              }          iMessageBuffer.limit(initialLimit);
              iMessageBuffer.compact();
            }
          }
          catch (IOException e) {
            iLogger.info("Client disconnection");
            iMessageBuffer.clear();
            stop();
          }
        }
      }
    }这是一个实际中使用的TCPServer范例。这个服务器对于每一个客户连接都开一条线程,通过ITCPConnectionListener来与外界交流。
    public interface ITCPConnectionListener {
      void onMessage(ByteBuffer buffer);  void onClose();  void onUnexpectedClose();}
    这里对于消息的假设是,客户端给服务器发送的消息都有一个消息头,消息头里面定义了这个消息的长度,由IMessageLengthParser来取得。而消息头的长度必须固定,仔细看构造函数的参数。
    public interface IMessageLengthParser {
      int parseLength(int offset, ByteBuffer buffer);
    }这些都在构造函数里面传入。如果你花两天时间把这段代码研究透彻,你就升级了。
      

  33.   

    还是建议楼主去学习NIO比较好 非阻塞套接字可以很好的解决楼主的问题 在实际应用中楼主的这种想法也不具备生产力