2017 - 10 -31 網絡編程 Socket UDP TCP

1 網絡編程概述
計算機網絡 
  是指將地理位置不同的具有獨立功能更的多臺計算機及其外部設備,通過通信線路連接起來,在網絡操作系統,網絡管理軟件及網絡通信協議的管理和協調下,實現資源共享和信息傳遞的計算機系統。
網絡編程
  就是用來實現網絡互連的不同計算機上運行的程序間可以進行數據交換。
   
2 網絡模型
  計算機網絡之間以何種規則進行通信,就是網絡模型研究問題。
 
                
 OSI參考模型      TCP/IP參考模型
  ---------                    ----------
  應用層              
  ---------                 
  表示層                    應用層 
  ---------
  會話層
  ---------                   -----------
  傳輸層                    傳輸層
  ---------                   -----------
  網絡層                   網絡層
  ---------                   -----------
  數據鏈路層      
  ---------              主機至網絡層
  物理層          
  ---------                   -----------             

網絡模型7層概述:
1.物理層:主要定義物理設備標準,如網線的接口類型、光纖的接口類型、各種傳輸介質的傳輸速率等。它的主要作用是傳輸比特流(就是由1、0轉化爲電流強弱來進行傳輸,到達目的地後在轉化爲1、0,也就是我們常說的數模轉換與模數轉換)。這一層的數據叫做比特。 
2. 數據鏈路層:主要將從物理層接收的數據進行MAC地址(網卡的地址)的封裝與解封裝。常把這一層的數據叫做幀。在這一層工作的設備是交換機,數據通過交換機來傳輸。 
3. 網絡層:主要將從下層接收到的數據進行IP地址(例192.168.0.1)的封裝與解封裝。在這一層工作的設備是路由器,常把這一層的數據叫做數據包。 
4. 傳輸層:定義了一些傳輸數據的協議和端口號(WWW端口80等),如:TCP(傳輸控制協議,傳輸效率低,可靠性強,用於傳輸可靠性要求高,數據量大的數據),UDP(用戶數據報協議,與TCP特性恰恰相反,用於傳輸可靠性要求不高,數據量小的數據,如QQ聊天數據就是通過這種方式傳輸的)。 主要是將從下層接收的數據進行分段和傳輸,到達目的地址後再進行重組。常常把這一層數據叫做段。 
5.會話層:通過傳輸層(端口號:傳輸端口與接收端口)建立數據傳輸的通路。主要在你的系統之間發起會話或者接受會話請求(設備之間需要互相認識可以是IP也可以是MAC或者是主機名) 
6.表示層:主要是進行對接收的數據進行解釋、加密與解密、壓縮與解壓縮等(也就是把計算機能夠識別的東西轉換成人能夠能識別的東西(如圖片、聲音等)。 
7.應用層: 主要是一些終端的應用,比如說FTP(各種文件下載),WEB(IE瀏覽),QQ之類的(可以把它理解成我們在電腦屏幕上可以看到的東西.就是終端應用)。
     
3 網絡編程三要素
  A:IP地址
  B:端口
  C:協議

(1)
IP地址:
     網絡中計算機的唯一標識。

     計算機只能識別二進制的數據,所以我們的IP地址應該是一個二進制的數據。
     但是呢,我們配置的IP地址確不是二進制的,爲什麼呢?
           IP:192.168.1.100
           換算:11000000 10101000 00000001 01100100
        假如真是:11000000 10101000 00000001 01100100的話。
        我們如果每次再上課的時候要配置該IP地址,記憶起來就比較的麻煩。
        所以,爲了方便表示IP地址,我們就把IP地址的每一個字節上的數據換算成十進制,然後用.分開來表示:
              "點分十進制"

           IP地址的組成:網絡號段+主機號段
               A類:第一號段爲網絡號段+後三段的主機號段
                      一個網絡號:256*256*256 = 16777216
               B類:前二號段爲網絡號段+後二段的主機號段
                      一個網絡號:256*256 = 65536
               C類:前三號段爲網絡號段+後一段的主機號段
                      一個網絡號:256

         IP地址的分類:
                A類     1.0.0.1---127.255.255.254     (1)10.X.X.X是私有地址(私有地址就是在互聯網上不使用,而被用在局域網絡中的地址)                                                        (2)127.X.X.X是保留地址,用做循環測試用的。
                B類     128.0.0.1---191.255.255.254      172.16.0.0---172.31.255.255是私有地址。169.254.X.X是保留地址。
                C類     192.0.0.1---223.255.255.254      192.168.X.X是私有地址
                D類     224.0.0.1---239.255.255.254
                E類     240.0.0.1---247.255.255.254

        兩個DOS命令:
                ipconfig 查看本機ip地址
                ping 後面跟ip地址。測試本機與指定的ip地址間的通信是否有問題

        特殊的IP地址:
                127.0.0.1 迴環地址(表示本機)
                x.x.x.255 廣播地址
                x.x.x.0 網絡地址
(2)
端口號:
        正在運行的程序的標識。
        有效端口:0~65535,其中0~1024系統使用或保留端口。
(3)
協議:
        通信的規則

        UDP: 
                把數據打包
                數據有限制
                不建立連接
                速度快
                不可靠

        TCP:
                建立連接通道
                數據無限制
                速度慢
                可靠

      舉例:
            UDP:發短信
            TCP:打電話

4 InetAddress類的概述
如果一個類沒有構造方法:
   A:成員全部是靜態的(Math,Arrays,Collections)
   B:單例設計模式(Runtime)
   C:類中有靜態方法返回該類的對象(InetAddress)
           class Demo {
                  private Demo(){}
 
                  public static Demo getXxx() {
                            return new Demo();
                  }
         }

看InetAddress的成員方法:
public static InetAddress getByName(String host):根據主機名或者IP地址的字符串表示得到IP地址對象

public class InetAddressDemo {
         public static void main(String[] args) throws UnknownHostException {
                // public static InetAddress getByName(String host)
                // InetAddress address = InetAddress.getByName("liuyi");
                // InetAddress address = InetAddress.getByName("192.168.12.92");
                InetAddress address = InetAddress.getByName("192.168.12.63");

                // 獲取兩個東西:主機名,IP地址
                // public String getHostName()
                String name = address.getHostName();
                // public String getHostAddress()
                String ip = address.getHostAddress();
                System.out.println(name + "---" + ip);
}
}

5 Socket
Socket:網絡套接字
Socket:網絡編程,套接字編程
Socket包含了:IP地址+端口
Socket原理機制:
      通信的兩端都有Socket
      網絡通信其實就是Socket間的通信
      數據在兩個Socket間通過IO傳輸
--------------------------------------------------------------------------
 計算機A                                                                   計算機B     |
     QQ                                                                         QQ         |
  Socket           在某種協議下通過IO流傳輸               Socket       |
(IP和端口)--------------------------------------------(IP和端口)   |
---------------------------------------------------------------------------

6 UDP協議
(1)UDP協議發送數據
 A:創建發送端Socket對象
 B:創建數據,並把數據打包
 C:調用Socket對象的發送方法發送數據包
 D:釋放資源

public class SendDemo {
         public static void main(String[] args) throws IOException {
                // 創建發送端Socket對象
                // DatagramSocket()
                DatagramSocket ds = new DatagramSocket();

                // 創建數據,並把數據打包 數據 長度 IP 端口
                // DatagramPacket(byte[] buf, int length, InetAddress address, int port)
                // 創建數據
                byte[] bys = "hello,udp,我來了".getBytes();
                // 長度
                int length = bys.length;
                // IP地址對象
                InetAddress address = InetAddress.getByName("192.168.12.92");
                // 端口
                int port = 10086;
                DatagramPacket dp = new DatagramPacket(bys, length, address, port);

                // 調用Socket對象的發送方法發送數據包
                // public void send(DatagramPacket p)
                ds.send(dp);

                // 釋放資源
                ds.close();
       }
}
(2)UDP協議接收數據
 A:創建接收端Socket對象
 B:創建一個數據包(接收容器)
 C:調用Socket對象的接收方法接收數據
 D:解析數據包,並顯示在控制檯
 E:釋放資源 

public class ReceiveDemo {
        public static void main(String[] args) throws IOException {
                // 創建接收端Socket對象
                // DatagramSocket(int port)
                DatagramSocket ds = new DatagramSocket(10086);

                // 創建一個數據包(接收容器)
                // DatagramPacket(byte[] buf, int length)
                byte[] bys = new byte[1024];
                int length = bys.length;
                DatagramPacket dp = new DatagramPacket(bys, length);

                // 調用Socket對象的接收方法接收數據
                // public void receive(DatagramPacket p)
                ds.receive(dp); // 阻塞式

                // 解析數據包,並顯示在控制檯
                // 獲取對方的ip
                // public InetAddress getAddress()
                InetAddress address = dp.getAddress();
                String ip = address.getHostAddress();
                // public byte[] getData():獲取數據緩衝區
                // public int getLength():獲取數據的實際長度
                byte[] bys2 = dp.getData();
                int len = dp.getLength();
                String s = new String(bys2, 0, len);
                System.out.println(ip + "傳遞的數據是:" + s);

                // 釋放資源
                ds.close();
        }
}

(3)UDP發送和接收數據圖解



7 UDP優化
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
public class ReceiveDemo {
        public static void main(String[] args) throws IOException {
                // 創建接收端的Socket對象
                DatagramSocket ds = new DatagramSocket(12345);

                // 創建一個包裹
                byte[] bys = new byte[1024];
                DatagramPacket dp = new DatagramPacket(bys, bys.length);

                // 接收數據
                ds.receive(dp);

                // 解析數據
                String ip = dp.getAddress().getHostAddress();
                String s = new String(dp.getData(), 0, dp.getLength());
                System.out.println("from " + ip + " data is : " + s);

                // 釋放資源
                ds.close();
        }
}

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class SendDemo {
      public static void main(String[] args) throws IOException {
                // 創建發送端的Socket對象
                DatagramSocket ds = new DatagramSocket();

                // 創建數據並打包
                byte[] bys = "helloworld".getBytes();
                DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.12.92"), 12345);

                // 發送數據
                ds.send(dp);

                // 釋放資源
                ds.close();
}
}

8 發送端的數據來自於鍵盤錄入案例
public class ReceiveDemo {
       public static void main(String[] args) throws IOException {
                // 創建接收端的Socket對象
                DatagramSocket ds = new DatagramSocket(12345);

                while (true) {
                     // 創建一個包裹
                     byte[] bys = new byte[1024];
                     DatagramPacket dp = new DatagramPacket(bys, bys.length);

                     // 接收數據
                     ds.receive(dp);

                     // 解析數據
                     String ip = dp.getAddress().getHostAddress();
                     String s = new String(dp.getData(), 0, dp.getLength());
                     System.out.println("from " + ip + " data is : " + s);
          }

                // 釋放資源
                // 接收端應該一直開着等待接收數據,是不需要關閉
                // ds.close();
       }
}
 
 數據來自於鍵盤錄入
 鍵盤錄入數據要自己控制錄入結束。
 
public class SendDemo {
      public static void main(String[] args) throws IOException {
                // 創建發送端的Socket對象
                DatagramSocket ds = new DatagramSocket();

                // 封裝鍵盤錄入數據
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                String line = null;
                while ((line = br.readLine()) != null) {
                      if ("886".equals(line)) {
                                  break;
                        }

                        // 創建數據並打包
                        byte[] bys = line.getBytes();
                        // DatagramPacket dp = new DatagramPacket(bys, bys.length,
                        // InetAddress.getByName("192.168.12.92"), 12345);
                        DatagramPacket dp = new DatagramPacket(bys, bys.length,
                        InetAddress.getByName("192.168.12.255"), 12345);

                        // 發送數據
                        ds.send(dp);
              }

               // 釋放資源
               ds.close();
        }
}

9 多線程實現聊天室程序
通過多線程改進剛纔的聊天程序,這樣我就可以實現在一個窗口發送和接收數據了。
public class ChatRoom {
        public static void main(String[] args) throws IOException {
                DatagramSocket dsSend = new DatagramSocket();
                DatagramSocket dsReceive = new DatagramSocket(12306);

                SendThread st = new SendThread(dsSend);
                ReceiveThread rt = new ReceiveThread(dsReceive);

                Thread t1 = new Thread(st);
                Thread t2 = new Thread(rt);

                t1.start();
                t2.start();
      }
}
public class SendThread implements Runnable {

        private DatagramSocket ds;

        public SendThread(DatagramSocket ds) {
              this.ds = ds;
       }
        @Override
        public void run() {
                 try {
                     // 封裝鍵盤錄入數據
                     BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                     String line = null;
                     while ((line = br.readLine()) != null) {
                         if ("886".equals(line)) {
                                      break;
                      }

                      // 創建數據並打包
                      byte[] bys = line.getBytes();
                      // DatagramPacket dp = new DatagramPacket(bys, bys.length,
                      // InetAddress.getByName("192.168.12.92"), 12345);
                      DatagramPacket dp = new DatagramPacket(bys, bys.length,InetAddress.getByName("192.168.12.255"), 12306);
                      // 發送數據
                      ds.send(dp);
       }

                      // 釋放資源
                      ds.close();
               } catch (IOException e) {
                   e.printStackTrace();
          }
     }
}

public class ReceiveThread implements Runnable {
        private DatagramSocket ds;
        public ReceiveThread(DatagramSocket ds) {
        this.ds = ds;
       }
        @Override
        public void run() {
            try {
               while (true) {
                 // 創建一個包裹
                 byte[] bys = new byte[1024];
                 DatagramPacket dp = new DatagramPacket(bys, bys.length);
                 // 接收數據
                 ds.receive(dp);

                 // 解析數據
                 String ip = dp.getAddress().getHostAddress();
                 String s = new String(dp.getData(), 0, dp.getLength());
                 System.out.println("from " + ip + " data is : " + s);
        }
           } catch (IOException e) {
                      e.printStackTrace();
                }
        }
}

10 TCP協議
(1)TCP協議發送數據
A:創建發送端的Socket對象
      這一步如果成功,就說明連接已經成功了。
B:獲取輸出流,寫數據
C:釋放資源

連接被拒絕。TCP協議一定要先看服務器。
java.net.ConnectException: Connection refused: connect

public class ClientDemo {
         public static void main(String[] args) throws IOException {
                // 創建發送端的Socket對象
                // Socket(InetAddress address, int port)
                // Socket(String host, int port)
                // Socket s = new Socket(InetAddress.getByName("192.168.12.92"), 8888);

                Socket s = new Socket("192.168.12.92", 8888);

                // 獲取輸出流,寫數據
                // public OutputStream getOutputStream()
                OutputStream os = s.getOutputStream();

                os.write("hello,tcp,我來了".getBytes());

                // 釋放資源
                s.close();
      }
}

(2) TCP協議接收數據
A:創建接收端的Socket對象
B:監聽客戶端連接。返回一個對應的Socket對象
C:獲取輸入流,讀取數據顯示在控制檯
D:釋放資源
public class ServerDemo {
       public static void main(String[] args) throws IOException {
                // 創建接收端的Socket對象
                // ServerSocket(int port)
                ServerSocket ss = new ServerSocket(8888);

                // 監聽客戶端連接。返回一個對應的Socket對象
                // public Socket accept()
                Socket s = ss.accept(); // 偵聽並接受到此套接字的連接。此方法在連接傳入之前一直阻塞。

                // 獲取輸入流,讀取數據顯示在控制檯
                InputStream is = s.getInputStream();

                byte[] bys = new byte[1024];
                int len = is.read(bys); // 阻塞式方法
                String str = new String(bys, 0, len);
                String ip = s.getInetAddress().getHostAddress();
                System.out.println(ip + "---" + str);

                // 釋放資源
                s.close();
                // ss.close(); //這個不應該關閉
       }
}

(3)TCP協議發送和接收數據圖解


11 服務器給客戶端一個反饋的案例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
       public static void main(String[] args) throws IOException {
                // 創建服務器Socket對象
                ServerSocket ss = new ServerSocket(9999);

                // 監聽客戶端的連接
                Socket s = ss.accept(); // 阻塞

                // 獲取輸入流
                InputStream is = s.getInputStream();
                byte[] bys = new byte[1024];
                int len = is.read(bys); // 阻塞
                String server = new String(bys, 0, len);
                System.out.println("server:" + server);

                // 獲取輸出流
                OutputStream os = s.getOutputStream();
                os.write("數據已經收到".getBytes());

                // 釋放資源
                s.close();
                // ss.close();
         }
}

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ClientDemo {
       public static void main(String[] args) throws IOException {
                // 創建客戶端Socket對象
                Socket s = new Socket("192.168.12.92", 9999);

                // 獲取輸出流
                OutputStream os = s.getOutputStream();
                os.write("今天天氣很好,適合睡覺".getBytes());

                // 獲取輸入流
                InputStream is = s.getInputStream();
                byte[] bys = new byte[1024];
                int len = is.read(bys);// 阻塞
                String client = new String(bys, 0, len);
                System.out.println("client:" + client);

                // 釋放資源
                s.close();
      }
}

