循序漸進Java Socket網絡編程(多客戶端、信息共享、文件傳輸)


前言:在最近一個即將結束的項目中使用到了Socket編程,用於調用另一系統進行處理並返回數據。故把Socket的基礎知識總結梳理一遍。

一、TCP/IP協議

  既然是網絡編程,涉及幾個系統之間的交互,那么首先要考慮的是如何准確的定位到網絡上的一台或幾台主機,另一個是如何進行可靠高效的數據傳輸。這里就要使用到TCP/IP協議。

  TCP/IP協議(傳輸控制協議)由網絡層的IP協議和傳輸層的TCP協議組成。IP層負責網絡主機的定位,數據傳輸的路由,由IP地址可以唯一的確定Internet上的一台主機。TCP層負責面向應用的可靠的或非可靠的數據傳輸機制,這是網絡編程的主要對象。

二、TCP與UDP

  TCP是一種面向連接的保證可靠傳輸的協議。通過TCP協議傳輸,得到的是一個順序的無差錯的數據流。發送方和接收方的成對的兩個socket之間必須建立連接,以便在TCP協議的基礎上進行通信,當一個socket(通常都是server socket)等待建立連接時,另一個socket可以要求進行連接,一旦這兩個socket連接起來,它們就可以進行雙向數據傳輸,雙方都可以進行發送或接收操作。

  UDP是一種面向無連接的協議,每個數據報都是一個獨立的信息,包括完整的源地址或目的地址,它在網絡上以任何可能的路徑傳往目的地,因此能否到達目的地,到達目的地的時間以及內容的正確性都是不能被保證的。

TCP與UDP區別:

TCP特點:

  1、TCP是面向連接的協議,通過三次握手建立連接,通訊完成時要拆除連接,由於TCP是面向連接協議,所以只能用於點對點的通訊。而且建立連接也需要消耗時間和開銷。

  2、TCP傳輸數據無大小限制,進行大數據傳輸。

  3、TCP是一個可靠的協議,它能保證接收方能夠完整正確地接收到發送方發送的全部數據。

UDP特點:

  1、UDP是面向無連接的通訊協議,UDP數據包括目的端口號和源端口號信息,由於通訊不需要連接,所以可以實現廣播發送。

  2、UDP傳輸數據時有大小限制,每個被傳輸的數據報必須限定在64KB之內。

  3、UDP是一個不可靠的協議,發送方所發送的數據報並不一定以相同的次序到達接收方。

TCP與UDP應用:

  1、TCP在網絡通信上有極強的生命力,例如遠程連接(Telnet)和文件傳輸(FTP)都需要不定長度的數據被可靠地傳輸。但是可靠的傳輸是要付出代價的,對數據內容正確性的檢驗必然占用計算機的處理時間和網絡的帶寬,因此TCP傳輸的效率不如UDP高。

  2,UDP操作簡單,而且僅需要較少的監護,因此通常用於局域網高可靠性的分散系統中client/server應用程序。例如視頻會議系統,並不要求音頻視頻數據絕對的正確,只要保證連貫性就可以了,這種情況下顯然使用UDP會更合理一些。

三、Socket是什么

  Socket通常也稱作"套接字",用於描述IP地址和端口,是一個通信鏈的句柄。網絡上的兩個程序通過一個雙向的通訊連接實現數據的交換,這個雙向鏈路的一端稱為一個Socket,一個Socket由一個IP地址和一個端口號唯一確定。應用程序通常通過"套接字"向網絡發出請求或者應答網絡請求。 Socket是TCP/IP協議的一個十分流行的編程界面,但是,Socket所支持的協議種類也不光TCP/IP一種,因此兩者之間是沒有必然聯系的。在Java環境下,Socket編程主要是指基於TCP/IP協議的網絡編程。

  Socket通訊過程:服務端監聽某個端口是否有連接請求,客戶端向服務端發送連接請求,服務端收到連接請求向客戶端發出接收消息,這樣一個連接就建立起來了。客戶端和服務端都可以相互發送消息與對方進行通訊。

  Socket的基本工作過程包含以下四個步驟:

  1、創建Socket;

  2、打開連接到Socket的輸入輸出流;

  3、按照一定的協議對Socket進行讀寫操作;

  4、關閉Socket。

