HUST-計算機網絡實驗-socket編程


隨筆---HUST計網實驗:socket編程

博主大三在讀,第一次寫隨筆,水平有限,就當記錄一下學習的過程,順便面試前復習項目的時候看看。

實驗要求:

編寫一個 Web 服務器軟件,要求如下:

基本要求:

1.可配置 Web 服務器的監聽地址、監聽端口和主目錄(不得寫在代碼里面,不能每配置一次都要重編譯代碼);

2.能夠單線程處理一個請求。當一個客戶(瀏覽器,如輸入

“URL:http:// 202.103.2.3/index.html”)連接時創建一個連接套接字;

3.從連接套接字接收 http 請求報文,並根據請求報文的確定用戶請求的網頁文件;

4.從服務器的文件系統獲得請求的文件。 創建一個由請求的文件組成的 http 響應報文。;

5.經 TCP 連接向請求的瀏覽器發送響應,瀏覽器可以正確顯示網頁的內容;

高級要求:

1.能夠傳輸包含多媒體(如圖片)的網頁給客戶端,並能在客戶端正確顯示;

2.在服務器端的屏幕上輸出請求的來源(IP 地址、端口號和 HTTP 請求命令行);

3.在服務器端的屏幕上能夠輸出對每一個請求處理的結果;

4.對於無法成功定位文件的請求,根據錯誤原因,作相應錯誤提示,並具備一定的異常情況處理能力。

Socket套接字介紹:

Socket 是一個抽象概念,代表了通信雙方的端點(Endpoint),通信雙方通過 Socket 發送或接收數據。為了將應用程序和底層的網絡通信協議屏蔽開來,采用套接字(Socket)這樣一個抽象概念來作為應用程序和底層網絡之間的應用程序編程接口(API)。因為網絡應用程序是進程之間的通信,為了唯一的標識通信對等方的通信進程,套接字必須包含 2 種信息:(1) 通信對等方的網絡地址。(2) 通信對等方的進程號,通常叫端口號。

構造方法(常用):ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException。參數port指定服務器要綁定的端口(服務器要監聽的端口),參數backlog指定客戶連接請求隊列的長度,參數bindAddr指定服務器要綁定的IP地址。

java中的Socket類主要包括兩個:服務器端ServerSocket和客戶端Socket。

系統實現:

實驗要求可配置web服務器的監聽地址、端口,且不能寫在文件里面,因此可以創建一個新的配置文件config.data,內容分兩行,包含端口和ip的配置信息,用/結束。

內容如下:

點擊查看代碼
port:5050/
inetaddr:10.21.207.240/

主類MultiThreadsServer:

public class MultiThreadsServer {
    // Main method
    public static void main(String[] args) {
        int Port_File = 0;//服務器要綁定的端口
        int state = 0;
        try {
            //讀取配置文件內容
            BufferedReader ConfigReader = new BufferedReader(new FileReader("D:\\新建qq文件保存位置\\netlab1\\netlab1\\src\\config.data"));
            String temp_str = "",addr="";//addr:要綁定的ip地址
            while ((temp_str = ConfigReader.readLine()) != null) {
                if (temp_str.contains("port:")) {
                    temp_str = temp_str.substring(temp_str.indexOf("port:") + 5, temp_str.indexOf("/")); //每一行以"/"結尾
                    Port_File = Integer.parseInt(temp_str);
                    state++;
                }
                if (temp_str.contains("inetaddr:")) {
                    temp_str = temp_str.substring(temp_str.indexOf("inetaddr:") + 9, temp_str.indexOf("/"));
                    addr=temp_str;
                    state++;
                }
                if (state == 2)
                    break;  //讀取完port和inetaddr就停止,防止文件內容不符合要求
            }
            // 創建一個server socket
            ServerSocket serverSocket = new ServerSocket(Port_File, 10, InetAddress.getByName(addr));
            System.out.println("server is listening port:" + serverSocket.getLocalPort());
            // 給線程編號
            int i = 0;
            //用accept()方法監聽客戶端鏈接
            while (true) {
                Socket connectToClient = serverSocket.accept();
                // 在控制台上輸出連接號
                System.out.println("Starting thread " + i);
                // 為連接創建一個新的線程
                ThreadHandler thread = new ThreadHandler(connectToClient);
                // 線程執行
                thread.start();
                i++;
            }
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }
}

線程類ThreadHandler:

class ThreadHandler extends Thread {
    private Socket connectToClient; // 客戶端
    public ThreadHandler(Socket socket) {
        connectToClient = socket;
    }
    //實現run()方法
    public void run() {
        try {
            System.out.println("build a link with client:" + connectToClient.getPort());
            while (true) {
                //從socket建立輸入流
                InputStream socketInputStream = connectToClient.getInputStream();
                // 等待HTTP請求
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //字節數組buffer作為數據緩沖來讀取inputstream里面的數據
                byte[] buffer = new byte[socketInputStream.available()];//如果網絡被阻塞buffer大小為0,未被阻塞則正常接收到
                socketInputStream.read(buffer);
                String request = new String(buffer);
                //如果網絡被阻塞,request長度為0,跳過
                if (request.length() != 0) {
                    //輸出請求
                    System.out.println(request);
                    String firstLineOfRequest = request.substring(0, request.indexOf("\r\n"));
                    String[] firstLineParts = firstLineOfRequest.split(" ");
                    String uri = firstLineParts[1];//獲取uri
                    String filename1 = uri.replace("/","");
                    String filename2 = "D:\\新建qq文件保存位置\\netlab1\\netlab1\\src\\"+filename1;//獲取請求的文件名
                    File file1 = new File(filename2);
                    //文件不存在的時候,把uri設置為/,實驗要求不輸入文件名時無法定位的文件中,實際開發一般自動定位到index.html
                    if(!file1.exists()) uri = "/";
                    // 定義缺省狀態
                    if (uri.equals("/")) {
                        uri = "/error.html";    //把缺省狀態和無法定位狀態放到一起去,少寫一個缺省文件
                        firstLineParts[1] = "/error.html";
                        System.out.println("查找文件可能不存在");
                    }
                    String contentType; //文件類型
                    if (uri.contains(".html") || uri.contains(".htm")) {
                        contentType = "text/html";
                    } else {
                        if (uri.contains(".jpg") || uri.contains(".jpeg")) {
                            contentType = "image/jpeg";
                        } else {
                            contentType = "text/plain";
                        }
                    }
                    // 報文長度
                    long file_size = RequestLength("D:\\新建qq文件保存位置\\netlab1\\netlab1\\src\\" + uri); //*
                    // response result
                    String responseFirstLine = "HTTP/1.1 200 OK\r\n";
                    String responseHeader = "Content-Type:" + contentType + "\r\n";
                    String responseLength = "Content-Length:" + file_size + "\r\n\r\n";
                    InputStream inputStream = new FileInputStream("D:\\新建qq文件保存位置\\netlab1\\netlab1\\src\\" + uri); //*
                    OutputStream outputStream = connectToClient.getOutputStream(); //通過客戶端獲取outputStream
                    //輸出請求處理結果
                    outputStream.write(responseFirstLine.getBytes());
                    outputStream.write(responseHeader.getBytes());
                    outputStream.write(responseLength.getBytes());
                    System.out.println("Content-length=" + file_size);
                    System.out.println("uri"+uri);
                    int len = 0;
                    byte[] bytes = new byte[1024];
                    while ((len = inputStream.read(bytes)) != -1) {
                        outputStream.write(bytes, 0, len);
                    }
                    // 等待客戶接受HTTP響應結果
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                // connectToClient.close();
            }
        } catch (IOException ex) {
            System.err.println(ex);
        }
    }
    public static long RequestLength(String filename) {
        File file = new File(filename);
        if (!file.exists() || !file.isFile()) {
            System.out.println("404!Not Found File!");
            return -1;
        } else
            return file.length();
    }
}

實驗結果:

本地ip(10.21.207.240)+port(5050)+uri(/): 正確顯示未定位到

本地ip(10.21.207.240)+port(5050)+uri(/index.html):正確獲取

本地ip(10.21.207.240)+port(5050)+uri(/index111.html):對不存在的文件的訪問

把電腦和手機連上了同一個ip地址(此時為192.168.43.230),這樣就能在手機端顯示結果了:

實驗要求圓滿完成。服務器的文件就用靜態文件存放在當前src目錄下,如果要使用代碼的話記得改文件名稱和加自己的文件哦。


免責聲明!

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



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