// 先開服務器 再開客戶端 
// 輸出:  
           client:數據已經收到
           server:今天天氣很好,適合睡覺

12 客戶端鍵盤錄入服務器控制檯輸出
public class ClientDemo {
        public static void main(String[] args) throws IOException {
                // 創建客戶端Socket對象
                Socket s = new Socket("192.168.12.92", 22222);

                // 鍵盤錄入數據
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
                 // 把通道內的流給包裝一下
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

                String line = null;
                while ((line = br.readLine()) != null) {
                    // 鍵盤錄入數據要自定義結束標記
                    if ("886".equals(line)) {
                               break;
                   }
                       bw.write(line);
                       bw.newLine();
                       bw.flush();
          }

                // 釋放資源
                // bw.close();
                // br.close();
                s.close();
        }
}
public class ServerDemo {
      public static void main(String[] args) throws IOException {
                // 創建服務器Socket對象
                ServerSocket ss = new ServerSocket(22222);

                // 監聽客戶端連接
                Socket s = ss.accept();

                // 包裝通道內容的流
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String line = null;
                while ((line = br.readLine()) != null) {
                        System.out.println(line);
                }

                // br.close();
                s.close();
                // ss.close();
}
}

13  客戶端鍵盤錄入,服務器輸出文本文件
public class ClientDemo {
         public static void main(String[] args) throws IOException {
                // 創建客戶端Socket對象
                Socket s = new Socket("192.168.12.92", 23456);

                // 封裝鍵盤錄入
                BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// 封裝通道內的數據
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

                String line = null;
                while ((line = br.readLine()) != null) {
                  if ("over".equals(line)) {
                              break;
                   }
                        bw.write(line);
                        bw.newLine();
bw.flush();
             }
                // bw.close();
                // br.close();
                s.close();
      }
}
public class ServerDemo {
       public static void main(String[] args) throws IOException {
                // 創建服務器Socket對象
                ServerSocket ss = new ServerSocket(23456);

                // 監聽客戶端連接
                Socket s = ss.accept();

                // 封裝通道內的數據
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                // 封裝文本文件
                BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));

                String line = null;
                while ((line = br.readLine()) != null) {
                     bw.write(line);
                     bw.newLine();
                     bw.flush();
         }

                bw.close();
                // br.close();
                s.close();
                // ss.close();
      }
}

