天天看點

作業系統實驗報告一

作業系統實驗報告一

                       姓名:許恺

                       學号:2014011329

                       日期:9月29日

一.相關技術資料——》

Socket:

網絡上的兩個程式通過一個雙向的通信連接配接實作資料的交換,這個連接配接的一端稱為一個socket。

建立網絡通信連接配接至少要一對端口号(socket)。socket本質是程式設計接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程式員做網絡開發所用的接口,這就是Socket程式設計接口;HTTP是轎車,提供了封裝或者顯示資料的具體形式;Socket是發動機,提供了網絡通信的能力。

綁定

函數原型:

int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);

參數說明:

socket:是一個

套接字

描述符。

address:是一個sockaddr結構

指針

,該結構中包含了要結合的位址和

端口号

address_len:确定address

緩沖區

的長度。

傳回值:

如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。

接收

int recv(SOCKET socket, char FAR* buf, int len, int flags);

  

socket:一個辨別已連接配接

套接口

的描述字。

buf:用于接收資料的

len:緩沖區長度。

flags:指定調用方式。取值:MSG_PEEK 檢視目前資料,資料将被複制到緩沖區中,但并不從輸入隊列中删除;MSG_OOB 處理

帶外資料

若無錯誤發生,recv()傳回讀入的位元組數。如果連接配接已中止,傳回0。否則的話,傳回SOCKET_ERROR錯誤,應用程式可通過WSAGetLastError()擷取相應

錯誤代碼

