server socket

這次在java實驗的時候,要求使用server socket編寫服務器和客戶端的網絡通信。最開始認爲應該是挺簡單的,但是後來發現低估了它。出現了不少的問題,所以也在這裏與大家分享。

問題描述

服務器程序的處理規則如下:
1) 向客戶端程序發送Verifying Server!。
2) 若讀口令次數超過3次,則發送Illegal User!給客戶端,程序退出。否則向下執行步驟3)。
3) 讀取客戶端程序提供的口令。
4) 若口令不正確,則發送PassWord Wrong!給客戶端,並轉步驟2),否則向下執行步驟5)。
5) 發送Registration Successful!給客戶端程序。

客戶端程序的處理規則如下:
1) 讀取服務器反饋信息。
2) 若反饋信息不是Verifying Server!,則提示Server Wrong!,程序退出。否則向下執行步驟3)
3) 提示輸入PassWord並將輸入的口令發送給服務器。
4) 讀取服務器反饋信息。
5) 若反饋信息是Illegal User!,則提示Illegal User!,程序退出。否則向下執行步驟6)
6) 若反饋信息是PassWord Wrong!,則提示PassWord Wrong!,並轉步驟3),否則向下執行步驟。
7) 輸出Registration Successful!。

初步實現

首先,我們一定要清楚,這次和之前的程序不同,雖然都是在本地上,但是服務器客戶端需要兩個啓動程序來實現,以達到我們模擬遠程連接的效果。

然後就是如何利用socket實現我們的功能了。

clipboard.png

通過上面的圖示,我們可以知道,首先需要先開啓服務器,然後等待客戶端的連接。

當客戶端通過socket進行連接後,服務器端也會建立一個socket對象來幫助實現服務器和客戶端的通信。

這時候就建立了一個TCP連接,我們就可以在服務器寫數據,然後在客戶端讀取了,實現雙方通信。

最後,當有一方決定通信結束,就會關閉連接。通信結束。

下面是初步實現的代碼:

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 服務器
 */
public class ServerTest {
    public static void main(String[] args) {
        try {
            ServerSocket serverSocket = new ServerSocket(8080);
            Socket clientSocket = serverSocket.accept();

            String welcome  = "verifying server!";
            OutputStream outputStream = clientSocket.getOutputStream();
            outputStream.write(welcome.getBytes());

            InputStream inputStream = clientSocket.getInputStream();
            int time = 0;

            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            String password = bufferedReader.readLine();

            // 獲取登錄信息,允許3次登錄
            while (time < 3) {
                if (password.equals("123")) {
                    outputStream.flush();
                    outputStream.write("Registration Successful!".getBytes());
                    break;
                } else {
                    outputStream.write("PassWord Wrong!".getBytes());
                    outputStream.flush();
                    password = bufferedReader.readLine();
                    time++;
                }
            }

            if (time >= 3) {
                outputStream.flush();
                outputStream.write("Illegal User!".getBytes());
            }

            outputStream.close();
            clientSocket.close();
            serverSocket.close();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.Socket;

/**
 * 客戶端
 */
public class ClientTest {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8080);

            String aline = new String();
            
            // 獲取輸入流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            aline = bufferedReader.readLine();

            // 訪問失敗
            if (!aline.equals("verifying server!")) {
                System.out.println("Server Wrong!");
                return;
            }

            // 獲取響應結果
            String feedback = bufferedReader.readLine();

            while (feedback == null || feedback.equals("PassWord Wrong!")) {
                if (feedback != null) {
                    if (feedback.equals("PassWord Wrong!")) {
                        System.out.println("PassWord Wrong!");
                    } else if (feedback.equals("Registration Successful!")) {
                        System.out.println("Registration Successful!");
                        break;
                    }
                }

                System.out.println("輸入密碼:");
                
                // 輸入密碼
                Scanner scanner = new Scanner(System.in);
                OutputStream outputStream = socket.getOutputStream();
                String password = scanner.nextLine();
                outputStream.write(password.getBytes());
                
                feedback = bufferedReader.readLine();
            }

            // 關閉連接
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

初步實現後,運行:一片空白。什麼都沒有發生。

出問題了很正常,斷點調試一下吧。發現問題所在:

clipboard.png

客戶端執行到這一行時,停止,然後服務器端接着執行;

clipboard.png

同樣的服務器端也到這行就停止了。

原因是服務器一方等着輸入密碼,而客戶端一方等着響應結果,然後又沒有開始輸入密碼。所以雙方就這麼一直等着,誰都不動了。

解決

通過上面的分析,我們只要將僵局打破就能夠解決問題。所以就先輸入密碼,然後再獲取響應結果。這樣就不會出問題了。

所以服務器端的代碼並不需要做什麼改動,只用修改客戶端程序就行了。

import java.io.*;
import java.net.Socket;

/**
 * 客戶端
 */
public class ClientTest {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8080);

            String aline = new String();
            
            // 獲取輸入流
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            aline = bufferedReader.readLine();

            // 訪問失敗
            if (!aline.equals("verifying server!")) {
                System.out.println("Server Wrong!");
                return;
            }

            // 主要修改這裏,不先獲取響應結果
            // 初始化響應結果
            String feedback = null;

            while (true) {
                if (feedback != null) {
                    if (feedback.equals("PassWord Wrong!")) {
                        System.out.println("PassWord Wrong!");
                    } else if (feedback.equals("Registration Successful!")) {
                        System.out.println("Registration Successful!");
                        break;
                    }
                }

                System.out.println("輸入密碼:");
                
                // 輸入密碼
                Scanner scanner = new Scanner(System.in);
                OutputStream outputStream = socket.getOutputStream();
                String password = scanner.nextLine();
                outputStream.write(password.getBytes());
                
                feedback = bufferedReader.readLine();
            }

            // 關閉連接
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

clipboard.png

成功實現!

總結

這次的實驗給我提了個醒,看似簡單的東西,也永遠不要輕視它。只有拿出應有的實例去解決問題,問題纔是你眼中那個簡單的問題。

後注:在瀏覽網上他人實現的時候,發現有用多線程來實現的,後面我也會對這篇文章做出更新,將多線程的升級版實現與大家分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章