14 客戶端文本文件,服務器輸出到控制檯
public class ClientDemo {
     public static void main(String[] args) throws IOException {
             // 創建Socket對象
             Socket s = new Socket("192.168.12.92", 34567);

             // 封裝文本文件
             BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
             // 封裝通道內的流
             BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

             String line = null;
             while ((line = br.readLine()) != null) {
                     bw.write(line);
                     bw.newLine();
                     bw.flush();
       }

             br.close();
             s.close();
        }
}
public class ServerDemo {
       public static void main(String[] args) throws IOException {
               // 創建服務器Socket對象
               ServerSocket ss = new ServerSocket(34567);
               // 監聽客戶端連接
               Socket s = ss.accept();
               // 封裝通道內的流
               BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
               String line = null;
               while ((line = br.readLine()) != null) {
                     System.out.println(line);
           }
             s.close();
       }
}

15 客戶端文本文件,服務器輸出文本文件
public class UploadServer {
      public static void main(String[] args) throws IOException {
               // 創建服務器端的Socket對象
               ServerSocket ss = new ServerSocket(11111);

               // 監聽客戶端連接
               Socket s = ss.accept();

               // 封裝通道內的流
               BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
               // 封裝文本文件
               BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));

               String line = null;
               while ((line = br.readLine()) != null) {
                       bw.write(line);
                       bw.newLine();
                       bw.flush();
           }

                bw.close();
                s.close();
      }
}