四、Java中的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,它必須被捕獲,因為它們返回的流對象,通常都會被另一個流對象使用。

五、基本的Client/Server程序

以下是一個基本的客戶端/服務器端程序代碼。主要實現了服務器端一直監聽某個端口,等待客戶端連接請求。客戶端根據IP地址和端口號連接服務器端,從鍵盤上輸入一行信息,發送到服務器端,然后接收服務器端返回的信息,最后結束會話。這個程序一次只能接受一個客戶連接。

ps:這個小例子寫好后,服務端一直接收不到消息,調試了好長時間,才發現誤使用了PrintWriter的print()方法,而BufferedReader的readLine()方法一直沒有遇到換行,所以一直等待讀取。我暈死~~@_@

客戶端程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package sock;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
  
public class SocketClient {
     public static void main(String[] args) {
         try {
             /** 創建Socket*/
             // 創建一個流套接字並將其連接到指定 IP 地址的指定端口號(本處是本機)
             Socket socket = new Socket( "127.0.0.1" , 2013 );
             // 60s超時
             socket.setSoTimeout( 60000 );
  
             /** 發送客戶端准備傳輸的信息 */
             // 由Socket對象得到輸出流,並構造PrintWriter對象
             PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true );
             // 將輸入讀入的字符串輸出到Server
             BufferedReader sysBuff = new BufferedReader( new InputStreamReader(System.in));
             printWriter.println(sysBuff.readLine());
             // 刷新輸出流,使Server馬上收到該字符串
             printWriter.flush();
  
             /** 用於獲取服務端傳輸來的信息 */
             // 由Socket對象得到輸入流,並構造相應的BufferedReader對象
             BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream()));
             // 輸入讀入一字符串
             String result = bufferedReader.readLine();
             System.out.println( "Server say : " + result);
  
             /** 關閉Socket*/
             printWriter.close();
             bufferedReader.close();
             socket.close();
         } catch (Exception e) {
             System.out.println( "Exception:" + e);
         }
     }
}

服務器端程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package sock;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
  
public class SocketServer {
     public static void main(String[] args) {
         try {
             /** 創建ServerSocket*/
             // 創建一個ServerSocket在端口2013監聽客戶請求
             ServerSocket serverSocket = new ServerSocket( 2013 );
             while ( true ) {
                 // 偵聽並接受到此Socket的連接,請求到來則產生一個Socket對象,並繼續執行
                 Socket socket = serverSocket.accept();
  
                 /** 獲取客戶端傳來的信息 */
                 // 由Socket對象得到輸入流,並構造相應的BufferedReader對象
                 BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream()));
                 // 獲取從客戶端讀入的字符串
                 String result = bufferedReader.readLine();
                 System.out.println( "Client say : " + result);
  
                 /** 發送服務端准備傳輸的 */
                 // 由Socket對象得到輸出流,並構造PrintWriter對象
                 PrintWriter printWriter = new PrintWriter(socket.getOutputStream());
                 printWriter.print( "hello Client, I am Server!" );
                 printWriter.flush();
  
                 /** 關閉Socket*/
                 printWriter.close();
                 bufferedReader.close();
                 socket.close();
             }
         } catch (Exception e) {
             System.out.println( "Exception:" + e);
         } finally {
//          serverSocket.close();
         }
     }
}

六、多客戶端連接服務器

上面的服務器端程序一次只能連接一個客戶端,這在實際應用中顯然是不可能的。通常的網絡環境是多個客戶端連接到某個主機進行通訊,所以我們要對上面的程序進行改造。

設計思路:服務器端主程序監聽某一個端口,客戶端發起連接請求,服務器端主程序接收請求,同時構造一個線程類,用於接管會話。當一個Socket會話產生后,這個會話就會交給線程進行處理,主程序繼續進行監聽。

下面的實現程序流程是:客戶端和服務器建立連接,客戶端發送消息,服務端根據消息進行處理並返回消息,若客戶端申請關閉,則服務器關閉此連接,雙方通訊結束。