建立程序,以及傳遞socket參數

 bool bRet=::CreateProcess(

  NULL,

  szCommandLine,

  FALSE,

  CREATE_NEW_CONSOLE,

  &si,

  &pi);

 if(bRet)

 {

  ::CloseHandle(pi.hThread);

  ::CloseHandle(pi.hProcess);

1.函數說明:

WIN32API函數CreateProcess用來建立一個新的程序和它的主線程,這個新程序運作指定的可執行檔案。

2.函數原型:

BOOL CreateProcess

(

    LPCTSTR lpApplicationName,        

    LPTSTR lpCommandLine,        

    LPSECURITY_ATTRIBUTES lpProcessAttributes。

    LPSECURITY_ATTRIBUTES lpThreadAttributes,        

    BOOL bInheritHandles,        

    DWORD dwCreationFlags,

    LPVOID lpEnvironment,        

    LPCTSTR lpCurrentDirectory,        

    LPSTARTUPINFO lpStartupInfo,        

    LPPROCESS_INFORMATION lpProcessInformation 

);

3. 參數:

lpApplicationName:

指向一個NULL結尾的、用來指定可執行子產品的字元串。

     這個字元串可以使可執行子產品的絕對路徑,也可以是相對路徑,在後一種情況下,函數使用目前驅動器和目錄建立可執行子產品的路徑。

    這個參數可以被設為NULL,在這種情況下,可執行子產品的名字必須處于 lpCommandLine 參數的最前面并由空格符與後面的字元分開。

    這個被指定的子產品可以是一個Win32應用程式。如果适當的子系統在目前計算機上可用的話,它也可以是其他類型的子產品(如MS-DOS 或OS/2)。

 在Windows NT中,如果可執行子產品是一個16位的應用程式,那麼這個參數應該被設定為NULL,并且因該在lpCommandLine參數中指定可執行子產品的名稱。16位的應用程式是以DOS虛拟機或Win32上的Windows(WOW) 為程序的方式運作。

lpCommandLine:

指向一個NULL結尾的、用來指定要運作的指令行。

 這個參數可以為空,那麼函數将使用參數指定的字元串當作要運作的程式的指令行。

 如果lpApplicationName和lpCommandLine參數都不為空,那麼lpApplicationName參數指定将要被運作的子產品,lpCommandLine參數指定将被運作的子產品的指令行。新運作的程序可以使用GetCommandLine函數獲得整個指令行。C語言程式可以使用argc和argv參數。

如果lpApplicationName參數為空,那麼這個字元串中的第一個被空格分隔的要素指定可執行子產品名。如果檔案名不包含擴充名,那麼.exe将被假定為預設的擴充名。如果檔案名以一個點(.)結尾且沒有擴充名,或檔案名中包含路徑,.exe将不會被加到後面。如果檔案名中不包含路徑,Windows将按照如下順序尋找這個可執行檔案:

1.目前應用程式的目錄。

2.父程序的目錄。

  1. Windows目錄。可以使用GetWindowsDirectory函數獲得這個目錄。

     4.列在PATH環境變量中的目錄。

         如果被建立的程序是一個以MS-DOS或16位Windows為基礎的應用程式,lpCommandLine參數應該是一個以可執行檔案的檔案名作為第一個要素的絕對路徑,因為這樣做可以使32位Windows程式工作的很好,這樣設定lpCommandLine參數是最強壯的。

傳參

作業系統實驗報告一
作業系統實驗報告一

二.報告内容

1. 閱讀源代碼,并調試,使得在自己本地機上能夠顯示出網頁資訊

// WebServer1.0.cpp : 定義控制台應用程式的入口點。
//

#include "stdafx.h"


#include <iostream>
#include <fstream>
#include <stdio.h> 
#include <Winsock2.h> 
#include <string>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
//檔案路徑
static string dir = "D:\\xukai\\學習\\作業系統實驗\\webServer1\\webServer\\Debug";
void main(int argc, _TCHAR* argv[])
{
    //初始化WinSock庫
    WORD wVersionRequested;
    WSADATA wsaData;

    cout << "初始化庫成功" << endl;

    wVersionRequested = MAKEWORD(2, 2);
    int wsaret = WSAStartup(wVersionRequested, &wsaData);

    if (wsaret)
        return;
以上為網絡程式設計架構
    //建立SOCKET 
    SOCKET socketSrv;
    socketSrv = socket(AF_INET, SOCK_STREAM, 0);

    if (socketSrv == INVALID_SOCKET)
        return;
    cout << "建立socket成功" << endl;
    SOCKADDR_IN addrSrv;
    addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    addrSrv.sin_family = AF_INET;
    addrSrv.sin_port = htons(80);
以上為建立socket包,定義參數
    //綁定套接字
    if (bind(socketSrv, (struct sockaddr*)&addrSrv, sizeof(SOCKADDR)))
    {
        //關閉連接配接
        shutdown(socketSrv, 1);
        closesocket(socketSrv);
        WSACleanup();
        return;
    }
    cout << "綁定套接字成功!" << endl;
    //等待用戶端連接配接
    SOCKADDR_IN addrCli;
    int len = sizeof(SOCKADDR);
    //監聽端口
    if (listen(socketSrv, 5) == SOCKET_ERROR)
    {
        printf("監聽失敗!\n");
    }
    while (true)
    {
        SOCKET socketconn;
        socketconn = accept(socketSrv, (SOCKADDR*)&addrCli, &len);
        //接受連接配接
用戶端反應,服務端接收
        if (socketconn == SOCKET_ERROR)
        {
            printf("接受連接配接失敗!\n");
            return;
        }
        cout << "連接配接成功" << endl;
        /*    while(true)
        {*/

        //連接配接成功後與用戶端進行會話
        char recvBuff[1024];
        string sendBuff;
        string locDir;
        ifstream fp;

        //接收請求
        if (recv(socketconn, recvBuff, 1024, 0) == SOCKET_ERROR)
        {
            continue;
        }
請求用戶端發出的位址,放到recvBuff
        //讀取http請求頭
        string recvBuffer = recvBuff;
        int posGet = recvBuffer.find("GET", 0);

        int posHttp = recvBuffer.find("HTTP", 0);

        //截取html檔案路徑
        for (int pos = posGet + 4; pos<posHttp; pos++)
        {
            if (recvBuffer[pos] == '/')
            {
                locDir.push_back('\\');
                continue;
            }
            locDir.push_back(recvBuffer[pos]);
        }
翻譯為檔案位址路徑
        locDir = dir + locDir;

        //    locDir.insert(0,1,'.');
        //打開http請求檔案進行讀取
        fp.open(locDir.c_str(), std::ios::binary);
        //打開檔案失敗
        if (!fp.is_open())
        {

            cout << "請求檔案" << locDir.c_str() << "不存在" << endl;
        }
        else//打開檔案成功并讀取
        {
            char buffer[1024];

            while (fp.good() && !fp.eof())
            {
                fp.getline(buffer, 1024);
                //将讀取的内容追加入sendBuff中
                sendBuff.append(buffer);
                buffer[0] = '\0';
            }
讀檔案,将檔案寫到sendBuff中
        }
        fp.close();
        //響應請求,将頁面資訊發送到用戶端
        if (send(socketconn, sendBuff.c_str(), sendBuff.length(), 0) == SOCKET_ERROR)
        {
            continue;
        }
向用戶端發送網頁的内容
        shutdown(socketconn, 1);
        //}
        //關閉連接配接
        closesocket(socketconn);

    }
    //關閉連接配接
    shutdown(socketSrv, 1);
    closesocket(socketSrv);
    WSACleanup();

    return;
}      
作業系統實驗報告一

2.每接收到用戶端的連結請求,即建立一個程序,在此程序中完成與用戶端的通訊。新建立的程序能夠獲得使用者的請求的檔案,在磁盤的指定位置将此檔案讀取出來,并經過socket将檔案資訊傳回到申請用戶端(浏覽器),然後此程序執行結束。

Webserver.cpp
//編寫指令行參數
        wchar_t  pCmdLine[256];
        wsprintf(pCmdLine, L"D:\\xukai\\學習\\作業系統實驗\\webServer1\\Process\\Debug\\Process.exe %d", socketconn);
        //聲明建立程序參數
        LPPROCESS_INFORMATION pi=NULL;
        STARTUPINFO si = { sizeof(si) };
        //建立程序并驗證是否成功
        BOOL ret = CreateProcess(LPCTSTR("Process.exe"),pCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, pi);
        if (ret) {
            // 關閉子程序的主線程句柄
            CloseHandle(pi->hThread);
            
            // 關閉子程序句柄
            CloseHandle(pi->hProcess);
        }

Process.cpp
// Process.cpp : 定義控制台應用程式的入口點。
//
#pragma once

#include "stdafx.h"

#include <iostream>
#include <fstream>
#include <stdio.h> 
#include <Winsock2.h> 
#include <string>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
//檔案路徑
static string dir = "D:\\xukai\\學習\\作業系統實驗\\webServer1\\webServer\\Debug";
void main(int argc, CHAR* argv[])
{
    cout << argc << endl;
    cout << argv[0] << endl;
    cout << argv[1] << endl;
    //連接配接成功後與用戶端進行會話
    char recvBuff[1024];
    string sendBuff;
    string locDir;
    ifstream fp;
    SOCKET socketconn = atoi(argv[1]);

    //接收請求
    if (recv(socketconn, recvBuff, 1024, 0) == SOCKET_ERROR)
    {
        return;
    }

    //讀取http請求頭
    string recvBuffer = recvBuff;
    int posGet = recvBuffer.find("GET", 0);

    int posHttp = recvBuffer.find("HTTP", 0);

    //截取html檔案路徑
    for (int pos = posGet + 4; pos<posHttp; pos++)
    {
        if (recvBuffer[pos] == '/')
        {
            locDir.push_back('\\');
            continue;
        }
        locDir.push_back(recvBuffer[pos]);
    }
    locDir = dir + locDir;

    //    locDir.insert(0,1,'.');
    //打開http請求檔案進行讀取
    fp.open(locDir.c_str(), std::ios::binary);
    //打開檔案失敗
    if (!fp.is_open())
    {

        cout << "請求檔案" << locDir.c_str() << "不存在" << endl;
    }
    else//打開檔案成功并讀取
    {
        char buffer[1024];

        while (fp.good() && !fp.eof())
        {
            fp.getline(buffer, 1024);
            //将讀取的内容追加入sendBuff中
            sendBuff.append(buffer);
            buffer[0] = '\0';
        }
    }
    fp.close();
    //響應請求,将頁面資訊發送到用戶端

    if (send(socketconn, sendBuff.c_str(), sendBuff.length(), 0) == SOCKET_ERROR)
    {
        return;
    }

    shutdown(socketconn, 1);
    //}
    //關閉連接配接
    closesocket(socketconn);

    return;
}      

三.思考題

3.思考一下,為什麼有時在IE浏覽器中,請求網頁在伺服器中會收到多個請求?我們該怎樣利用這種情況,來優化伺服器?

答:因為一個網頁分為文本和圖檔等,是不同的檔案,是以需要分别請求,如果多線程并行傳輸就會快點。

4.能否解決網頁名稱為中文的問題

答:如果中文不能被識别的話可以通過兩次編譯碼實作,轉成字元串傳輸。

 5.為什麼在IE浏覽器中顯示的内容缺少了一些圖檔等資訊?怎麼來解決

答:個人認為是在客戶發出申請,伺服器傳回socket包時有丢失,或者在阻塞的時候遺漏了資訊包。

繼續閱讀