目錄[-]html
1、TCP/IP協議java
6、多客戶端鏈接服務器app
7、信息共享less
8、文件傳輸socket
推薦閱讀:ide
前言:在最近一個即將結束的項目中使用到了Socket編程,用於調用另外一系統進行處理並返回數據。故把Socket的基礎知識總結梳理一遍。
既然是網絡編程,涉及幾個系統之間的交互,那麼首先要考慮的是如何準確的定位到網絡上的一臺或幾臺主機,另外一個是如何進行可靠高效的數據傳輸。這裏就要使用到TCP/IP協議。
TCP/IP協議(傳輸控制協議)由網絡層的IP協議和傳輸層的TCP協議組成。IP層負責網絡主機的定位,數據傳輸的路由,由IP地址能夠惟一的肯定Internet上的一臺主機。TCP層負責面向應用的可靠的或非可靠的數據傳輸機制,這是網絡編程的主要對象。
TCP是一種面向鏈接的保證可靠傳輸的協議。經過TCP協議傳輸,獲得的是一個順序的無差錯的數據流。發送方和接收方的成對的兩個socket之間必須創建鏈接,以便在TCP協議的基礎上進行通訊,當一個socket(一般都是server socket)等待創建鏈接時,另外一個socket能夠要求進行鏈接,一旦這兩個socket鏈接起來,它們就能夠進行雙向數據傳輸,雙方均可以進行發送或接收操做。
UDP是一種面向無鏈接的協議,每一個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳往目的地,所以可否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。
TCP與UDP區別:
TCP特色:
一、TCP是面向鏈接的協議,經過三次握手創建鏈接,通信完成時要拆除鏈接,因爲TCP是面向鏈接協議,因此只能用於點對點的通信。並且創建鏈接也須要消耗時間和開銷。
二、TCP傳輸數據無大小限制,進行大數據傳輸。
三、TCP是一個可靠的協議,它能保證接收方可以完整正確地接收到發送方發送的所有數據。
UDP特色:
一、UDP是面向無鏈接的通信協議,UDP數據包括目的端口號和源端口號信息,因爲通信不須要鏈接,因此能夠實現廣播發送。
二、UDP傳輸數據時有大小限制,每一個被傳輸的數據報必須限定在64KB以內。
三、UDP是一個不可靠的協議,發送方所發送的數據報並不必定以相同的次序到達接收方。
TCP與UDP應用:
一、TCP在網絡通訊上有極強的生命力,例如遠程鏈接(Telnet)和文件傳輸(FTP)都須要不定長度的數據被可靠地傳輸。可是可靠的傳輸是要付出代價的,對數據內容正確性的檢驗必然佔用計算機的處理時間和網絡的帶寬,所以TCP傳輸的效率不如UDP高。
2,UDP操做簡單,並且僅須要較少的監護,所以一般用於局域網高可靠性的分散系統中client/server應用程序。例如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就能夠了,這種狀況下顯然使用UDP會更合理一些。
Socket一般也稱做"套接字",用於描述IP地址和端口,是一個通訊鏈的句柄。網絡上的兩個程序經過一個雙向的通信鏈接實現數據的交換,這個雙向鏈路的一端稱爲一個Socket,一個Socket由一個IP地址和一個端口號惟一肯定。應用程序一般經過"套接字"向網絡發出請求或者應答網絡請求。 Socket是TCP/IP協議的一個十分流行的編程界面,可是,Socket所支持的協議種類也不光TCP/IP一種,所以二者之間是沒有必然聯繫的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網絡編程。
Socket通信過程:服務端監聽某個端口是否有鏈接請求,客戶端向服務端發送鏈接請求,服務端收到鏈接請求向客戶端發出接收消息,這樣一個鏈接就創建起來了。客戶端和服務端均可以相互發送消息與對方進行通信。
Socket的基本工做過程包含如下四個步驟:
一、建立Socket;
二、打開鏈接到Socket的輸入輸出流;
三、按照必定的協議對Socket進行讀寫操做;
四、關閉Socket。
在java.net包下有兩個類:Socket和ServerSocket。ServerSocket用於服務器端,Socket是創建網絡鏈接時使用的。在鏈接成功時,應用程序兩端都會產生一個Socket實例,操做這個實例,完成所需的會話。對於一個網絡鏈接來講,套接字是平等的,並無差異,不由於在服務器端或在客戶端而產生不一樣級別。不論是Socket仍是ServerSocket它們的工做都是經過SocketImpl類及其子類完成的。
列出幾個經常使用的構造方法:
1 2 3 4 5 6 7 8 9 |
Socket(InetAddress address,int port);//建立一個流套接字並將其鏈接到指定 IP 地址的指定端口號 Socket(String host,int port);//建立一個流套接字並將其鏈接到指定主機上的指定端口號 Socket(InetAddress address,int port, InetAddress localAddr,int localPort);//建立一個套接字並將其鏈接到指定遠程地址上的指定遠程端口 Socket(String host,int port, InetAddress localAddr,int localPort);//建立一個套接字並將其鏈接到指定遠程主機上的指定遠程端口 Socket(SocketImpl impl);//使用用戶指定的 SocketImpl 建立一個未鏈接 Socket ServerSocket(int port);//建立綁定到特定端口的服務器套接字 ServerSocket(int port,int backlog);//利用指定的 backlog 建立服務器套接字並將其綁定到指定的本地端口號 ServerSocket(int port,int backlog, InetAddress bindAddr);//使用指定的端口、偵聽 backlog 和要綁定到的本地 IP地址建立服務器 |
構造方法的參數中,address、host和port分別是雙向鏈接中另外一方的IP地址、主機名和端 口號,stream指明socket是流socket仍是數據報socket,localPort表示本地主機的端口號,localAddr和bindAddr是本地機器的地址(ServerSocket的主機地址),impl是socket的父類,既能夠用來建立serverSocket又能夠用來建立Socket。count則表示服務端所能支持的最大鏈接數。
注意:必須當心選擇端口號。每個端口提供一種特定的服務,只有給出正確的端口,才 能得到相應的服務。0~1023的端口號爲系統所保留,例如http服務的端口號爲80,telnet服務的端口號爲21,ftp服務的端口號爲23, 因此咱們在選擇端口號時,最好選擇一個大於1023的數以防止發生衝突。
幾個重要的Socke方法:
1 2 3 |
public InputStream getInputStream();//方法得到網絡鏈接輸入,同時返回一個IutputStream對象實例 public OutputStream getOutputStream();//方法鏈接的另外一端將獲得輸入,同時返回一個OutputStream對象實例 public Socket accept();//用於產生"阻塞",直到接受到一個鏈接,而且返回一個客戶端的Socket對象實例。 |
"阻塞"是一個術語,它使程序運行暫時"停留"在這個地方,直到一個會話產生,而後程序繼續;一般"阻塞"是由循環產生的。
注意:其中getInputStream和getOutputStream方法均會產生一個IOException,它必須被捕獲,由於它們返回的流對象,一般都會被另外一個流對象使用。
如下是一個基本的客戶端/服務器端程序代碼。主要實現了服務器端一直監聽某個端口,等待客戶端鏈接請求。客戶端根據IP地址和端口號鏈接服務器端,從鍵盤上輸入一行信息,發送到服務器端,而後接收服務器端返回的信息,最後結束會話。這個程序一次只能接受一個客戶鏈接。
ps:這個小例子寫好後,服務端一直接收不到消息,調試了好長時間,才發現誤使用了PrintWriter的print()方法,而BufferedReader的readLine()方法一直沒有遇到換行,因此一直等待讀取。我暈死~~@_@
客戶端程序:
01 |
package sock; |
02 |
03 |
import java.io.BufferedReader; |
04 |
import java.io.InputStreamReader; |
05 |
import java.io.PrintWriter; |
06 |
import java.net.Socket; |
07 |
08 |
public class SocketClient { |
09 |
public static void main(String[] args) { |
10 |
try { |
11 |
/** 建立Socket*/ |
12 |
// 建立一個流套接字並將其鏈接到指定 IP 地址的指定端口號(本處是本機) |
13 |
Socket socket = new Socket( "127.0.0.1" , 2013 ); |
14 |
// 60s超時 |
15 |
socket.setSoTimeout( 60000 ); |
16 |
17 |
/** 發送客戶端準備傳輸的信息 */ |
18 |
// 由Socket對象獲得輸出流,並構造PrintWriter對象 |
19 |
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true ); |
20 |
// 將輸入讀入的字符串輸出到Server |
21 |
BufferedReader sysBuff = new BufferedReader( new InputStreamReader(System.in)); |
22 |
printWriter.println(sysBuff.readLine()); |
23 |
// 刷新輸出流,使Server立刻收到該字符串 |
24 |
printWriter.flush(); |
25 |
26 |
/** 用於獲取服務端傳輸來的信息 */ |
27 |
// 由Socket對象獲得輸入流,並構造相應的BufferedReader對象 |
28 |
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream())); |
29 |
// 輸入讀入一字符串 |
30 |
String result = bufferedReader.readLine(); |
31 |
System.out.println( "Server say : " + result); |
32 |
33 |
/** 關閉Socket*/ |
34 |
printWriter.close(); |
35 |
bufferedReader.close(); |
36 |
socket.close(); |
37 |
} catch (Exception e) { |
38 |
System.out.println( "Exception:" + e); |
39 |
} |
40 |
} |
41 |
} |
服務器端程序:
01 |
package sock; |
02 |
import java.io.BufferedReader; |
03 |
import java.io.InputStreamReader; |
04 |
import java.io.PrintWriter; |
05 |
import java.net.ServerSocket; |
06 |
import java.net.Socket; |
07 |
08 |
public class SocketServer { |
09 |
public static void main(String[] args) { |
10 |
try { |
11 |
/** 建立ServerSocket*/ |
12 |
// 建立一個ServerSocket在端口2013監聽客戶請求 |
13 |
ServerSocket serverSocket = new ServerSocket( 2013 ); |
14 |
while ( true ) { |
15 |
// 偵聽並接受到此Socket的鏈接,請求到來則產生一個Socket對象,並繼續執行 |
16 |
Socket socket = serverSocket.accept(); |
17 |
18 |
/** 獲取客戶端傳來的信息 */ |
19 |
// 由Socket對象獲得輸入流,並構造相應的BufferedReader對象 |
20 |
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream())); |
21 |
// 獲取從客戶端讀入的字符串 |
22 |
String result = bufferedReader.readLine(); |
23 |
System.out.println( "Client say : " + result); |
24 |
25 |
/** 發送服務端準備傳輸的 */ |
26 |
// 由Socket對象獲得輸出流,並構造PrintWriter對象 |
27 |
PrintWriter printWriter = new PrintWriter(socket.getOutputStream()); |
28 |
printWriter.print( "hello Client, I am Server!" ); |
29 |
printWriter.flush(); |
30 |
31 |
/** 關閉Socket*/ |
32 |
printWriter.close(); |
33 |
bufferedReader.close(); |
34 |
socket.close(); |
35 |
} |
36 |
} catch (Exception e) { |
37 |
System.out.println( "Exception:" + e); |
38 |
} finally { |
39 |
// serverSocket.close(); |
40 |
} |
41 |
} |
42 |
} |
上面的服務器端程序一次只能鏈接一個客戶端,這在實際應用中顯然是不可能的。一般的網絡環境是多個客戶端鏈接到某個主機進行通信,因此咱們要對上面的程序進行改造。
設計思路:服務器端主程序監聽某一個端口,客戶端發起鏈接請求,服務器端主程序接收請求,同時構造一個線程類,用於接管會話。當一個Socket會話產生後,這個會話就會交給線程進行處理,主程序繼續進行監聽。
下面的實現程序流程是:客戶端和服務器創建鏈接,客戶端發送消息,服務端根據消息進行處理並返回消息,若客戶端申請關閉,則服務器關閉此鏈接,雙方通信結束。
客戶端程序:
01 |
package sock; |
02 |
03 |
import java.io.BufferedReader; |
04 |
import java.io.InputStreamReader; |
05 |
import java.io.PrintWriter; |
06 |
import java.net.Socket; |
07 |
08 |
public class SocketClient { |
09 |
public static void main(String[] args) { |
10 |
try { |
11 |
Socket socket = new Socket( "127.0.0.1" , 2013 ); |
12 |
socket.setSoTimeout( 60000 ); |
13 |
14 |
PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true ); |
15 |
BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream())); |
16 |
17 |
String result = "" ; |
18 |
while (result.indexOf( "bye" ) == - 1 ){ |
19 |
BufferedReader sysBuff = new BufferedReader( new InputStreamReader(System.in)); |
20 |
printWriter.println(sysBuff.readLine()); |
21 |
printWriter.flush(); |
22 |
23 |
result = bufferedReader.readLine(); |
24 |
System.out.println( "Server say : " + result); |
25 |
} |
26 |
27 |
printWriter.close(); |
28 |
bufferedReader.close(); |
29 |
socket.close(); |
30 |
} catch (Exception e) { |
31 |
System.out.println( "Exception:" + e); |
32 |
} |
33 |
} |
34 |
} |
服務器端程序:
01 |
package sock; |
02 |
import java.io.*; |
03 |
import java.net.*; |
04 |
05 |
public class Server extends ServerSocket { |
06 |
private static final int SERVER_PORT = 2013 ; |
07 |
08 |
public Server() throws IOException { |
09 |
super (SERVER_PORT); |
10 |
11 |
try { |
12 |
while ( true ) { |
13 |
Socket socket = accept(); |
14 |
new CreateServerThread(socket); //當有請求時,啓一個線程處理 |
15 |
} |
16 |
} catch (IOException e) { |
17 |
} finally { |
18 |
close(); |
19 |
} |
20 |
} |
21 |
22 |
//線程類 |
23 |
class CreateServerThread extends Thread { |
24 |
private Socket client; |
25 |
private BufferedReader bufferedReader; |
26 |
private PrintWriter printWriter; |
27 |
28 |
public CreateServerThread(Socket s) throws IOException { |
29 |
client = s; |
30 |
31 |
bufferedReader = new BufferedReader( new InputStreamReader(client.getInputStream())); |
32 |
33 |
printWriter = new PrintWriter(client.getOutputStream(), true ); |
34 |
System.out.println( "Client(" + getName() + ") come in..." ); |
35 |
36 |
start(); |
37 |
} |
38 |
39 |
public void run() { |
40 |
try { |
41 |
String line = bufferedReader.readLine(); |
42 |
43 |
while (!line.equals( "bye" )) { |
44 |
printWriter.println( "continue, Client(" + getName() + ")!" ); |
45 |
line = bufferedReader.readLine(); |
46 |
System.out.println( "Client(" + getName() + ") say: " + line); |
47 |
} |
48 |
printWriter.println( "bye, Client(" + getName() + ")!" ); |
49 |
50 |
System.out.println( "Client(" + getName() + ") exit!" ); |
51 |
printWriter.close(); |
52 |
bufferedReader.close(); |
53 |
client.close(); |
54 |
} catch (IOException e) { |
55 |
} |
56 |
} |
57 |
} |
58 |
59 |
public static void main(String[] args) throws IOException { |
60 |
new Server(); |
61 |
} |
62 |
} |
以上雖然實現了多個客戶端和服務器鏈接,可是仍然是消息在一個客戶端和服務器之間相互傳播。如今咱們要實現信息共享,即服務器能夠向多個客戶端發送廣播消息,客戶端也能夠向其餘客戶端發送消息。相似於聊天室的那種功能,實現信息能在多個客戶端之間共享。
設計思路:客戶端循環能夠不停輸入向服務器發送消息,而且啓一個線程,專門用來監聽服務器端發來的消息並打印輸出。服務器端啓動時,啓動一個監聽什麼時候須要向客戶端發送消息的線程。每次接受客戶端鏈接請求,都啓一個線程進行處理,而且將客戶端信息存放到公共集合中。當客戶端發送消息時,服務器端將消息順序存入隊列中,當須要輸出時,從隊列中取出廣播到各客戶端處。客戶端輸入showuser命令能夠查看在線用戶列表,輸入bye向服務器端申請退出鏈接。
PS:如下代碼在測試時發現了一箇中文亂碼小問題,當文件設置UTF-8編碼時,不管怎樣在代碼中設置輸入流編碼都不起做用,輸入中文仍然會亂碼。把文件設置爲GBK編碼後,不用在代碼中設置輸入流編碼都能正常顯示傳輸中文。
客戶端代碼:
01 |
package sock; |
02 |
03 |
import java.io.BufferedReader; |
04 |
import java.io.InputStreamReader; |
05 |
import java.io.PrintWriter; |
06 |
import java.net.Socket; |
07 |
08 |
public class SocketClient extends Socket{ |
09 |
10 |
private static final String SERVER_IP = "127.0.0.1" ; |
11 |
private static final int SERVER_PORT = 2013 ; |
12 |
13 |
private Socket client; |
14 |
private PrintWriter out; |
15 |
private BufferedReader in; |
16 |
17 |
/** |
18 |
* 與服務器鏈接,並輸入發送消息 |
19 |
*/ |
20 |
public SocketClient() throws Exception{ |
21 |
super (SERVER_IP, SERVER_PORT); |
22 |
client = this ; |
23 |
out = new PrintWriter( this .getOutputStream(), true ); |
24 |
in = new BufferedReader( new InputStreamReader( this .getInputStream())); |
25 |
new readLineThread(); |
26 |
27 |
while ( true ){ |
28 |
in = new BufferedReader( new InputStreamReader(System.in)); |
29 |
String input = in.readLine(); |
30 |
out.println(input); |
31 |
} |
32 |
} |
33 |
34 |
/** |
35 |
* 用於監聽服務器端向客戶端發送消息線程類 |
36 |
*/ |
37 |
class readLineThread extends Thread{ |
38 |
39 |
private BufferedReader buff; |
40 |
public readLineThread(){ |
41 |
try { |
42 |
buff = new BufferedReader( new InputStreamReader(client.getInputStream())); |
43 |
start(); |
44 |
} catch (Exception e) { |
45 |
} |
46 |
} |
47 |
48 |
@Override |
49 |
public void run() { |
50 |
try { |
51 |
while ( true ){ |
52 |
String result = buff.readLine(); |
53 |
if ( "byeClient" .equals(result)){ //客戶端申請退出,服務端返回確認退出 |
54 |
break ; |
55 |
} else { //輸出服務端發送消息 |
56 |
System.out.println(result); |
57 |
} |
58 |
} |
59 |
in.close(); |
60 |
out.close(); |
61 |
client.close(); |
62 |
} catch (Exception e) { |
63 |
} |
64 |
} |
65 |
} |
66 |
67 |
public static void main(String[] args) { |
68 |
try { |
69 |
new SocketClient(); //啓動客戶端 |
70 |
} catch (Exception e) { |
71 |
} |
72 |
} |
73 |
} |
服務器端代碼:
001 |
package sock; |
002 |
import java.io.BufferedReader; |
003 |
import java.io.IOException; |
004 |
import java.io.InputStreamReader; |
005 |
import java.io.PrintWriter; |
006 |
import java.net.ServerSocket; |
007 |
import java.net.Socket; |
008 |
import java.util.ArrayList; |
009 |
import java.util.LinkedList; |
010 |
import java.util.List; |
011 |
012 |
013 |
public class Server extends ServerSocket{ |
014 |
015 |
private static final int SERVER_PORT = 2013 ; |
016 |
017 |
private static boolean isPrint = false ; //是否輸出消息標誌 |
018 |
private static List user_list = new ArrayList(); //登陸用戶集合 |
019 |
private static List<ServerThread> thread_list = new ArrayList<ServerThread>(); //服務器已啓用線程集合 |
020 |
private static LinkedList<String> message_list = new LinkedList<String>(); //存放消息隊列 |
021 |
022 |
/** |
023 |
* 建立服務端Socket,建立向客戶端發送消息線程,監聽客戶端請求並處理 |
024 |
*/ |
025 |
public Server() throws IOException{ |
026 |
super (SERVER_PORT); //建立ServerSocket |
027 |
new PrintOutThread(); //建立向客戶端發送消息線程 |
028 |
029 |
try { |
030 |
while ( true ){ //監聽客戶端請求,啓個線程處理 |
031 |
Socket socket = accept(); |
032 |
new ServerThread(socket); |
033 |
} |
034 |
} catch (Exception e) { |
035 |
} finally { |
036 |
close(); |
037 |
} |
038 |
} |
039 |
040 |
/** |
041 |
* 監聽是否有輸出消息請求線程類,向客戶端發送消息 |
042 |
*/ |
043 |
class PrintOutThread extends Thread{ |
044 |
045 |
public PrintOutThread(){ |
046 |
start(); |
047 |
} |
048 |
049 |
@Override |
050 |
public void run() { |
051 |
while ( true ){ |
052 |
if (isPrint){ //將緩存在隊列中的消息按順序發送到各客戶端,並從隊列中清除。 |
053 |
String message = message_list.getFirst(); |
054 |
for (ServerThread thread : thread_list) { |
055 |
thread.sendMessage(message); |
056 |
} |
057 |
message_list.removeFirst(); |
058 |
isPrint = message_list.size() > 0 ? true : false ; |
059 |
} |
060 |
} |
061 |
} |
062 |
} |
063 |
064 |
/** |
065 |
* 服務器線程類 |
066 |
*/ |
067 |
class ServerThread extends Thread{ |
068 |
private Socket client; |
069 |
private PrintWriter out; |
070 |
private BufferedReader in; |
071 |
private String name; |
072 |
073 |
public ServerThread(Socket s) throws IOException{ |
074 |
client = s; |
075 |
out = new PrintWriter(client.getOutputStream(), true ); |
076 |
in = new BufferedReader( new InputStreamReader(client.getInputStream())); |
077 |
in.readLine(); |
078 |
out.println( "成功連上聊天室,請輸入你的名字:" ); |
079 |
start(); |
080 |
} |
081 |
082 |
@Override |
083 |
public void run() { |
084 |
try { |
085 |
int flag = 0 ; |
086 |
String line = in.readLine(); |
087 |
while (! "bye" .equals(line)){ |
088 |
//查看在線用戶列表 |
089 |
if ( "showuser" .equals(line)) { |
090 |
out.println( this .listOnlineUsers()); |
091 |
line = in.readLine(); |
092 |
} |
093 |
//第一次進入,保存名字 |
094 |
if (flag++ == 0 ){ |
095 |
name = line; |
096 |
user_list.add(name); |
097 |
thread_list.add( this ); |
098 |
out.println(name + "你好,能夠開始聊天了..." ); |
099 |
this .pushMessage( "Client<" + name + ">進入聊天室..." ); |
100 |
} else { |
101 |
this .pushMessage( "Client<" + name + "> say : " + line); |
102 |
} |
103 |
line = in.readLine(); |
104 |
} |
105 |
out.println( "byeClient" ); |
106 |
} catch (Exception e) { |
107 |
e.printStackTrace(); |
108 |
} finally { //用戶退出聊天室 |
109 |
try { |
110 |
client.close(); |
111 |
} catch (IOException e) { |
112 |
e.printStackTrace(); |
113 |
} |
114 |
thread_list.remove( this ); |
115 |
user_list.remove(name); |
116 |
pushMessage( "Client<" + name + ">退出了聊天室" ); |
117 |
} |
118 |
} |
119 |
120 |
//放入消息隊列末尾,準備發送給客戶端 |
121 |
private void pushMessage(String msg){ |
122 |
message_list.addLast(msg); |
123 |
isPrint = true ; |
124 |
} |
125 |
126 |
//向客戶端發送一條消息 |
127 |
private void sendMessage(String msg){ |
128 |
out.println(msg); |
129 |
} |
130 |
131 |
//統計在線用戶列表 |
132 |
private String listOnlineUsers() { |
133 |
String s = "--- 在線用戶列表 ---\015\012" ; |
134 |
for ( int i = 0 ; i < user_list.size(); i++) { |
135 |
s += "[" + user_list.get(i) + "]\015\012" ; |
136 |
} |
137 |
s += "--------------------" ; |
138 |
return s; |
139 |
} |
140 |
} |
141 |
142 |
public static void main(String[] args) throws IOException { |
143 |
new Server(); //啓動服務端 |
144 |
} |
145 |
} |
客戶端向服務器端傳送文件,服務端可獲取文件名用於保存,獲取文件大小計算傳輸進度,比較簡單,直接貼代碼。
客戶端代碼:
01 |
package sock; |
02 |
03 |
import java.io.DataOutputStream; |
04 |
import java.io.File; |
05 |
import java.io.FileInputStream; |
06 |
import java.net.Socket; |
07 |
08 |
/** |
09 |
* 客戶端 |
10 |
*/ |
11 |
public class Client extends Socket{ |
12 |
13 |
private static final String SERVER_IP = "127.0.0.1" ; |
14 |
private static final int SERVER_PORT = 2013 ; |
15 |
16 |
private Socket client; |
17 |
private FileInputStream fis; |
18 |
private DataOutputStream dos; |
19 |
20 |
public Client(){ |
21 |
try { |
22 |
try { |
23 |
client = new Socket(SERVER_IP, SERVER_PORT); |
24 |
//向服務端傳送文件 |
25 |
File file = new File( "c:/test.doc" ); |
26 |
fis = new FileInputStream(file); |
27 |
dos = new DataOutputStream(client.getOutputStream()); |
28 |
29 |
//文件名和長度 |
30 |
dos.writeUTF(file.getName()); |
31 |
dos.flush(); |
32 |
dos.writeLong(file.length()); |
33 |
dos.flush(); |
34 |
35 |
//傳輸文件 |
36 |
byte [] sendBytes = new byte [ 1024 ]; |
37 |
int length = 0 ; |
38 |
while ((length = fis.read(sendBytes, 0 , sendBytes.length)) > 0 ){ |
39 |
dos.write(sendBytes, 0 , length); |
40 |
dos.flush(); |
41 |
} |
42 |
} catch (Exception e) { |
43 |
e.printStackTrace(); |
44 |
} finally { |
45 |
if (fis != null ) |
46 |
fis.close(); |
47 |
if (dos != null ) |
48 |
dos.close(); |
49 |
client.close(); |
50 |
} |
51 |
} catch (Exception e) { |
52 |
e.printStackTrace(); |
53 |
} |
54 |
} |
55 |
56 |
public static void main(String[] args) throws Exception { |
57 |
new Client(); |
58 |
} |
59 |
} |
服務器端代碼:
01 |
package sock; |
02 |
import java.io.DataInputStream; |
03 |
import java.io.File; |
04 |
import java.io.FileOutputStream; |
05 |
import java.net.ServerSocket; |
06 |
import java.net.Socket; |
07 |
08 |
/** |
09 |
* 服務器 |
10 |
*/ |
11 |
public class Server extends ServerSocket{ |
12 |
13 |
private static final int PORT = 2013 ; |
14 |
15 |
private ServerSocket server; |
16 |
private Socket client; |
17 |
private DataInputStream dis; |
18 |
private FileOutputStream fos; |
19 |
20 |
public Server() throws Exception{ |
21 |
try { |
22 |
try { |
23 |
server = new ServerSocket(PORT); |
24 |
25 |
while ( true ){ |
26 |
client = server.accept(); |
27 |
28 |
dis = new DataInputStream(client.getInputStream()); |
29 |
//文件名和長度 |
30 |
String fileName = dis.readUTF(); |
31 |
long fileLength = dis.readLong(); |
32 |
fos = new FileOutputStream( new File( "d:/" + fileName)); |
33 |
34 |
byte [] sendBytes = new byte [ 1024 ]; |
35 |
int transLen = 0 ; |
36 |
System.out.println( "----開始接收文件<" + fileName + ">,文件大小爲<" + fileLength + ">----" ); |
37 |
while ( true ){ |
38 |
int read = 0 ; |
39 |
read = dis.read(sendBytes); |
40 |
if (read == - 1 ) |
41 |
break ; |
42 |
transLen += read; |
43 |
System.out.println( "接收文件進度" + 100 * transLen/fileLength + "%..." ); |
44 |
fos.write(sendBytes, 0 , read); |
45 |
fos.flush(); |
46 |
} |
47 |
System.out.println( "----接收文件<" + fileName + ">成功-------" ); |
48 |
client.close(); |
49 |
} |
50 |
} catch (Exception e) { |
51 |
e.printStackTrace(); |
52 |
} finally { |
53 |
if (dis != null ) |
54 |
dis.close(); |
55 |
if (fos != null ) |
56 |
fos.close(); |
57 |
server.close(); |
58 |
} |
59 |
} catch (Exception e) { |
60 |
e.printStackTrace(); |
61 |
} |
62 |
} |
63 |
64 |
public static void main(String[] args) throws Exception { |
65 |
new Server(); |
66 |
} |
67 |
} |