客戶端程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package sock;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
  
public class SocketClient {
     public static void main(String[] args) {
         try {
             Socket socket = new Socket( "127.0.0.1" , 2013 );
             socket.setSoTimeout( 60000 );
  
             PrintWriter printWriter = new PrintWriter(socket.getOutputStream(), true );
             BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(socket.getInputStream()));
              
             String result = "" ;
             while (result.indexOf( "bye" ) == - 1 ){
                 BufferedReader sysBuff = new BufferedReader( new InputStreamReader(System.in));
                 printWriter.println(sysBuff.readLine());
                 printWriter.flush();
                  
                 result = bufferedReader.readLine();
                 System.out.println( "Server say : " + result);
             }
  
             printWriter.close();
             bufferedReader.close();
             socket.close();
         } catch (Exception e) {
             System.out.println( "Exception:" + e);
         }
     }
}

服務器端程序:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
package sock;
import java.io.*;
import java.net.*;
  
public class Server extends ServerSocket {
     private static final int SERVER_PORT = 2013 ;
  
     public Server() throws IOException {
         super (SERVER_PORT);
  
         try {
             while ( true ) {
                 Socket socket = accept();
                 new CreateServerThread(socket); //當有請求時,啟一個線程處理
             }
         } catch (IOException e) {
         } finally {
             close();
         }
     }
  
     //線程類
     class CreateServerThread extends Thread {
         private Socket client;
         private BufferedReader bufferedReader;
         private PrintWriter printWriter;
  
         public CreateServerThread(Socket s) throws IOException {
             client = s;
  
             bufferedReader = new BufferedReader( new InputStreamReader(client.getInputStream()));
              
             printWriter = new PrintWriter(client.getOutputStream(), true );
             System.out.println( "Client(" + getName() + ") come in..." );
              
             start();
         }
  
         public void run() {
             try {
                 String line = bufferedReader.readLine();
  
                 while (!line.equals( "bye" )) {
                     printWriter.println( "continue, Client(" + getName() + ")!" );
                     line = bufferedReader.readLine();
                     System.out.println( "Client(" + getName() + ") say: " + line);
                 }
                 printWriter.println( "bye, Client(" + getName() + ")!" );
                  
                 System.out.println( "Client(" + getName() + ") exit!" );
                 printWriter.close();
                 bufferedReader.close();
                 client.close();
             } catch (IOException e) {
             }
         }
     }
  
     public static void main(String[] args) throws IOException {
         new Server();
     }
}

七、信息共享

以上雖然實現了多個客戶端和服務器連接,但是仍然是消息在一個客戶端和服務器之間相互傳播。現在我們要實現信息共享,即服務器可以向多個客戶端發送廣播消息,客戶端也可以向其他客戶端發送消息。類似於聊天室的那種功能,實現信息能在多個客戶端之間共享。

設計思路:客戶端循環可以不停輸入向服務器發送消息,並且啟一個線程,專門用來監聽服務器端發來的消息並打印輸出。服務器端啟動時,啟動一個監聽何時需要向客戶端發送消息的線程。每次接受客戶端連接請求,都啟一個線程進行處理,並且將客戶端信息存放到公共集合中。當客戶端發送消息時,服務器端將消息順序存入隊列中,當需要輸出時,從隊列中取出廣播到各客戶端處。客戶端輸入showuser命令可以查看在線用戶列表,輸入bye向服務器端申請退出連接。

PS:以下代碼在測試時發現了一個中文亂碼小問題,當文件設置UTF-8編碼時,無論怎樣在代碼中設置輸入流編碼都不起作用,輸入中文仍然會亂碼。把文件設置為GBK編碼后,不用在代碼中設置輸入流編碼都能正常顯示傳輸中文。

客戶端代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package sock;
 
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
  
public class SocketClient extends Socket{
  
     private static final String SERVER_IP = "127.0.0.1" ;
     private static final int SERVER_PORT = 2013 ;
      
     private Socket client;
     private PrintWriter out;
     private BufferedReader in;
      
