项目场景:
模拟TCP写聊天程序,客户端对象有两个方法,speak和listen;服务端对象也有两个方法,speak和listen。当客户端键盘输入"bye"时,表示客户端不再speak,服务端收到客户端的"bye"后,服务端也不再listen。服务端键盘输入"bye"时,表示服务端不再speak,客户端收到客户端的"bye"后,客户端也不再listen。总之,客户端先说bye,服务端回复bye,双方通讯结束。- 附上问题代码如下:
- 客户端聊天者的类:
package com.kuang.net.shangxuetang.chat;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ClientChatter {
private String ip;
private int speakPort;
private int listenPort;
private int toPort;
ServerSocket serverSocket;
Socket socket = null;
public ClientChatter(String ip, int toPort) throws IOException {
this.ip = ip;
this.toPort = toPort;
socket = new Socket(InetAddress.getByName(ip), toPort);
}
public void speak() throws IOException {
OutputStream outputStream = socket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
while (true){
String s = bufferedReader.readLine();
outputStream.write(s.getBytes());
if("bye".equals(s)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bufferedReader!=null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void listen() throws IOException {
byte[] bytes = new byte[1024];
int temp=0;
String content="";
InputStream inputStream = socket.getInputStream();
try {
while (true){
if((temp=inputStream.read(bytes))!=-1){
content=new String(bytes,0,temp);
System.out.println("服务端说:"+content);
}
if("bye".equals(content)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 服务端聊天者的类:
package com.kuang.net.shangxuetang.chat;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class ServerChatter {
private int listenPort;
ServerSocket serverSocket;
Socket accept = null;
public ServerChatter(int listenPort) throws IOException {
this.listenPort = listenPort;
serverSocket = new ServerSocket(listenPort);
accept = serverSocket.accept();
}
public void speak() throws IOException {
OutputStream outputStream = accept.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
while (true){
String s = bufferedReader.readLine();
outputStream.write(s.getBytes());
if("bye".equals(s)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bufferedReader!=null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void listen() throws IOException {
byte[] bytes = new byte[1024];
int temp=0;
String content="";
InputStream inputStream = accept.getInputStream();
try {
while (true){
if((temp=inputStream.read(bytes))!=-1){
content=new String(bytes,0,temp);
System.out.println("客户端说:"+content);
}
if("bye".equals(content)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 创建客户端聊天者对象,其两个线程,一个听一个说:
package com.kuang.net.shangxuetang.chat;
import java.io.IOException;
public class TestChatterClient {
public static void main(String[] args) throws IOException {
ClientChatter clientChatter = new ClientChatter("127.0.0.1",1000);
new Thread(()->{
try {
clientChatter.speak();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
clientChatter.listen();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
- 创建服务端聊天者对象,其两个线程,一个听一个说:
package com.kuang.net.shangxuetang.chat;
import java.io.IOException;
public class TestChatterServer {
public static void main(String[] args) throws IOException {
ServerChatter serverChatter = new ServerChatter(1000);
new Thread(()->{
try {
serverChatter.speak();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
new Thread(()->{
try {
serverChatter.listen();
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
问题描述:
当客户端键盘输入"bye"时,服务端能收到"bye",但是客户端的listen方法报错Socket closed ![在这里插入图片描述](https://img-blog.csdnimg.cn/20210308214524240.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NhdF8yX2xvdmVfZG9n,size_16,color_FFFFFF,t_70)原因分析:
多次尝试后发现,把speak和listen方法里finally里的关闭流注释掉,就不报错了。
经过查询得知,Java的socket是一个全双工套接字,任何的输入流或输出流的close()都会造成Socket关闭。
解决方案:
将socket的输入输出流提到对象中作为参数,等speak和listen线程都结束后,在主函数里关闭流。
等speak和listen线程都结束后,在主函数里关闭流可以用CountDownLatch实现。具体代码如下:
- 客户端聊天者的类:
package com.kuang.net.shangxuetang.chat;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class ClientChatter {
private String ip;
private int toPort;
ServerSocket serverSocket;
Socket socket = null;
InputStream inputStream = null;
OutputStream outputStream = null;
public ClientChatter(String ip, int toPort) throws IOException {
this.ip = ip;
this.toPort = toPort;
socket = new Socket(InetAddress.getByName(ip), toPort);
inputStream = socket.getInputStream();
outputStream = socket.getOutputStream();
}
public InputStream getInputStream() {
return inputStream;
}
public OutputStream getOutputStream() {
return outputStream;
}
public Socket getSocket() {
return socket;
}
public void speak() throws IOException {
OutputStream outputStream = socket.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
while (true){
String s = bufferedReader.readLine();
outputStream.write(s.getBytes());
if("bye".equals(s)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
/* if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
if(bufferedReader!=null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void listen() throws IOException {
byte[] bytes = new byte[1024];
int temp=0;
String content="";
InputStream inputStream = socket.getInputStream();
try {
while (true){
if((temp=inputStream.read(bytes))!=-1){
content=new String(bytes,0,temp);
System.out.println("服务端说:"+content);
}
if("bye".equals(content)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
/* if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
}
}
}
- 服务端聊天者的类:
package com.kuang.net.shangxuetang.chat;
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
public class ServerChatter {
private int listenPort;
ServerSocket serverSocket;
Socket accept = null;
InputStream inputStream = null;
OutputStream outputStream = null;
public ServerChatter(int listenPort) throws IOException {
this.listenPort = listenPort;
serverSocket = new ServerSocket(listenPort);
accept = serverSocket.accept();
inputStream = accept.getInputStream();
outputStream = accept.getOutputStream();
}
public InputStream getInputStream() {
return inputStream;
}
public OutputStream getOutputStream() {
return outputStream;
}
public void speak() throws IOException {
OutputStream outputStream = accept.getOutputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
try {
while (true){
String s = bufferedReader.readLine();
outputStream.write(s.getBytes());
if("bye".equals(s)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
/* if(outputStream!=null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
if(bufferedReader!=null){
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public void listen() throws IOException {
byte[] bytes = new byte[1024];
int temp=0;
String content="";
InputStream inputStream = accept.getInputStream();
try {
while (true){
if((temp=inputStream.read(bytes))!=-1){
content=new String(bytes,0,temp);
System.out.println("客户端说:"+content);
}
if("bye".equals(content)){
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
/* if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}*/
}
}
}
- 创建客户端聊天者对象,其两个线程,一个听一个说:
package com.kuang.net.shangxuetang.chat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.CountDownLatch;
public class TestChatterServer {
public static void main(String[] args) throws IOException {
ServerChatter serverChatter = new ServerChatter(1000);
CountDownLatch countDownLatch = new CountDownLatch(2);
try {
new Thread(()->{
try {
serverChatter.speak();
} catch (IOException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}).start();
new Thread(()->{
try {
serverChatter.listen();
} catch (IOException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}).start();
try {
//等待,直到计数器归零后再向下执行
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
InputStream inputStream = serverChatter.getInputStream();
OutputStream outputStream = serverChatter.getOutputStream();
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
- 创建服务户端聊天者对象,其两个线程,一个听一个说:
package com.kuang.net.shangxuetang.chat;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
public class TestChatterClient {
public static void main(String[] args) throws IOException {
ClientChatter clientChatter = new ClientChatter("127.0.0.1",1000);
CountDownLatch countDownLatch = new CountDownLatch(2);
try {
new Thread(()->{
try {
clientChatter.speak();
} catch (IOException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}).start();
new Thread(()->{
try {
clientChatter.listen();
} catch (IOException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}).start();
try {
//等待,直到计数器归零后再向下执行
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
OutputStream outputStream = clientChatter.getOutputStream();
InputStream inputStream = clientChatter.getInputStream();
Socket socket = clientChatter.getSocket();
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket != null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}