作業系統實驗報告一
姓名:許恺
學号: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,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL,
&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#include#include#include#include
#pragma comment(lib, "ws2_32.lib")
using namespacestd;//檔案路徑
static string dir = "D:\\xukai\\學習\\作業系統實驗\\webServer1\\webServer\\Debug";void main(int argc, _TCHAR*argv[])
{//初始化WinSock庫
WORD wVersionRequested;
WSADATA wsaData;
cout<< "初始化庫成功" <
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成功" <
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<< "綁定套接字成功!" <
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<< "連接配接成功" <
{*/
//連接配接成功後與用戶端進行會話
char recvBuff[1024];stringsendBuff;stringlocDir;
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
{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() << "不存在" <
}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#include#include#include#include
#pragma comment(lib, "ws2_32.lib")
using namespacestd;//檔案路徑
static string dir = "D:\\xukai\\學習\\作業系統實驗\\webServer1\\webServer\\Debug";void main(int argc, CHAR*argv[])
{
cout<< argc <
cout<< argv[0] <
cout<< argv[1] <
char recvBuff[1024];stringsendBuff;stringlocDir;
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
{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() << "不存在" <
}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包時有丢失,或者在阻塞的時候遺漏了資訊包。