public class UploadClient {
      public static void main(String[] args) throws IOException {
              // 創建客戶端Socket對象
              Socket s = new Socket("192.168.12.92", 11111);

              // 封裝文本文件
              BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
              // 封裝通道內流
              BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

              String line = null;
              while ((line = br.readLine()) != null) {
                     bw.write(line);
                     bw.newLine();
                     bw.flush();
           }

             // 釋放資源
             br.close();
             s.close();
        }
}

16 TCP上傳文本文件並給出反饋

按照我們正常的思路加入反饋信息,結果卻沒反應。爲什麼呢?
讀取文本文件是可以以null作爲結束信息的,但是呢,通道內是不能這樣結束信息的。
所以,服務器根本就不知道你結束了。而你還想服務器給你反饋。所以,就相互等待了。

如何解決呢?
A:在多寫一條數據,告訴服務器,讀取到這條數據說明我就結束,你也結束吧。
             這樣做可以解決問題,但是不好。
B:Socket對象提供了一種解決方案
             public void shutdownOutput()

public class UploadServer {
        public static void main(String[] args) throws IOException {
               // 創建服務器端的Socket對象
               ServerSocket ss = new ServerSocket(11111);

               // 監聽客戶端連接
               Socket s = ss.accept();// 阻塞

               // 封裝通道內的流
               BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
               // 封裝文本文件
               BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java"));

               String line = null;
               while ((line = br.readLine()) != null) { // 阻塞
               // if("over".equals(line)){
               // break;
               // }
                      bw.write(line);
                      bw.newLine();
                      bw.flush();
             }

               // 給出反饋
               BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
               bwServer.write("文件上傳成功");
               bwServer.newLine();
               bwServer.flush();

               // 釋放資源
               bw.close();
               s.close();
      }
}