     /**
      * 與服務器連接,並輸入發送消息
      */
     public SocketClient() throws Exception{
         super (SERVER_IP, SERVER_PORT);
         client = this ;
         out = new PrintWriter( this .getOutputStream(), true );
         in = new BufferedReader( new InputStreamReader( this .getInputStream()));
         new readLineThread();
          
         while ( true ){
             in = new BufferedReader( new InputStreamReader(System.in));
             String input = in.readLine();
             out.println(input);
         }
     }
      
     /**
      * 用於監聽服務器端向客戶端發送消息線程類
      */
     class readLineThread extends Thread{
          
         private BufferedReader buff;
         public readLineThread(){
             try {
                 buff = new BufferedReader( new InputStreamReader(client.getInputStream()));
                 start();
             } catch (Exception e) {
             }
         }
          
         @Override
         public void run() {
             try {
                 while ( true ){
                     String result = buff.readLine();
                     if ( "byeClient" .equals(result)){ //客戶端申請退出,服務端返回確認退出
                         break ;
                     } else { //輸出服務端發送消息
                         System.out.println(result);
                     }
                 }
                 in.close();
                 out.close();
                 client.close();
             } catch (Exception e) {
             }
         }
     }
      
     public static void main(String[] args) {
         try {
             new SocketClient(); //啟動客戶端
         } catch (Exception e) {
         }
     }
}

服務器端代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package sock;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
  
  
public class Server extends ServerSocket{
      
     private static final int SERVER_PORT = 2013 ;
      
     private static boolean isPrint = false ; //是否輸出消息標志
     private static List user_list = new ArrayList(); //登錄用戶集合
     private static List<ServerThread> thread_list = new ArrayList<ServerThread>(); //服務器已啟用線程集合
     private static LinkedList<String> message_list = new LinkedList<String>(); //存放消息隊列
      
     /**
      * 創建服務端Socket,創建向客戶端發送消息線程,監聽客戶端請求並處理
      */
     public Server() throws IOException{
         super (SERVER_PORT); //創建ServerSocket
         new PrintOutThread(); //創建向客戶端發送消息線程
          
         try {
             while ( true ){ //監聽客戶端請求,啟個線程處理
                 Socket socket = accept();
                 new ServerThread(socket);
             }
         } catch (Exception e) {
         } finally {
             close();
         }
     }
      
     /**
      * 監聽是否有輸出消息請求線程類,向客戶端發送消息
      */
     class PrintOutThread extends Thread{
          
         public PrintOutThread(){
             start();
         }
          
         @Override
         public void run() {
             while ( true ){
                 if (isPrint){ //將緩存在隊列中的消息按順序發送到各客戶端,並從隊列中清除。
                     String message = message_list.getFirst();
                     for (ServerThread thread : thread_list) {
                         thread.sendMessage(message);
                     }
                     message_list.removeFirst();
                     isPrint = message_list.size() > 0 ? true : false ;
                 }
             }
         }
     }
      
     /**
      * 服務器線程類
      */
     class ServerThread extends Thread{
         private Socket client;
         private PrintWriter out;
         private BufferedReader in;
         private String name;
          
         public ServerThread(Socket s) throws IOException{
             client = s;
             out = new PrintWriter(client.getOutputStream(), true );
             in = new BufferedReader( new InputStreamReader(client.getInputStream()));
             in.readLine();
             out.println( "成功連上聊天室,請輸入你的名字:" );
             start();
         }
          
         @Override
         public void run() {
             try {
                 int flag = 0 ;
                 String line = in.readLine();
                 while (! "bye" .equals(line)){
                     //查看在線用戶列表
                     if ( "showuser" .equals(line)) {
                         out.println( this .listOnlineUsers());
                         line = in.readLine();
                     }
                     //第一次進入,保存名字
                     if (flag++ == 0 ){
                         name = line;
                         user_list.add(name);
                         thread_list.add( this );
                         out.println(name + "你好,可以開始聊天了..." );
                         this .pushMessage( "Client<" + name + ">進入聊天室..." );
                     } else {
                         this .pushMessage( "Client<" + name + "> say : " + line);
                     }
                     line = in.readLine();
                 }
                 out.println( "byeClient" );
             } catch (Exception e) {
                 e.printStackTrace();
             } finally { //用戶退出聊天室
                 try {
                     client.close();
                 } catch (IOException e) {
                     e.printStackTrace();
                 }
                 thread_list.remove( this );
                 user_list.remove(name);
                 pushMessage( "Client<" + name + ">退出了聊天室" );
             }
         }
          
