作業系統實驗報告一
姓名:許恺
學号: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.父程序的目錄。
-
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包時有丢失,或者在阻塞的時候遺漏了資訊包。