下面分別是服務端和客戶端的代碼....
客戶端代碼:
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本來就是用來傳輸數據的,而你把這個數據的輸入輸出流給關閉了,他就不具備傳輸數據的作用,自然再以打開狀態運行也是無效的。