操作系统实验报告一

亦余心之所善兮,虽九死其犹未悔。这篇文章主要讲述操作系统实验报告一相关的知识,希望能为你提供帮助。












操作系统实验报告一






姓名:许恺
【操作系统实验报告一】学号: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.父进程的目录。


  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_tpCmdLine[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包时有丢失,或者在阻塞的时候遗漏了信息包。











    推荐阅读