TCP通信学习 服务端没有响应

天空中的狗_ 2015-03-18 03:52:47
用Socket实现了TCP的聊天室的简单模型,
服务器端有两个类:TCPListener是用来监听客户端的连接请求的,并且新起一个线程用来不断的读取socket中的数据并且发送给所有已经建立连接的客户;TCPConnection就是这个读写的线程
客户端有两个类:TCPClient是用来发送连接请求的,并且监听键盘输入的聊天内容并写进socket,每次连接新起一个线程用来不断读取socket里返回的其他用户的聊天内容;TCPClientThread就是这个线程

先运行TCPListener后,再运行TCPClient,问题就是:一个客户端输入内容后,服务器端用来不断读取数据的线程并没有反映,也就是黑体的代码没有执行,其他客户端也没有收到服务器端返回的这个客户端的聊天信息。


TCPListener
public class TCPListener {

public static List<Socket> sockets = Collections.synchronizedList(new ArrayList<Socket>());

public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(3003);
while(true){
Socket socket = server.accept();
sockets.add(socket);
new Thread(new TCPConnection(socket)).start();
}
}
}

TCPConnection
public class TCPConnection implements Runnable{

private Socket socket = null;

private BufferedReader input = null;

public TCPConnection(Socket socket){
this.socket = socket;
try {
input = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

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

@Override
public void run() {
String content = null;
try {
while((content = (input.readLine())) != null){
System.out.println(“不断读取socket中的数据的这段代码没有被执行”);
for(Socket target : TCPListener.sockets){
BufferedWriter output = new BufferedWriter(new OutputStreamWriter(target.getOutputStream()));
output.write((content + " from server"));
System.out.println(content + " from server");
// output.flush();
}
}
} catch (IOException e) {
TCPListener.sockets.remove(socket);
System.out.println("A client has exited because of unexpected errors");
e.printStackTrace();
}
}
}

TCPClient
public class TCPClient {

public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 3003);
new Thread(new TCPClientThread(socket)).start();

PrintWriter writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
String content = null;
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
while((content = reader.readLine()) != null){
writer.println(content);
// writer.flush();
}

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

TCPClientThread
public class TCPClientThread implements Runnable{

public Socket socket = null;

public BufferedReader input = null;

public TCPClientThread(Socket socket){
this.socket = socket;
try {
input = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void run() {
try {
String line = null;
while((line = input.readLine()) != null){
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
...全文
303 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
天空中的狗_ 2015-03-23
  • 打赏
  • 举报
回复 1
业务逻辑就是这样的,具体的原因我也查到了,就是BufferedInputStream的readLine方法总是会去读取到\n才会停止,也就是说如果收到的内容里没有\n的话那么就会一直阻塞在读取数据中,换了个包装类或者给一个带\n的字符串,然后就可以了,谢谢大家,还是自己API的研读不够深刻,我再把这块儿细看一下。
天空中的狗_ 2015-03-23
  • 打赏
  • 举报
回复
引用 9 楼 u010005508 的回复:
[quote=引用 4 楼 u011351725 的回复:] [quote=引用 3 楼 u010005508 的回复:] 根据楼主代码稍加修改,详情见注释 .......... 我试了每次输出后都flush,但是还是在之前阻塞住了?你确定你只是把那两个注释行放开了? 我为什么还是阻塞呢
一个是注释放开,一个是服务器线程run()方法中那个BufferedWriter 改用PrintWriter了!我在添加的注释中写了啊[/quote] 是这样的,具体的原因我也查到了,就是BufferedInputStream的readLine方法总是会去读取到\n才会停止,也就是说如果收到的内容里没有\n的话那么就会一直阻塞在读取数据中,换了个包装类,然后就可以了,多谢!
skgary 2015-03-20
  • 打赏
  • 举报
回复
你客户端和服务器之间的交互规则是一个业务逻辑问题。 而阻塞,线程同步之类的,都是纯技术问题。 不要把这两者搞在一起。
mxway 2015-03-20
  • 打赏
  • 举报
回复
引用 5 楼 u011351725 的回复:
[quote=引用 1 楼 mxway 的回复:] 你的C/S通信模式是,当客户端连到服务器时,先接从客户端发送的数据,然后服务端程序将向所有连接到服务端的客户端发送数据。再看你的客户端
 Socket socket = new Socket("127.0.0.1", 3003);
            new Thread(new TCPClientThread(socket)).start();
TCPClientThread类的run
 String line = null;
            while((line = input.readLine()) != null){
                System.out.println(line);
            }
你的客户端连接到服务器后,还没有向服务器发送就想着要接收服务返回的东西了。由于你使用的socket阻塞模式编程,所以当客户端的run执行后,服务器一直等客户发数据,而客户端也一直在等着接收服务器的数据。这样就造成了死锁。
确实是这样的,造成死锁了。可以用同步来解决吗?还是说要用非阻塞socket通信?[/quote] 我觉得最好的办法就是使用非阻塞模式了。
天空中的狗_ 2015-03-20
  • 打赏
  • 举报
回复
引用 1 楼 mxway 的回复:
你的C/S通信模式是,当客户端连到服务器时,先接从客户端发送的数据,然后服务端程序将向所有连接到服务端的客户端发送数据。再看你的客户端
 Socket socket = new Socket("127.0.0.1", 3003);
            new Thread(new TCPClientThread(socket)).start();
TCPClientThread类的run
 String line = null;
            while((line = input.readLine()) != null){
                System.out.println(line);
            }
你的客户端连接到服务器后,还没有向服务器发送就想着要接收服务返回的东西了。由于你使用的socket阻塞模式编程,所以当客户端的run执行后,服务器一直等客户发数据,而客户端也一直在等着接收服务器的数据。这样就造成了死锁。
确实是这样的,造成死锁了。可以用同步来解决吗?还是说要用非阻塞socket通信?
天空中的狗_ 2015-03-20
  • 打赏
  • 举报
回复
引用 3 楼 u010005508 的回复:
根据楼主代码稍加修改,详情见注释

//TCPListener.java文件

import java.io.*;
import java.net.*;
import java.util.*;
public class TCPListener {
	
	public static List<Socket> sockets = Collections.synchronizedList(new ArrayList<Socket>());
	
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(3003);
		while(true){
			Socket socket = server.accept();
			sockets.add(socket);
			new Thread(new TCPConnection(socket)).start();
		}
	}
}

class TCPConnection implements Runnable{

	private Socket socket = null;
	
	private BufferedReader input = null;
	
	public TCPConnection(Socket socket){
		this.socket = socket;
		try {
			input = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		String content = null;
		try {
			//楼主之前没有响应,是因为客户端那边没有刷新缓冲区,这里就接收不到数据,程序会阻塞在这个地方,下面的代码也就无法执行
			while((content = (input.readLine())) != null){
				//System.out.println("不断读取socket中的数据的这段代码没有被执行");
				for(Socket target : TCPListener.sockets){
					PrintWriter writer = new PrintWriter(new OutputStreamWriter(target.getOutputStream()));//仿照客户端,改为PrintWriter输出
					writer.println((content + " from server"));
					//System.out.println(content + " from server");
					writer.flush();//这里也要刷新,不然客户端程序也读不到数据,客户端程序也同样会阻塞
				}
			}
		} catch (IOException e) {
			TCPListener.sockets.remove(socket);
			System.out.println("A client has exited because of unexpected errors");
			e.printStackTrace();
		}
	}
}

//TCPClient.java文件
import java.io.*;
import java.net.*;

public class TCPClient {

	public static void main(String[] args) {
		try {
			Socket socket = new Socket("127.0.0.1", 3003);
			new Thread(new TCPClientThread(socket)).start();

			PrintWriter writer = new PrintWriter(new OutputStreamWriter(
					socket.getOutputStream()));
			String content = null;
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					System.in));
			while ((content = reader.readLine()) != null) {
				writer.println(content);
				writer.flush();// 刷新缓冲区,不然服务器端接收不到数据,会导致其程序阻塞
			}

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

class TCPClientThread implements Runnable {

	public Socket socket = null;

	public BufferedReader input = null;

	public TCPClientThread(Socket socket) {
		this.socket = socket;
		try {
			input = new BufferedReader(new InputStreamReader(
					this.socket.getInputStream()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		try {
			String line = null;
			while ((line = input.readLine()) != null) {
				System.out.println(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
我试了每次输出后都flush,但是还是在之前阻塞住了?你确定你只是把那两个注释行放开了? 我为什么还是阻塞呢
highnewrain 2015-03-20
  • 打赏
  • 举报
回复
引用 4 楼 u011351725 的回复:
[quote=引用 3 楼 u010005508 的回复:] 根据楼主代码稍加修改,详情见注释

//TCPListener.java文件

import java.io.*;
import java.net.*;
import java.util.*;
public class TCPListener {
	
	public static List<Socket> sockets = Collections.synchronizedList(new ArrayList<Socket>());
	
	public static void main(String[] args) throws IOException {
		ServerSocket server = new ServerSocket(3003);
		while(true){
			Socket socket = server.accept();
			sockets.add(socket);
			new Thread(new TCPConnection(socket)).start();
		}
	}
}

class TCPConnection implements Runnable{

	private Socket socket = null;
	
	private BufferedReader input = null;
	
	public TCPConnection(Socket socket){
		this.socket = socket;
		try {
			input = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	public void run() {
		String content = null;
		try {
			//楼主之前没有响应,是因为客户端那边没有刷新缓冲区,这里就接收不到数据,程序会阻塞在这个地方,下面的代码也就无法执行
			while((content = (input.readLine())) != null){
				//System.out.println("不断读取socket中的数据的这段代码没有被执行");
				for(Socket target : TCPListener.sockets){
					PrintWriter writer = new PrintWriter(new OutputStreamWriter(target.getOutputStream()));//仿照客户端,改为PrintWriter输出
					writer.println((content + " from server"));
					//System.out.println(content + " from server");
					writer.flush();//这里也要刷新,不然客户端程序也读不到数据,客户端程序也同样会阻塞
				}
			}
		} catch (IOException e) {
			TCPListener.sockets.remove(socket);
			System.out.println("A client has exited because of unexpected errors");
			e.printStackTrace();
		}
	}
}

//TCPClient.java文件
import java.io.*;
import java.net.*;

public class TCPClient {

	public static void main(String[] args) {
		try {
			Socket socket = new Socket("127.0.0.1", 3003);
			new Thread(new TCPClientThread(socket)).start();

			PrintWriter writer = new PrintWriter(new OutputStreamWriter(
					socket.getOutputStream()));
			String content = null;
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					System.in));
			while ((content = reader.readLine()) != null) {
				writer.println(content);
				writer.flush();// 刷新缓冲区,不然服务器端接收不到数据,会导致其程序阻塞
			}

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

class TCPClientThread implements Runnable {

	public Socket socket = null;

	public BufferedReader input = null;

	public TCPClientThread(Socket socket) {
		this.socket = socket;
		try {
			input = new BufferedReader(new InputStreamReader(
					this.socket.getInputStream()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	@Override
	public void run() {
		try {
			String line = null;
			while ((line = input.readLine()) != null) {
				System.out.println(line);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
我试了每次输出后都flush,但是还是在之前阻塞住了?你确定你只是把那两个注释行放开了? 我为什么还是阻塞呢[/quote] 一个是注释放开,一个是服务器线程run()方法中那个BufferedWriter 改用PrintWriter了!我在添加的注释中写了啊
highnewrain 2015-03-20
  • 打赏
  • 举报
回复
楼主你首先得清楚你这个示例程序应该具有啥样的业务逻辑! 若你的想法就是服务器程序等待某个客户端程序发送数据之后,再将这个数据转发到其它客户端,那么代码按你原来那么写就行了,细节稍微修改就行了
highnewrain 2015-03-18
  • 打赏
  • 举报
回复
根据楼主代码稍加修改,详情见注释

//TCPListener.java文件

import java.io.*;
import java.net.*;
import java.util.*;
public class TCPListener {

public static List<Socket> sockets = Collections.synchronizedList(new ArrayList<Socket>());

public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(3003);
while(true){
Socket socket = server.accept();
sockets.add(socket);
new Thread(new TCPConnection(socket)).start();
}
}
}

class TCPConnection implements Runnable{

private Socket socket = null;

private BufferedReader input = null;

public TCPConnection(Socket socket){
this.socket = socket;
try {
input = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));

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

@Override
public void run() {
String content = null;
try {
//楼主之前没有响应,是因为客户端那边没有刷新缓冲区,这里就接收不到数据,程序会阻塞在这个地方,下面的代码也就无法执行
while((content = (input.readLine())) != null){
//System.out.println("不断读取socket中的数据的这段代码没有被执行");
for(Socket target : TCPListener.sockets){
PrintWriter writer = new PrintWriter(new OutputStreamWriter(target.getOutputStream()));//仿照客户端,改为PrintWriter输出
writer.println((content + " from server"));
//System.out.println(content + " from server");
writer.flush();//这里也要刷新,不然客户端程序也读不到数据,客户端程序也同样会阻塞
}
}
} catch (IOException e) {
TCPListener.sockets.remove(socket);
System.out.println("A client has exited because of unexpected errors");
e.printStackTrace();
}
}
}


//TCPClient.java文件
import java.io.*;
import java.net.*;

public class TCPClient {

public static void main(String[] args) {
try {
Socket socket = new Socket("127.0.0.1", 3003);
new Thread(new TCPClientThread(socket)).start();

PrintWriter writer = new PrintWriter(new OutputStreamWriter(
socket.getOutputStream()));
String content = null;
BufferedReader reader = new BufferedReader(new InputStreamReader(
System.in));
while ((content = reader.readLine()) != null) {
writer.println(content);
writer.flush();// 刷新缓冲区,不然服务器端接收不到数据,会导致其程序阻塞
}

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

class TCPClientThread implements Runnable {

public Socket socket = null;

public BufferedReader input = null;

public TCPClientThread(Socket socket) {
this.socket = socket;
try {
input = new BufferedReader(new InputStreamReader(
this.socket.getInputStream()));
} catch (IOException e) {
e.printStackTrace();
}
}

@Override
public void run() {
try {
String line = null;
while ((line = input.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

天空中的狗_ 2015-03-18
  • 打赏
  • 举报
回复
引用
由于你使用的socket阻塞模式编程,所以当客户端的run执行后,服务器一直等客户发数据,而客户端也一直在等着接收服务器的数据。这样就造成了死锁。
确实是这样的,造成死锁了。可以用同步来解决吗?还是说要用非阻塞socket通信?
mxway 2015-03-18
  • 打赏
  • 举报
回复
你的C/S通信模式是,当客户端连到服务器时,先接从客户端发送的数据,然后服务端程序将向所有连接到服务端的客户端发送数据。再看你的客户端
 Socket socket = new Socket("127.0.0.1", 3003);
            new Thread(new TCPClientThread(socket)).start();
TCPClientThread类的run
 String line = null;
            while((line = input.readLine()) != null){
                System.out.println(line);
            }
你的客户端连接到服务器后,还没有向服务器发送就想着要接收服务返回的东西了。由于你使用的socket阻塞模式编程,所以当客户端的run执行后,服务器一直等客户发数据,而客户端也一直在等着接收服务器的数据。这样就造成了死锁。
WebSocket客户端和服务端实例源码 WebSocket ws实例 HTML5 用java实现的服务端 Websocket与服务器的正常通信 众所周知,Web 应用的交互过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现,这种机制对于信息变化不是特别频繁的应用尚可,但对于实时要求高、海量并发的应用来说显得捉襟见肘,尤其在当前业界移动互联网蓬勃发展的趋势下,高并发与用户实时响应是 Web 应用经常面临的问题,比如金融证券的实时信息,Web 导航应用中的地理位置获取,社交网络的实时消息推送等。 传统的请求-响应模式的 Web 开发在处理此类业务场景时,通常采用实时通讯方案,常见的是: 轮询,原理简单易懂,就是客户端通过一定的时间间隔以频繁请求的方式向服务器发送请求,来保持客户端和服务器端的数据同步。问题很明显,当客户端以固定频率向服务器端发送请求时,服务器端的数据可能并没有更新,带来很多无谓请求,浪费带宽,效率低下。 基于 Flash,AdobeFlash 通过自己的 Socket 实现完成数据交换,再利用 Flash 暴露出相应的接口为 JavaScript 调用,从而达到实时传输目的。此方式比轮询要高效,且因为 Flash 安装率高,应用场景比较广泛,但在移动互联网终端上 Flash 的支持并不好。IOS 系统中没有 Flash 的存在,在 Android 中虽然有 Flash 的支持,但实际的使用效果差强人意,且对移动设备的硬件配置要求较高。2012 年 Adobe 官方宣布不再支持 Android4.1+系统,宣告了 Flash 在移动终端上的死亡。 从上文可以看出,传统 Web 模式在处理高并发及实时性需求的时候,会遇到难以逾越的瓶颈,我们需要一种高效节能的双向通信机制来保证数据的实时传输。在此背景下,基于 HTML5 规范的、有 Web TCP 之称的 WebSocket 应运而生。 早期 HTML5 并没有形成业界统一的规范,各个浏览器和应用服务器厂商有着各异的类似实现,如 IBM 的 MQTT,Comet 开源框架等,直到 2014 年,HTML5 在 IBM、微软、Google 等巨头的推动和协作下终于尘埃落地,正式从草案落实为实际标准规范,各个应用服务器及浏览器厂商逐步开始统一,在 JavaEE7 中也实现了 WebSocket 协议,从而无论是客户端还是服务端的 WebSocket 都已完备,读者可以查阅HTML5 规范,熟悉新的 HTML 协议规范及 WebSocket 支持。

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