         //放入消息隊列末尾,准備發送給客戶端
         private void pushMessage(String msg){
             message_list.addLast(msg);
             isPrint = true ;
         }
          
         //向客戶端發送一條消息
         private void sendMessage(String msg){
             out.println(msg);
         }
          
         //統計在線用戶列表
         private String listOnlineUsers() {
             String s = "--- 在線用戶列表 ---\015\012" ;
             for ( int i = 0 ; i < user_list.size(); i++) {
                 s += "[" + user_list.get(i) + "]\015\012" ;
             }
             s += "--------------------" ;
             return s;
         }
     }
      
     public static void main(String[] args) throws IOException {
         new Server(); //啟動服務端
     }
}

八、文件傳輸

客戶端向服務器端傳送文件,服務端可獲取文件名用於保存,獲取文件大小計算傳輸進度,比較簡單,直接貼代碼。

客戶端代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package sock;
 
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.Socket;
  
/**
  * 客戶端
  */
public class Client extends Socket{
      
     private static final String SERVER_IP = "127.0.0.1" ;
     private static final int SERVER_PORT = 2013 ;
      
     private Socket client;
     private FileInputStream fis;
     private DataOutputStream dos;
      
     public Client(){
         try {
             try {
                 client = new Socket(SERVER_IP, SERVER_PORT);
                 //向服務端傳送文件
                 File file = new File( "c:/test.doc" );
                 fis = new FileInputStream(file);
                 dos = new DataOutputStream(client.getOutputStream());
                  
                 //文件名和長度
                 dos.writeUTF(file.getName());
                 dos.flush();
                 dos.writeLong(file.length());
                 dos.flush();
                  
                 //傳輸文件
                 byte [] sendBytes = new byte [ 1024 ];
                 int length = 0 ;
                 while ((length = fis.read(sendBytes, 0 , sendBytes.length)) > 0 ){
                     dos.write(sendBytes, 0 , length);
                     dos.flush();
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 if (fis != null )
                     fis.close();
                 if (dos != null )
                     dos.close();
                 client.close();
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
      
     public static void main(String[] args) throws Exception {
         new Client();
     }
}

服務器端代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
package sock;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
  
/**
  * 服務器
  */
public class Server extends ServerSocket{
  
     private static final int PORT = 2013 ;
      
     private ServerSocket server;
     private Socket client;
     private DataInputStream dis;
     private FileOutputStream fos;
      
     public Server() throws Exception{
         try {
             try {
                 server = new ServerSocket(PORT);
                  
                 while ( true ){
                     client = server.accept();
                      
                     dis = new DataInputStream(client.getInputStream());
                     //文件名和長度
                     String fileName = dis.readUTF();
                     long fileLength = dis.readLong();
                     fos = new FileOutputStream( new File( "d:/" + fileName));
                      
                     byte [] sendBytes = new byte [ 1024 ];
                     int transLen = 0 ;
                     System.out.println( "----開始接收文件<" + fileName + ">,文件大小為<" + fileLength + ">----" );
                     while ( true ){
                         int read = 0 ;
                         read = dis.read(sendBytes);
                         if (read == - 1 )
                             break ;
                         transLen += read;
                         System.out.println( "接收文件進度" + 100 * transLen/fileLength + "%..." );
                         fos.write(sendBytes, 0 , read);
                         fos.flush();
                     }
                     System.out.println( "----接收文件<" + fileName + ">成功-------" );
                     client.close();
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally {
                 if (dis != null )
                     dis.close();
                 if (fos != null )
                     fos.close();
                 server.close();
             }
         } catch (Exception e) {
             e.printStackTrace();
         }
     }
      
     public static void main(String[] args) throws Exception {
         new Server();
     }
}

 

原文見:http://my.oschina.net/leejun2005/blog/104955


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM