socket 阻塞原因详解

 

假设一个服务器程序,一个客户端程序。

 

根据课本上的知识,服务器程序代码如下:

 

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)聊天。下期介绍。

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值