假设一个服务器程序,一个客户端程序。
根据课本上的知识,服务器程序代码如下:
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)聊天。下期介绍。