实验 服务于多个请求的WEB服务器
【实验目的】
学习WEB服务器的编程,了解套接字监听、建立连接等机制。
【实验环境】
PC机(带有浏览器),寝室的局域网。
【实验重点及难点】
学习WEB的编程,学习套接字的编程。
【实验内容】
用C++开发出一个能够并行服务于多个请求的多线程Web服务器。为每个请求响应对创建一个独立的TCP连接。每个连接由一个独立的线程来处理。还要有一个主线程,在主线程中,服务器监听要建立连接的客户端。
【实验过程步骤】
1、用C++开发平台,编写服务器程序,通过修改,直到编译无错误,且运行成功为止。
2、用C++开发平台,编写客户端程序,通过修改,直到编译无错误,且可以运行为止。
3、运行客户端程序,出现结果如下图:
Failed connect()。原因是服务器端还没打开(没有运行)。所以连接失败。
3、运行服务器端,出现结果如下图:
如图所示,主线程工作,监听要建立连接的客房端。
此时再运行客户端,则可以建立连接:且接收到从服务器端返回的消息:“这是服务器,这是服务器,收到请求,收到请求!”
建立连接,且收到回复消息!
4、用Web浏览器测试此服务器
在傲游的地址栏里输入:http://192.168.1.6:6789/index.html,出现下图:
此时服务器端接收到请求:
再往傲游的地址栏里输入:http://192.168.1.6:6789,出现下图:
此时服务器端又接收到新的请求:
5、在寝室局域网内测试此服务器
把客户端拿到寝室其他的机子上运行(为了测试,在所有验证的过程中,我把客户端程序中的服务器IP地址由127.0.0.1改为192.168.1.6
服务器端在我机子上运行,客户端分别拿到寝室其他电脑上运行,在客户端都接收到同样的回复
在服务器端,则出现如下图的结果:
前三个都是客户端在我机子上运行的结果,只是把IP改了而已
而后三个则是把客户端在其他的电脑上运行的结果。它们的IP分别是192.168.1.11和192.168.1.7 。
6、在寝室局域网内用不同主机上的浏览器测试主机上的服务器
在192.168.1.11的主机上分别输入http://192.168.1.6:6789,和http://192.168.1.6:6789/index.html,显示如下:
在192.168.1.7的主机上分别输入http://192.168.1.6:6789/index.html,显示如下:
而在我这边的服务器上则收到了三个请求,如下所示:
【客户端程序代码】
#include<winsock2.h>
#pragma comment(lib,"WS2_32")
class CInitSock
{
public:
CInitSock(BYTE minorVer=2,BYTE majorVer=2)
{//初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion=MAKEWORD(minorVer,majorVer);
if(::WSAStartup (sockVersion,&wsaData)!=0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup ();
}
private:
BYTE minorVer;
BYTE majorVer;
};
#include<stdio.h>
CInitSock initSock;//初始化Winsock库
int main()
{
//创建套接字
SOCKET s=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(s==INVALID_SOCKET)
{
printf("Failed socket()/n");
return 0;
}
//填写远程地址信息
sockaddr_in servAddr;
servAddr.sin_family=AF_INET;
servAddr.sin_port=htons(6789);
//下面要填写服务器程序所在机器的IP地址
//防止程序运行在不同的主机上出错,在这用127.0.0.1
servAddr.sin_addr.S_un.S_addr=inet_addr("192.168.1.6");
if(::connect(s,(sockaddr*)&servAddr,sizeof(servAddr))==-1)
{
printf("Failed connect()/n");
return 0;
}
//接收数据
char buff[256];
int nRecv=::recv(s,buff,256,0);
if(nRecv>0)
{
buff[nRecv]='/0';
printf("服务器返回数据:%s",buff);
}
//关闭套接字
::closesocket(s);
return 0;
}
【客户端程序代码】
#include<winsock2.h>
#pragma comment(lib,"WS2_32")
class CInitSock
{
public:
CInitSock(BYTE minorVer=2,BYTE majorVer=2)
{//初始化WS2_32.dll
WSADATA wsaData;
WORD sockVersion=MAKEWORD(minorVer,majorVer);
if(::WSAStartup (sockVersion,&wsaData)!=0)
{
exit(0);
}
}
~CInitSock()
{
::WSACleanup ();
}
private:
BYTE minorVer;
BYTE majorVer;
};
#include<stdio.h>
CInitSock initSock;
int main()
{
SOCKET sListen=::socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen==INVALID_SOCKET)
{
printf("Failed socket()/n");
return 0;
}
//填充sockaddr_in结构
sockaddr_in sinAddr;
sinAddr.sin_family=AF_INET;
sinAddr.sin_port=htons(6789);
sinAddr.sin_addr.S_un.S_addr=INADDR_ANY;
//绑定这个套接字到一个本地地址
if(::bind(sListen,(LPSOCKADDR)&sinAddr,sizeof(sinAddr))==SOCKET_ERROR)
{
printf("Failed bind()/n");
return 0;
}
//进入监听模式
if(::listen(sListen,2)==SOCKET_ERROR)
{
printf("Failed listen()/n");
return 0;
}
else printf("监听当中……/n");
//循环接受客户的连接请求
sockaddr_in remoteAddr;
int nAddrLen=sizeof(remoteAddr);
SOCKET sClient;
char szText[]="这是服务器,这是服务器,收到请求,收到请求!/r/n";
// if(!(::closesocket(sClient)))
// printf("继续监听……/n");
while(TRUE)
{//接受新连接
sClient=::accept(sListen,(SOCKADDR*)&remoteAddr,&nAddrLen);
if(sClient==INVALID_SOCKET)
{
printf("Failed accept()");
continue;
}
printf("接受到一个连接:%s/r/n",inet_ntoa(remoteAddr.sin_addr));
//向客户端发送数据
::send(sClient,szText,strlen(szText),0);
//关闭同客户端的连接
::closesocket(sClient);
}
//关闭监听套接字
// printf("关闭套接字");
::closesocket(sListen);
return 0;
}