public class UploadClient {
         public static void main(String[] args) throws IOException {
               // 創建客戶端Socket對象
               Socket s = new Socket("192.168.12.92", 11111);

               // 封裝文本文件
               BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
               // 封裝通道內流
               BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

                String line = null;
                while ((line = br.readLine()) != null) { // 阻塞
                       bw.write(line);
                       bw.newLine();
                       bw.flush();
           }

                //自定義一個結束標記
                //bw.write("over");
                //bw.newLine();
                //bw.flush();

                //Socket提供了一個終止,它會通知服務器你別等了,我沒有數據過來了
                s.shutdownOutput();

                // 接收反饋
                BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String client = brClient.readLine(); // 阻塞
                System.out.println(client);

                // 釋放資源
                br.close();
                s.close();
        }
}

17 多個客戶端上傳到服務器
通過while循環可以改進一個服務器接收多個客戶端。
但是這個是有問題的。
如果是這種情況,假設我還有張三,李四,王五這三個人分別執行客戶端
張三:好好學習.avi(100M)              256k
李四:天天向上.mp3(3M)                 1M
王五:ILoveJava.txt(1k)              100M
這樣會依次執行。。。所以應該用多線程

//客戶端不用改
public class UploadClient {
         public static void main(String[] args) throws IOException {
               // 創建客戶端Socket對象
               Socket s = new Socket("192.168.12.92", 11111);

               // 封裝文本文件
               BufferedReader br = new BufferedReader(new FileReader("InetAddressDemo.java"));
               // 封裝通道內流
               BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

                String line = null;
                while ((line = br.readLine()) != null) { // 阻塞
                       bw.write(line);
                       bw.newLine();
                       bw.flush();
           }

                //自定義一個結束標記
                //bw.write("over");
                //bw.newLine();
                //bw.flush();

                //Socket提供了一個終止,它會通知服務器你別等了,我沒有數據過來了
                s.shutdownOutput();

                // 接收反饋
                BufferedReader brClient = new BufferedReader(new InputStreamReader(s.getInputStream()));
                String client = brClient.readLine(); // 阻塞
                System.out.println(client);

                // 釋放資源
                br.close();
                s.close();
        }
}

public class UserThread implements Runnable {
     private Socket s;

     public UserThread(Socket s) {
            this.s = s;
        }

          @Override
         public void run() {
        try {
                // 封裝通道內的流
                BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
                // 封裝文本文件
                // BufferedWriter bw = new BufferedWriter(new
                // FileWriter("Copy.java"));

                // 爲了防止名稱衝突
                String newName = System.currentTimeMillis() + ".java";
                BufferedWriter bw = new BufferedWriter(new FileWriter(newName));

                String line = null;
                while ((line = br.readLine()) != null) { // 阻塞
                     bw.write(line);
                     bw.newLine();
                     bw.flush();
                    }

                // 給出反饋
                BufferedWriter bwServer = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
                bwServer.write("文件上傳成功");
                bwServer.newLine();
                bwServer.flush();

                // 釋放資源
                bw.close();
                   s.close();
              } catch (IOException e) {
                 e.printStackTrace();
               }
         }
}

public class UploadServer {
      public static void main(String[] args) throws IOException {
                // 創建服務器Socket對象
                ServerSocket ss = new ServerSocket(11111);

                while (true) {
                  Socket s = ss.accept();
                  new Thread(new UserThread(s)).start();
               }
       }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章