下面分别是服务端和客户端的代码....
客户端代码:
package cn.dzr.tcp.Client;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class ClientTcp
{
public static void main(String[] args) throws IOException
{
System.out.println("客服端启动,准备传输数据....");
//1,创建Socket
int port = 10010; //设定端口号
InetAddress address = InetAddress.getLocalHost();
Socket socket = new Socket(address,port);
//2,获取Socket中的数据
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
//选择需要传输的文件.
File file = new File("D:+"+File.separator+"Client/ke.jpg");
//将文件进行传输
//1,将文件内容转为文件流,先是传输到一个字节数组中。
//2,将字节数组中的内容传输到out中。
byte[] buff = new byte[1024];
FileInputStream fis = new FileInputStream(file);
int len = 0;
while((len =fis.read(buff))!=-1)
{
out.write(buff, 0, len);
}
System.out.println("文件传输完毕....");
byte[] bufin = new byte[1024];
int reclen = in.read(bufin);
String str = new String(bufin,0,reclen);
System.out.println("服务端传来的消息:"+str);
fis.close();
socket.close();
}
}
服务端的代码如下:
package cn.dzr.tcp.Server;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerTcp
{
public static void main(String[] args) throws IOException
{
System.out.println("服务端启动....准备接受数据.");
//创建ServSocket,
ServerSocket ss = new ServerSocket(10010);
//获取连接过来的socket
//为了让服务端能够一直接受消息,我们需要定义个while循环。
int count = 0;
int time = 1;
while(true)
{
System.out.println("准备第"+(time++)+"次接受数据");
Socket socket = ss.accept();
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
//创建文件保存传输过来的数据.
File file = new File("D:"+File.separator+"Server/ke.jpg");
while(file.exists())
{
file = new File("D:"+File.separator+"Server"+
File.separator+"keke("+(++count)+").jpg");
}
//直到文件不存在,才创建新的文件。
file.createNewFile();
System.out.println("保存的文件名字为: "+file.getName());
//绑定输出流,等会将接受的数据传递到该文件中....
FileOutputStream fos = new FileOutputStream(file);
byte[] buff = new byte[1024];
int len = 0; //保存读取的字节个数
int c = 1;
while((len = in.read(buff))!=-1)
//while((len = in.read(buff))!=-1)
{
fos.write(buff, 0, len);
System.out.println("第"+(c++)+"次"+"接受成功.. 数据长度为:"+len );
}
String str = "文件读取完毕!";
System.out.println(str);
out.write(str.getBytes());
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
fos.close();
socket.close();
}
//ss.close();
}
}
在启动服务端之后,我再启动客户端。
结果我发现客户端和服务都没有停止运行。为了了解到更详细的信息,我在服务端设置了很多的数据输出..
我发现服务端在下面这个while循环中一直都没有退出。
while((len = in.read(buff))!=-1)
//while((len = in.read(buff))!=-1)
{
fos.write(buff, 0, len);
System.out.println("第"+(c++)+"次"+"接受成功.. 数据长度为:"+len );
}
我百思不得其解,客户端数据已经传输完毕,为什么服务端会停留在这个while循环,在len的值变成60之后,就没有继续执行下去。为什么服务端一直都没有读取到文件流的末尾,从而让len的值变成-1,退出循环。
最后,我想,如果客户端的的socket的out流没有关闭,那该out就可能一直都在传输数据。所以,服务端就会处于一直接受数据的状态。
我再回头看我客户端的代码。
while((len =fis.read(buff))!=-1)
{
out.write(buff, 0, len);
}
System.out.println("文件传输完毕....");
byte[] bufin = new byte[1024];
int reclen = in.read(bufin);
在如上这段代码中。
当客户端将整个图片的数据发送出去之后,他就进入到“准备接受从服务端发送过来的数据”的状态。
而此时,由于客户端的socket,socket中的out流都没有关闭。
所以导致服务端停止在while循环中。其下面的
String str = "文件读取完毕!";
System.out.println(str);
out.write(str.getBytes());
这段向客户端发送数据的代码就无法执行,而客户端就一直在等待服务端传过来的消息,以便继续执行下去。所以,双方都在等待对方的资源,从而造成一个类似死锁的情况。想到这一点,于是,我就在客户端准备接受数据之前。
将socket中的OutputStream流给关闭。
也就是在
while((len =fis.read(buff))!=-1)
{
out.write(buff, 0, len);
}
System.out.println("文件传输完毕....");
byte[] bufin = new byte[1024];
int reclen = in.read(bufin);
这段代码的System.out.println("文件传输完毕....");的后面加上out.close();
我想,这样关闭了out流,那么服务端应该就会跳出循环,让代码执行下去。
这个时候,服务端的数据是继续执行下去了,但是客服端却又出现了新的问题。运行客户端程序的时候,会出现如下的提示:
客服端启动,准备传输数据....
文件传输完毕....
Exception in thread "main" java.net.SocketException: socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.net.SocketInputStream.read(SocketInputStream.java:107)
at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:43)
socket closed,流已经被关闭。
我想到的是,这里客户端的程序还没有执行下去,因为我没看到输出结果。
System.out.println("文件传输完毕....");
out.close();
byte[] bufin = new byte[1024];
int reclen = in.read(bufin);
String str = new String(bufin,0,reclen);
System.out.println("服务端传来的消息:"+str);
这一段代码,下面的System.out.println("服务端传来的消息:"+str);没有被执行。
那么,这段代码只有可能 int reclen = in.read(bufin); 这句代码执行时出现问题。因为如果这句通过,后面的代码没有什么可以阻碍他们的运行。
(at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:43))
这里其实已经标明了错误的位置....!!
再看服务端那边的情况:
其输出情况如下:
服务端启动....准备接受数据.
准备第1次接受数据
保存的文件名字为: keke(28).jpg
第1次接受成功.. 数据长度为:1024
第2次接受成功.. 数据长度为:1024
第3次接受成功.. 数据长度为:1024
第4次接受成功.. 数据长度为:1024
第5次接受成功.. 数据长度为:1024
第6次接受成功.. 数据长度为:1024
第7次接受成功.. 数据长度为:1024
第8次接受成功.. 数据长度为:1024
第9次接受成功.. 数据长度为:1024
第10次接受成功.. 数据长度为:1024
第11次接受成功.. 数据长度为:1024
第12次接受成功.. 数据长度为:1024
第13次接受成功.. 数据长度为:1024
第14次接受成功.. 数据长度为:1024
第15次接受成功.. 数据长度为:1024
第16次接受成功.. 数据长度为:1024
第17次接受成功.. 数据长度为:1024
第18次接受成功.. 数据长度为:1024
第19次接受成功.. 数据长度为:1024
第20次接受成功.. 数据长度为:1024
第21次接受成功.. 数据长度为:1024
第22次接受成功.. 数据长度为:1024
第23次接受成功.. 数据长度为:1024
第24次接受成功.. 数据长度为:1024
第25次接受成功.. 数据长度为:1024
第26次接受成功.. 数据长度为:1024
第27次接受成功.. 数据长度为:1024
第28次接受成功.. 数据长度为:1024
第29次接受成功.. 数据长度为:1024
第30次接受成功.. 数据长度为:1024
第31次接受成功.. 数据长度为:1024
第32次接受成功.. 数据长度为:1024
第33次接受成功.. 数据长度为:1024
第34次接受成功.. 数据长度为:60
文件读取完毕!
准备第2次接受数据
这标明服务端已经正常执行完成。
那么,到底是什么引起客户端的 (at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:43))
socket closed,流已经被关闭。
这个问题呢。
开始我在服务端加入了一个sleep(),线程休眠。
System.out.println(str);
out.write(str.getBytes());
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
fos.close();
socket.close();
}
//ss.close();
就是为了让服务端进行短暂的休眠,而不去执行socket.close();让客户端可以正常的接受数据。
我开始以为是因为服务端执行速度过快,导致socket.close()执行,从而关闭了流。
但是目前看来,情况并非如此。
于是,我想,是不是因为socket的out流关闭,就会导致in流也关闭,或者整个socket都被关闭呢。
我先试试看,将out.close();改成 in.close();
看看有什么情况。
结果,程序依然如上面 out.close() 被执行一般。
证明in.close和out.close会起到同样的作用。
那么,他是否可能会导致socket流被关闭呢。
于是,我想在in.close()下,再加上一句socket.close();
看下会发生什么情况。
客服端启动,准备传输数据....
文件传输完毕....
Exception in thread "main" java.net.SocketException: socket closed
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at java.net.SocketInputStream.read(SocketInputStream.java:107)
at cn.dzr.tcp.Client.ClientTcp.main(ClientTcp.java:44)
错误依旧指向 “ int reclen = in.read(bufin); ”
同时,我在socket.close()下加上许多句socket.close();
在执行的时候,这些socket.close()并不会产生错误。
这说明socket.close()是可以被执行无数次的,一个关闭的流再次被关闭并不会引发异常。
不过,我们依然无法验证,in.close()是否导致socket.close()发生。
虽然在错误提示信息里,有一个socket.closed的提示,但是异常分析通常具有非常大的不可预料性。很可能其他的原因,最后也导致出现这么一个提示。
比如在一段程序里,你少写一个;号,结果系统就提示一堆莫名其妙的错误。
如果你试图根据这些错误来解决问题,这比南辕北辙更加离谱了。
后来发现socket有一个isclosed的方法。
于是,我想通过代码验证一下。
while((len =fis.read(buff))!=-1)
{
out.write(buff, 0, len);
}
System.out.println("文件传输完毕....");
in.close();
System.out.println(socket.isClosed());
结果,这里会输出true。也就是说在in.close,out.close()后,socket也就随之关闭了。我思考下原因,可能是因为socket本来就是用来传输数据的,而你把这个数据的输入输出流给关闭了,他就不具备传输数据的作用,自然再以打开状态运行也是无效的。