假设一个服务器程序,一个客户端程序。
根据课本上的知识,服务器程序代码如下:
package com.server; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.ServerSocket; import java.net.Socket; public class Server { public static void main(String[] args) { new Server().run(); } public void run(){ try { ServerSocket serverSocket=new ServerSocket(9000); //得到客户端连接过来的socket Socket clientSocket=serverSocket.accept(); System.out.println("有客户端上钩了"); //拿到输入流 BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); //拿到输出流 PrintWriter printWriter=new PrintWriter(clientSocket.getOutputStream()); //拿到系统的输入流 BufferedReader sysBufferedReader=new BufferedReader(new InputStreamReader(System.in)); //在此就得打印一下从客服端收来的字符串 System.out.println("client says:"+bufferedReader.readLine());//阻塞 String contentFromSelf=sysBufferedReader.readLine();//阻塞 System.out.println("服务器由于自身读键盘输入数据而卡住"); while(!contentFromSelf.equals("bye")){ //将自己说的话放进socket中推给客户端 printWriter.println(contentFromSelf); //刷新,让客户端立即收到 printWriter.flush(); //将自己说的话显示在控制台 System.out.println("server says:"+contentFromSelf);//阻塞 //将客户端返回的话显示在下面 System.out.println("client says:"+bufferedReader.readLine());//阻塞 contentFromSelf=sysBufferedReader.readLine(); //可以在这里打印一句话来测试readLine是否阻塞 } clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } } }
客户端代码如下:
package com.client; import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.Socket; public class Client { public static void main(String[] args) { new Client().run(); } private void run(){ try { Socket socket=new Socket("127.0.0.1",9000); //拿到输入流 BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(socket.getInputStream())); //拿到输入流 PrintWriter printWriter=new PrintWriter(socket.getOutputStream()); //拿到系统的输入流 BufferedReader sysBufferedReader=new BufferedReader(new InputStreamReader(System.in)); String contentFromSelf=sysBufferedReader.readLine(); while(!contentFromSelf.equals("bye")){ //将自己说的话放进socket中推给服务端 printWriter.println(contentFromSelf); //刷新,让服务端立即收到 printWriter.flush(); //将自己说的话显示在控制台 System.out.println("client says:"+contentFromSelf); //将服务器返回的话显示在下面 System.out.println("server says:"+bufferedReader.readLine()); //客户端读下一句话 contentFromSelf=sysBufferedReader.readLine(); //可以在这里打印一句话来测试readLine是否阻塞 } }catch (Exception e) { e.printStackTrace(); } } }
这样展现出来的效果是:
1.客户端必须发送第一句话,然后服务器端才能收到。
2.接下来是服务端必须发送一句话,客户端才能收到
3.接下来又是客户端一句话,服务端才能收到
…………
有点像网游里面的回合制,实在是落后了。
原因解析:下面的bufferedReader.readLine方法是阻塞的,是在读管道里面的值。
BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
System.out.println("client says:"+bufferedReader.readLine());//阻塞
另外,下面这句话是从标准的键盘里面输入值,此方法也是阻塞的。contentFromSelf=sysBufferedReader.readLine();
解决办法:
1.将发送的过程专门放在一个线程里
2.将接收的过程专门放在一个线程里
整个代码如下:
1.Server.java
package com.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import com.util.Receiver;
import com.util.Sender;
public class Server {
public static void main(String[] args) {
new Server().run();
}
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(9000);
// 得到客户端连接过来的socket
Socket clientSocket = serverSocket.accept();
System.out.println("有客户端上钩了");
new Sender(clientSocket).start();
new Receiver(clientSocket).start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.Client.java
package com.client;
import java.net.Socket;
import com.util.Receiver;
import com.util.Sender;
public class Client {
public static void main(String[] args) {
new Client().run();
}
private void run() {
try {
Socket socket = new Socket("127.0.0.1", 9000);
new Sender(socket).start();
new Receiver(socket).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.Sender.java
package com.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Sender extends Thread {
private Socket socket;
public Sender(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 拿到输出流
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
// 拿到系统的输入流
BufferedReader sysBufferedReader = new BufferedReader(
new InputStreamReader(System.in));
String contentFromSelf = sysBufferedReader.readLine();// 阻塞
while (!contentFromSelf.equals("bye")) {
// 将自己说的话放进socket中推给客户端
printWriter.println(contentFromSelf);
// 刷新,让客户端立即收到
printWriter.flush();
// 将自己说的话显示在控制台
System.out.println("server says:" + contentFromSelf);// 阻塞
contentFromSelf = sysBufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.Receiver.java
package com.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class Sender extends Thread {
private Socket socket;
public Sender(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 拿到输出流
PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
// 拿到系统的输入流
BufferedReader sysBufferedReader = new BufferedReader(
new InputStreamReader(System.in));
String contentFromSelf = sysBufferedReader.readLine();// 阻塞
while (!contentFromSelf.equals("bye")) {
// 将自己说的话放进socket中推给客户端
printWriter.println(contentFromSelf);
// 刷新,让客户端立即收到
printWriter.flush();
// 将自己说的话显示在控制台
System.out.println("server says:" + contentFromSelf);// 阻塞
contentFromSelf = sysBufferedReader.readLine();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意:此服务端客户端程序只适用于一对一自由聊天,不能一(Server)对多(Client)聊天。下期介绍。