【网络编程】HTTP网络编程
13.1 HTTP 简介
HTTP(Hyper Text Transfer Protocol,超文本传输协议)是用于从万维网(WWW:World Wide Web) 服务器(简称Web 服务器)传输超文本到本地浏览器的传送协议,基于TCP/IP 通信协 议来传递数据 (HTML 文件、图片文件、查询结果等)。
13.2 HTTP 的工作原理
HTTP协议工作于客户端/服务器端架构上。浏览器作为HTTP 客户端通过URL 向 HTTP 服务器端即Web 服务器发送所有请求。
Web服务器有Apache 服务器、IIS 服务器 (Internet Information Services) 等。 Web服务器根据接收到的请求向客户端发送响应信息。
HTTP 的默认端口号为80,但是你也可以改为8080或者其他端口。 HTTP的注意事项如下3点:
- (1)HTTP 是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客 户的请求,并收到客户的应答后即断开连接。采用这种方式可以节省传输时间。
- (2)HTTP 是媒体独立的:这意味着,只要客户端和服务器知道如何处理数据内容,任 何类型的数据都可以通过HTTP 发送。客户端以及服务器指定使用适合的MIME-type内容类 型。
- (3)HTTP 是无状态的:HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记 忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连 接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答较快。
我们来看一下HTTP 协议通信流程,如图13-1所示。

13.3 HTTP 的特点
HTTP 协议的主要特点可概括如下:
- (1)支持客户/服务器模式。
- (2)简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的 有 GET 、HEAD 、POST 。每种方法规定了客户与服务器联系的类型不同。HTTP 协议简单, 使得HTTP 服务器的程序规模小,因而通信速度很快。
- (3)灵活: HTTP 允许传输任意类型的数据对象。正在传输的类型由Content-Type 加 以 标记 。
- (4)无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求, 并收到客户的应答后即断开连接。采用这种方式可以节省传输时间。
- (5)无状态: HTTP 协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。 缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的 数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。
13.4 HTTP 的消息结构
-
HTTP 是基于客户端/服务器端 (C/S) 的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。
-
一 个HTTP 客户端是一个应用程序(Web 浏览器或其他任何客户端),通过连接到服务 器达到向服务器发送一个或多个HTTP 请求的目的。
-
一 个HTTP 服务器同样也是一个应用程序(通常是一个Web 服务,如Apache Web服务 器或IS 服务器等),接收客户端的请求并向客户端发送HTTP 响应数据。
-
HTTP使用统一资源标识符(Uniform Resource Identifiers,URI)来传输数据和建立连接。
-
一旦建立连接后,数据消息就通过类似 Internet 邮件所使用的格式[RFC5322]和多用途 Internet邮件扩展 (MIME)[RFC2045] 来传送。
13.5 客户端请求消息
客户端发送一个HTTP 请求到服务器的请求消息由请求行(request line)、请求头部(也 称请求头)、空行和请求数据4部分组成。图13-2给出了请求报文的一般格式。

HTTP 协议定义了8种请求方法(或者叫“动作”),表明对 Request-URI 指定的资源 的不同操作方式,具体如下:
- (1)OPTIONS: 返回服务器针对特定资源所支持的HTTP 请求方法。也可以利用向Web 服务器发送’*'的请求来测试服务器的功能性。
- (2)HEAD: 向服务器索要与GET 请求相一致的响应,只不过响应体将不会被返回。这 一方法可以在不必传输整个响应内容的情况下就获取包含在响应消息头中的元信息。
- (3)GET: 向特定的资源发出请求。
- (4)POST: 向指定资源提交数据进行处理请求(例如,提交表单或者上传文件)。数据 被包含在请求体中。POST 请求可能会导致新资源的创建和/或已有资源的修改。
- (5)PUT: 向指定资源位置上传其最新内容。
- (6)DELETE: 请求服务器删除 Request-URI 所标识的资源。
- (7)TRACE: 回显服务器收到的请求,主要用于测试或诊断。
- (8)CONNECT:HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。
虽然HTTP 的请求方式有8种,但是我们在实际应用中常用的也就是get 和post, 其他请 求方式也都可以通过这两种方式间接地实现。
13.6 服务器响应消息
HTTP 响应也由4个部分组成,分别是状态行、消息报头(也称响应头)、空行和响应正文,如图13-3所示。

下面给出一个典型的使用GET来传递数据的实例。
客户端请求:

服务器端响应:

输出结果

图13-4演示请求和响应HTTP报文的操作。

13.7 HTTP 状态码
当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收 并显示网页前,此网页所在的服务器会返回一个包含HTTP 状态码的信息头(server header), 用以响应浏览器的请求。
HTTP状态码的英文为HTTP Status Code。下面是常见的HTTP 状态码:
- 200:请求成功。
- 301:资源(网页等)被永久转移到其他URL。
- 404:请求的资源(网页等)不存在。
- 500:内部服务器错误。
13.8 HTTP 状态码分类
HTTP状态码由3个十进制数字组成,第一个十进制数字定义状态码的类型,后两个数字没有分类的作用。HTTP 状态码共分为5种类型,如表13-1所示。
| 分类 | 分类描述 |
| 1** | 信息,服务器收到请求,需要请求者继续执行操作 |
| 2** | 成功,操作被成功接收并处理 |
| 3** | 重定向,需要进一步的操作以完成请求 |
| 4** | 客户端错误,请求包含语法错误或无法完成请求 |
| 5** | 服务器错误,服务器在处理请求的过程中发生了错误 |
13.9 实现HTTP 服务器
前面对 HTTP 协议进行了简单的介绍。下面我们利用前面的网络技术来实现一个 HTTP 服务器。我们学了好多服务器技术,比如CAsynSocket 、CSocket 、WSAAsyncSelect等。这里 我们选择WSAAsyncSelect 技 术 。
WSAAsyncSelect 模型是Windows socket的一个异步I/O 模 型,利用这个模型,应用程序可在一个套接字上接收以Windows 消息为基础的网络事件通知。 Windows sockets应用程序在创建套接字后,调用WSAAsyncSelect 函数注册感兴趣的网络事 件,当该事件发生时Windows 窗口收到消息,应用程序就可以对接收到的网络事件进行处理。 利用 WSAAsyncSelect 函数,将socket 消息发送到 hWnd 窗口上,然后在那里处理相应的 FD_READ 、FD_WRITE等消息。更多关于WSAAsyncSelect 的知识,我们前面章节已经介绍 过 了 。
为了便于学习,我们的HTTP 实现的功能并不多,主要实现了HTTP 最基本的一些功能。 大家可以把这个例子作为原型,完善其功能。
我们的HTTP 服务器基于异步选择模型WSAAsyncSelect。通过前面章节的学习应该知道, 这个模型需要一个Windows 窗口,因此我们的程序是一个基于对话框的程序。
13.9.2 界面设计
- (1)新建一个对话框工程,工程名是WebServer。
- (2)切换到资源视图,打开对话框编辑器,放置按钮,如图13-5所示。

从控件标题我们大致能知道其含义了。其中,服务器的根目录主要是放置网页文件的,比 如 index.htm。
log.h
//zww
/****************************************************************************************
* ///
* Original Filename: Log.h
*
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#if !defined(AFX_LOG_H__82043CA7_5940_4F7E_A316_7F91096B2008__INCLUDED_)
#define AFX_LOG_H__82043CA7_5940_4F7E_A316_7F91096B2008__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#include <string>using namespace std;#define MAX_MSG_SIZE 1024class CLog
{
public:BOOL ClearLog(const char*);BOOL LogMessage(const char*, const char*, const char* = NULL, long = NULL);CLog();virtual ~CLog();
private:FILE *m_f;char szLogFilePath[MAX_PATH];char szMessage[MAX_MSG_SIZE];char szDT[128];struct tm *newtime;time_t ltime;CRITICAL_SECTION cs;
};#endif // !defined(AFX_LOG_H__82043CA7_5940_4F7E_A316_7F91096B2008__INCLUDED_)
log.cpp
//zww
/****************************************************************************************
* ///
* Original Filename: Log.cpp
*
* History:
* Created/Modified by Date Main Purpose/Changes
* Souren M. Abeghyan 2001/05/25 Implementation of the CLog class.
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#include "stdafx.h"
#include "Log.h"#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//
// Construction/Destruction
//CLog::CLog()
{InitializeCriticalSection(&cs);
}CLog::~CLog()
{DeleteCriticalSection(&cs);
}BOOL CLog::LogMessage(const char *szFolder, const char *szMsg, const char *szMsg1, long nNumber)
{EnterCriticalSection(&cs);time(<ime);//获得计算机系统当前的日历时间if((!strlen(szFolder)) || (!strlen(szMsg)))return FALSE;if(!GetWindowsDirectory(szLogFilePath, MAX_PATH)){LeaveCriticalSection(&cs);return FALSE;}if(szLogFilePath[0] != '\\')strcat(szLogFilePath, "\\");strcat(szLogFilePath, szFolder);m_f = fopen(szLogFilePath, "a");if(m_f != NULL) {newtime = localtime(<ime);//将时间数值变换成本地时间,考虑到本地时区和夏令时标志;strftime(szDT, 128, // 格式化显示日期时间"%a, %d %b %Y %H:%M:%S", newtime);if(szMsg1 != NULL)sprintf(szMessage, "%s - %s.\t[%s]\t[%d]\n", szDT, szMsg, szMsg1, nNumber);elsesprintf(szMessage, "%s - %s.\t[%d]\n", szDT, szMsg, nNumber);int n = fwrite(szMessage, sizeof(char), strlen(szMessage), m_f);if(n != strlen(szMessage)){LeaveCriticalSection(&cs);fclose(m_f);return FALSE;}fclose(m_f);LeaveCriticalSection(&cs);return TRUE;}LeaveCriticalSection(&cs);return FALSE;
}BOOL CLog::ClearLog(const char *szFolder)
{if(!strlen(szFolder))return FALSE;if(!GetWindowsDirectory(szLogFilePath, MAX_PATH))return FALSE;if(szLogFilePath[0] != '\\')strcat(szLogFilePath, "\\");strcat(szLogFilePath, szFolder);return DeleteFile(szLogFilePath);
}
WebServer.h
//zww
// WebServer.h : main header file for the WEBSERVER application
//#if !defined(AFX_WEBSERVER_H__3F794A8E_734C_4694_8F25_F7CC03C513E5__INCLUDED_)
#define AFX_WEBSERVER_H__3F794A8E_734C_4694_8F25_F7CC03C513E5__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#ifndef __AFXWIN_H__#error include 'stdafx.h' before including this file for PCH
#endif#include "resource.h" // main symbols/
// CWebServerApp:
// See WebServer.cpp for the implementation of this class
//class CWebServerApp : public CWinApp
{
public:CWebServerApp();// Overrides// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CWebServerApp)public:virtual BOOL InitInstance();//}}AFX_VIRTUAL// Implementation//{{AFX_MSG(CWebServerApp)// NOTE - the ClassWizard will add and remove member functions here.// DO NOT EDIT what you see in these blocks of generated code !//}}AFX_MSGDECLARE_MESSAGE_MAP()
};///{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_WEBSERVER_H__3F794A8E_734C_4694_8F25_F7CC03C513E5__INCLUDED_)
WebServer.cpp
// WebServer.cpp : Defines the class behaviors for the application.
// zww#include "stdafx.h"
#include "WebServer.h"
#include "WebServerDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/
// CWebServerAppBEGIN_MESSAGE_MAP(CWebServerApp, CWinApp)//{{AFX_MSG_MAP(CWebServerApp)// NOTE - the ClassWizard will add and remove mapping macros here.// DO NOT EDIT what you see in these blocks of generated code!//}}AFX_MSGON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()/
// CWebServerApp constructionCWebServerApp::CWebServerApp()
{// TODO: add construction code here,// Place all significant initialization in InitInstance
}/
// The one and only CWebServerApp objectCWebServerApp theApp;/
// CWebServerApp initializationBOOL CWebServerApp::InitInstance()
{AfxEnableControlContainer();// Standard initialization// If you are not using these features and wish to reduce the size// of your final executable, you should remove from the following// the specific initialization routines you do not need.#ifdef _AFXDLLEnable3dControls(); // Call this when using MFC in a shared DLL
#elseEnable3dControlsStatic(); // Call this when linking to MFC statically
#endifSetRegistryKey("AcID_Soft");//弹出对话框CWebServerDlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){// TODO: Place code here to handle when the dialog is// dismissed with OK}else if (nResponse == IDCANCEL){// TODO: Place code here to handle when the dialog is// dismissed with Cancel}// Since the dialog has been closed, return FALSE so that we exit the// application, rather than start the application's message pump.return FALSE;
}
WebServerDlg.h
//zww
// WebServerDlg.h : header file
//#if !defined(AFX_WEBSERVERDLG_H__8F4AD293_7AED_4ED5_A263_0ED2ED7032C8__INCLUDED_)
#define AFX_WEBSERVERDLG_H__8F4AD293_7AED_4ED5_A263_0ED2ED7032C8__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000/
// CWebServerDlg dialog#include "HTTPServer.h"#define TIMER_ID_1 1
#define TIMER_TO_1 500class CWebServerDlg : public CDialog
{
public:UINT nTimerID;BOOL m_bRun;
// Construction
public:void RestoreSettings();void SaveSettings();CWebServerDlg(CWnd* pParent = NULL); // standard constructor// Dialog Data//{{AFX_DATA(CWebServerDlg)enum { IDD = IDD_WEBSERVER_DIALOG };CStatic m_nVisitors;CStatic m_nBytesRecv;CStatic m_nBytesSent;CStatic m_nRequests;CStatic m_nActiveConn;CString m_szHomeDir;CString m_szDefIndex;int m_Port;int m_PTO;CString m_szStatus;//}}AFX_DATA// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CWebServerDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support//}}AFX_VIRTUAL// Implementation
protected:HICON m_hIcon;CHTTPServer WebServer;// Generated message map functions//{{AFX_MSG(CWebServerDlg)virtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();afx_msg void OnStart();afx_msg void OnStop();afx_msg void OnClose();virtual void OnOK();afx_msg void OnTimer(UINT nIDEvent);afx_msg void OnReset();afx_msg void OnHomedirbrowse();//}}AFX_MSGDECLARE_MESSAGE_MAP()
};//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.#endif // !defined(AFX_WEBSERVERDLG_H__8F4AD293_7AED_4ED5_A263_0ED2ED7032C8__INCLUDED_)
WebServerDlg.cpp
// WebServerDlg.cpp : implementation file
// zww#include "stdafx.h"
#include "WebServer.h"
#include "WebServerDlg.h"#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif/
// CAboutDlg dialog used for App Aboutclass CAboutDlg : public CDialog
{
public:CAboutDlg();// Dialog Data//{{AFX_DATA(CAboutDlg)enum { IDD = IDD_ABOUTBOX };//}}AFX_DATA// ClassWizard generated virtual function overrides//{{AFX_VIRTUAL(CAboutDlg)protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support//}}AFX_VIRTUAL// Implementation
protected://{{AFX_MSG(CAboutDlg)//}}AFX_MSGDECLARE_MESSAGE_MAP()
};CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{//{{AFX_DATA_INIT(CAboutDlg)//}}AFX_DATA_INIT
}void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CAboutDlg)//}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)//{{AFX_MSG_MAP(CAboutDlg)// No message handlers//}}AFX_MSG_MAP
END_MESSAGE_MAP()/
// CWebServerDlg dialogCWebServerDlg::CWebServerDlg(CWnd* pParent /*=NULL*/): CDialog(CWebServerDlg::IDD, pParent)
{//{{AFX_DATA_INIT(CWebServerDlg)//初始化参数m_szHomeDir = _T("");m_szDefIndex = _T("");m_Port = 81;m_PTO = 10;m_szStatus = _T("");//}}AFX_DATA_INIT// Note that LoadIcon does not require a subsequent DestroyIcon in Win32m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}void CWebServerDlg::DoDataExchange(CDataExchange* pDX)
{CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CWebServerDlg)DDX_Control(pDX, IDC_VISITORS, m_nVisitors);DDX_Control(pDX, IDC_BYTESRECV, m_nBytesRecv);DDX_Control(pDX, IDC_BYTESENT, m_nBytesSent);DDX_Control(pDX, IDC_REQUESTS, m_nRequests);DDX_Control(pDX, IDC_ACTIVECONN, m_nActiveConn);DDX_Text(pDX, IDC_HOMEDIR, m_szHomeDir);DDX_Text(pDX, IDC_DEFINDEXFILE, m_szDefIndex);DDX_Text(pDX, IDC_PORT, m_Port);DDV_MinMaxInt(pDX, m_Port, 1, 65535);DDX_Text(pDX, IDC_PTO, m_PTO);DDV_MinMaxInt(pDX, m_PTO, 0, 10000);DDX_Text(pDX, IDC_STATUS, m_szStatus);//}}AFX_DATA_MAP
}BEGIN_MESSAGE_MAP(CWebServerDlg, CDialog)//{{AFX_MSG_MAP(CWebServerDlg)ON_WM_SYSCOMMAND()ON_WM_PAINT()ON_WM_QUERYDRAGICON()ON_BN_CLICKED(IDC_START, OnStart)ON_BN_CLICKED(IDC_STOP, OnStop)ON_WM_CLOSE()ON_WM_TIMER()ON_BN_CLICKED(IDC_RESET, OnReset)ON_BN_CLICKED(IDC_HOMEDIRBROWSE, OnHomedirbrowse)//}}AFX_MSG_MAP
END_MESSAGE_MAP()/
// CWebServerDlg message handlersBOOL CWebServerDlg::OnInitDialog()
{CDialog::OnInitDialog();// Add "About..." menu item to system menu.// IDM_ABOUTBOX must be in the system command range.ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);ASSERT(IDM_ABOUTBOX < 0xF000);CMenu* pSysMenu = GetSystemMenu(FALSE);if (pSysMenu != NULL){CString strAboutMenu;strAboutMenu.LoadString(IDS_ABOUTBOX);if (!strAboutMenu.IsEmpty()){pSysMenu->AppendMenu(MF_SEPARATOR);pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);}}// Set the icon for this dialog. The framework does this automatically// when the application's main window is not a dialogSetIcon(m_hIcon, TRUE); // Set big iconSetIcon(m_hIcon, FALSE); // Set small icon// TODO: Add extra initialization herem_bRun = FALSE;//保存设置RestoreSettings();//启动服务OnStart();return TRUE; // return TRUE unless you set the focus to a control
}void CWebServerDlg::OnSysCommand(UINT nID, LPARAM lParam)
{if ((nID & 0xFFF0) == IDM_ABOUTBOX){CAboutDlg dlgAbout;dlgAbout.DoModal();}else{CDialog::OnSysCommand(nID, lParam);}
}// If you add a minimize button to your dialog, you will need the code below
// to draw the icon. For MFC applications using the document/view model,
// this is automatically done for you by the framework.void CWebServerDlg::OnPaint()
{if (IsIconic()){CPaintDC dc(this); // device context for paintingSendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);// Center icon in client rectangleint cxIcon = GetSystemMetrics(SM_CXICON);int cyIcon = GetSystemMetrics(SM_CYICON);CRect rect;GetClientRect(&rect);int x = (rect.Width() - cxIcon + 1) / 2;int y = (rect.Height() - cyIcon + 1) / 2;// Draw the icondc.DrawIcon(x, y, m_hIcon);}else{CDialog::OnPaint();}
}// The system calls this to obtain the cursor to display while the user drags
// the minimized window.
HCURSOR CWebServerDlg::OnQueryDragIcon()
{return (HCURSOR) m_hIcon;
}//启动服务子程序
void CWebServerDlg::OnStart()
{//如果已经在运行if(m_bRun){AfxMessageBox("服务器已经在运行");return;}UpdateData();//否则启动服务m_bRun = WebServer.Start(LPCTSTR(m_szHomeDir), LPCTSTR(m_szDefIndex), m_Port, m_PTO * 1000);if(m_bRun)nTimerID = SetTimer(TIMER_ID_1, TIMER_TO_1, NULL);m_szStatus = "程序服务中...";UpdateData(FALSE);
}//停止服务
void CWebServerDlg::OnStop()
{if(m_bRun){//调用Shutdown来关闭服务BOOL bResult = WebServer.Shutdown();KillTimer(nTimerID);m_bRun = FALSE;m_szStatus = "目前没有提供服务.";UpdateData(FALSE);return;}AfxMessageBox("服务没有启动");
}//退出程序
void CWebServerDlg::OnClose()
{//保存设置SaveSettings();OnStop();CDialog::OnClose();
}void CWebServerDlg::OnOK()
{SaveSettings();OnStop();CDialog::OnOK();
}//定时器处理程序,主要是显示服务器数据
void CWebServerDlg::OnTimer(UINT nIDEvent)
{StatisticsTag st;CString szTemp;WebServer.GetStats(st);szTemp.Format("%d", st.nClientsConnected);m_nActiveConn.SetWindowText(szTemp);szTemp.Format("%.1f", st.nTotalRecv);m_nBytesRecv.SetWindowText(szTemp);szTemp.Format("%.1f", st.nTotalSent);m_nBytesSent.SetWindowText(szTemp);szTemp.Format("%d", st.nTotalHits);m_nRequests.SetWindowText(szTemp);szTemp.Format("%d", st.nVisitors);m_nVisitors.SetWindowText(szTemp);CDialog::OnTimer(nIDEvent);
}//重新启动服务
void CWebServerDlg::OnReset()
{OnStop();WebServer.Reset();UpdateData();OnStart();
}//设置服务根目录
void CWebServerDlg::OnHomedirbrowse()
{BROWSEINFO bi;char folder_name[MAX_PATH];char dir_name[MAX_PATH];LPMALLOC lpMalloc; bi.hwndOwner = GetSafeHwnd();bi.pidlRoot = NULL;bi.pszDisplayName = folder_name;bi.lpszTitle = "请选择目录";bi.ulFlags = BIF_EDITBOX | BIF_STATUSTEXT;bi.lpfn = NULL;bi.lParam = NULL;bi.iImage = NULL;LPITEMIDLIST pidl = SHBrowseForFolder(&bi);if(pidl){SHGetPathFromIDList(pidl, dir_name);m_szHomeDir = dir_name;UpdateData(FALSE);}if(!SHGetMalloc(&lpMalloc) && (lpMalloc != NULL)) { if(pidl != NULL) { lpMalloc->Free(pidl); } lpMalloc->Release(); }
}//保存设置
void CWebServerDlg::SaveSettings()
{CWinApp* pApp = AfxGetApp();UpdateData();pApp->WriteProfileString("Settings", "Server Root", m_szHomeDir);pApp->WriteProfileString("Settings", "Defindex", m_szDefIndex);pApp->WriteProfileInt("Settings", "Port", m_Port);pApp->WriteProfileInt("Settings", "PTO", m_PTO);
}//获取设置
void CWebServerDlg::RestoreSettings()
{CWinApp* pApp = AfxGetApp();//默认服务器根目录是"C:\\ServerRoot"m_szHomeDir = pApp->GetProfileString("Settings", "Server Root", "C:\\ServerRoot");m_szDefIndex = pApp->GetProfileString("Settings", "Defindex", "index.htm");m_Port = pApp->GetProfileInt("Settings", "Port", 80);m_PTO = pApp->GetProfileInt("Settings", "PTO", 10);UpdateData(FALSE);
}
HTTPServer.h
//zww
/****************************************************************************************
* ///
* Original Filename: HTTPServer.h* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#if !defined(AFX_HTTPSERVER_H__177A41DC_432E_4819_A614_8A0D66ABF434__INCLUDED_)
#define AFX_HTTPSERVER_H__177A41DC_432E_4819_A614_8A0D66ABF434__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#include "GenericServer.h"#define ERROR404 "/fnf.html"
#define ERROR501 "/mna.html"#define SERVERNAME "AcIDSoftWebServer/0.1b"typedef map<string, string> MIMETYPES;class CHTTPServer : public CGenericServer
{
public:CHTTPServer();virtual ~CHTTPServer();BOOL Start(string, string, int, int);BOOL IsComplete(string);BOOL ParseRequest(string, string&, BOOL&);int GotConnection(char*, int);int DataSent(DWORD);
private:string m_HomeDir;string m_DefIndex;MIMETYPES MimeTypes;};#endif // !defined(AFX_HTTPSERVER_H__177A41DC_432E_4819_A614_8A0D66ABF434__INCLUDED_)
HTTPServer.cpp
/****************************************************************************************
* ///
* Original Filename: HTTPServer.cpp
*
* History:
* Created/Modified by Date Main Purpose/Changes
* Souren M. Abeghyan 2001/05/25 Implementation of the CHTTPServer class
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#include "stdafx.h"
#include "WebServer.h"
#include "HTTPServer.h"
// zww
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif//
// Construction/Destruction
//CHTTPServer::CHTTPServer()
{//// 初始化 MIME 类型//MimeTypes["doc"] = "application/msword";MimeTypes["bin"] = "application/octet-stream";MimeTypes["dll"] = "application/octet-stream";MimeTypes["exe"] = "application/octet-stream";MimeTypes["pdf"] = "application/pdf";MimeTypes["p7c"] = "application/pkcs7-mime";MimeTypes["ai"] = "application/postscript";MimeTypes["eps"] = "application/postscript";MimeTypes["ps"] = "application/postscript";MimeTypes["rtf"] = "application/rtf";MimeTypes["fdf"] = "application/vnd.fdf";MimeTypes["arj"] = "application/x-arj";MimeTypes["gz"] = "application/x-gzip";MimeTypes["class"] = "application/x-java-class";MimeTypes["js"] = "application/x-javascript";MimeTypes["lzh"] = "application/x-lzh";MimeTypes["lnk"] = "application/x-ms-shortcut";MimeTypes["tar"] = "application/x-tar";MimeTypes["hlp"] = "application/x-winhelp";MimeTypes["cert"] = "application/x-x509-ca-cert";MimeTypes["zip"] = "application/zip";MimeTypes["cab"] = "application/x-compressed";MimeTypes["arj"] = "application/x-compressed";MimeTypes["aif"] = "audio/aiff";MimeTypes["aifc"] = "audio/aiff";MimeTypes["aiff"] = "audio/aiff";MimeTypes["au"] = "audio/basic";MimeTypes["snd"] = "audio/basic";MimeTypes["mid"] = "audio/midi";MimeTypes["rmi"] = "audio/midi";MimeTypes["mp3"] = "audio/mpeg";MimeTypes["vox"] = "audio/voxware";MimeTypes["wav"] = "audio/wav";MimeTypes["ra"] = "audio/x-pn-realaudio";MimeTypes["ram"] = "audio/x-pn-realaudio";MimeTypes["bmp"] = "image/bmp";MimeTypes["gif"] = "image/gif";MimeTypes["jpeg"] = "image/jpeg";MimeTypes["jpg"] = "image/jpeg";MimeTypes["tif"] = "image/tiff";MimeTypes["tiff"] = "image/tiff";MimeTypes["xbm"] = "image/xbm";MimeTypes["wrl"] = "model/vrml";MimeTypes["htm"] = "text/html";MimeTypes["html"] = "text/html";MimeTypes["c"] = "text/plain";MimeTypes["cpp"] = "text/plain";MimeTypes["def"] = "text/plain";MimeTypes["h"] = "text/plain";MimeTypes["txt"] = "text/plain";MimeTypes["rtx"] = "text/richtext";MimeTypes["rtf"] = "text/richtext";MimeTypes["java"] = "text/x-java-source";MimeTypes["css"] = "text/css";MimeTypes["mpeg"] = "video/mpeg";MimeTypes["mpg"] = "video/mpeg";MimeTypes["mpe"] = "video/mpeg";MimeTypes["avi"] = "video/msvideo";MimeTypes["mov"] = "video/quicktime";MimeTypes["qt"] = "video/quicktime";MimeTypes["shtml"] = "wwwserver/html-ssi";MimeTypes["asa"] = "wwwserver/isapi";MimeTypes["asp"] = "wwwserver/isapi";MimeTypes["cfm"] = "wwwserver/isapi";MimeTypes["dbm"] = "wwwserver/isapi";MimeTypes["isa"] = "wwwserver/isapi";MimeTypes["plx"] = "wwwserver/isapi";MimeTypes["url"] = "wwwserver/isapi";MimeTypes["cgi"] = "wwwserver/isapi";MimeTypes["php"] = "wwwserver/isapi";MimeTypes["wcgi"] = "wwwserver/isapi";
}CHTTPServer::~CHTTPServer()
{}int CHTTPServer::GotConnection(char*, int)
{return 0;
}int CHTTPServer::DataSent(DWORD)
{return 0;
}//启动服务,重要函数,分析根目录
BOOL CHTTPServer::Start(string HomeDir, string DefIndex, int Port, int PersTO)
{m_HomeDir = HomeDir;m_DefIndex = DefIndex;if(m_HomeDir.substr(m_HomeDir.size() - 1, 1) != "\\")m_HomeDir += "\\";return Run(Port, PersTO);
}//是否完成
BOOL CHTTPServer::IsComplete(string szRequest)
{if(szRequest.substr(szRequest.size() - 4, 4) == "\r\n\r\n")return TRUE;elsereturn FALSE;
}//分析请求数据
BOOL CHTTPServer::ParseRequest(string szRequest, string &szResponse, BOOL &bKeepAlive)
{////string szMethod;string szFileName;string szFileExt;string szStatusCode("200 OK");string szContentType("text/html");string szConnectionType("close");string szNotFoundMessage;string szDateTime;char pResponseHeader[2048];fpos_t lengthActual = 0, length = 0;char *pBuf = NULL;int n;//// 检查提交方法//n = szRequest.find(" ", 0);if(n != string::npos){szMethod = szRequest.substr(0, n);if(szMethod == "GET"){//// 获取文件名// int n1 = szRequest.find(" ", n + 1);if(n != string::npos){szFileName = szRequest.substr(n + 1, n1 - n - 1);if(szFileName == "/"){szFileName = m_DefIndex;}}else{LogMessage(LOGFILENAME, "No 'space' found in Request String #1", "ParseRequest");return FALSE;}}else{szStatusCode = "501 Not Implemented";szFileName = ERROR501;}}else{LogMessage(LOGFILENAME, "No 'space' found in Request String #2", "ParseRequest");return FALSE;}//// 分析链接类型//n = szRequest.find("\nConnection: Keep-Alive", 0);if(n != string::npos)bKeepAlive = TRUE;//// 分析内容类型//int nPointPos = szFileName.rfind(".");if(nPointPos != string::npos){szFileExt = szFileName.substr(nPointPos + 1, szFileName.size());strlwr((char*)szFileExt.c_str());MIMETYPES::iterator it;it = MimeTypes.find(szFileExt);if(it != MimeTypes.end())szContentType = (*it).second;}//得到目前的时间//char szDT[128];struct tm *newtime;time_t ltime;time((time_t*)<ime);newtime = gmtime(<ime);strftime(szDT, 128,"%a, %d %b %Y %H:%M:%S GMT", newtime);//// 读取文件//FILE *f;f = fopen((m_HomeDir + szFileName).c_str(), "r+b");if(f != NULL) {// 获得文件大小fseek(f, 0, SEEK_END);fgetpos(f, &lengthActual);fseek(f, 0, SEEK_SET);pBuf = new char[lengthActual + 1];length = fread(pBuf, 1, lengthActual, f);fclose(f);//// 返回响应//sprintf(pResponseHeader, "HTTP/1.0 %s\r\nDate: %s\r\nServer: %s\r\nAccept-Ranges: bytes\r\nContent-Length: %d\r\nConnection: %s\r\nContent-Type: %s\r\n\r\n",szStatusCode.c_str(), szDT, SERVERNAME, (int)length, bKeepAlive ? "Keep-Alive" : "close", szContentType.c_str());}else{//// 如果文件没有找到 //f = fopen((m_HomeDir + ERROR404).c_str(), "r+b");if(f != NULL) {// 获取文件大小fseek(f, 0, SEEK_END);fgetpos(f, &lengthActual);fseek(f, 0, SEEK_SET);pBuf = new char[lengthActual + 1];length = fread(pBuf, 1, lengthActual, f);fclose(f);szNotFoundMessage = string(pBuf, length);delete pBuf;pBuf = NULL;}szStatusCode = "404 Resource not found";sprintf(pResponseHeader, "HTTP/1.0 %s\r\nContent-Length: %d\r\nContent-Type: text/html\r\nDate: %s\r\nServer: %s\r\n\r\n%s",szStatusCode.c_str(), szNotFoundMessage.size(), szDT, SERVERNAME, szNotFoundMessage.c_str());bKeepAlive = FALSE; }szResponse = string(pResponseHeader);if(pBuf)szResponse += string(pBuf, length);delete pBuf;pBuf = NULL;return TRUE;
}
GernericServer.h
//zww
/****************************************************************************************
* ///
* Original Filename: genericserver.h
*
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/
#if !defined(AFX_GENERICSERVER_H__6C21FFA2_485A_11D5_9692_0050BA8CD8A0__INCLUDED_)
#define AFX_GENERICSERVER_H__6C21FFA2_485A_11D5_9692_0050BA8CD8A0__INCLUDED_#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000#define THREADKILL_TO 1000
#define THREADWAIT_TO 30000
#define TICK 100
#define THREADEXIT_SUCCESS 0x1234
#define SERVERPORT 80
#define MAX_BUFFER 100000
#define SENDBLOCK 200000
#define LOGFILENAME "UMServer.log"#pragma warning(disable:4786)#include <winsock2.h>
#include <string>
#include <vector>
#include <deque>
#include <map>
#include <algorithm>
#include <list>
#include <functional>
#include <process.h>#include "Log.h"using namespace std;struct NewConnectionTag;struct ThreadTag
{HANDLE hThread;unsigned int threadID;
};struct IDCompare : binary_function<ThreadTag, unsigned int, bool>
{bool operator()(const ThreadTag& _X, const unsigned int& _Y) const{return (_X.threadID == _Y); }
};typedef list<ThreadTag> THREADLIST;
typedef list<HANDLE> HANDLELIST;
typedef vector<string> STRVECT;struct StatisticsTag
{long nClientsConnected;long nTotalHits;double nTotalSent;double nTotalRecv;double nErrosCount;long nVisitors;
};class CGenericServer : public CLog
{
public:CGenericServer();virtual ~CGenericServer();
public: void GetStats(StatisticsTag&);void Reset();BOOL Run(int, int);BOOL Shutdown();
protected: virtual int GotConnection(char*, int) = 0;virtual int DataSent(DWORD) = 0;virtual BOOL IsComplete(string) = 0;virtual BOOL ParseRequest(string, string&, BOOL&) = 0;
private: static UINT __stdcall AcceptThread(LPVOID);static UINT __stdcall ClientThread(LPVOID);static UINT __stdcall HelperThread(LPVOID);BOOL AddClient(SOCKET, char*, int);void CleanupThread(WSAEVENT, SOCKET, NewConnectionTag*, DWORD);void CleanupThread(WSAEVENT, WSAEVENT, SOCKET);
private: HANDLE ThreadA; // Accept Threadunsigned int ThreadA_ID;HANDLE ThreadC; // Cleanup Threadunsigned int ThreadC_ID;WSAEVENT ShutdownEvent;HANDLE ThreadLaunchedEvent;THREADLIST ThreadList;HANDLELIST HandleList;StatisticsTag Stats;CRITICAL_SECTION cs;CRITICAL_SECTION _cs;int ServerPort;int PersistenceTO;BOOL bRun;
protected:STRVECT Visitors;
};struct NewConnectionTag
{CGenericServer *pGenericServer;SOCKET s;
};#endif // !defined(AFX_GENERICSERVER_H__6C21FFA2_485A_11D5_9692_0050BA8CD8A0__INCLUDED_)
GernericServer.cpp
/****************************************************************************************
* ///
* Original Filename: genericserver.cpp
*
* History:
* Created/Modified by Date Main Purpose/Changes
* Souren M. Abeghyan 2001/05/25 Implementation of the CGenericServer class.
*
* Comments:
* \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
****************************************************************************************/#include "stdafx.h"
#include "GenericServer.h"#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
// zww
//
// Construction/Destruction
//CGenericServer::CGenericServer()
{bRun = FALSE;
}CGenericServer::~CGenericServer()
{}//获取状态
void CGenericServer::GetStats(StatisticsTag &st)
{st.nTotalRecv = Stats.nTotalRecv;st.nTotalSent = Stats.nTotalSent;st.nTotalHits = Stats.nTotalHits;st.nVisitors = Visitors.size();EnterCriticalSection(&_cs);st.nClientsConnected = ThreadList.size();LeaveCriticalSection(&_cs);
}//增加新的连接
BOOL CGenericServer::AddClient(SOCKET s, char* ClientAddress, int port)
{GotConnection(ClientAddress, port);STRVECT::iterator it;it = find(Visitors.begin(), Visitors.end(), ClientAddress);if(it == Visitors.end())Visitors.push_back(ClientAddress);InterlockedIncrement(&Stats.nTotalHits);ThreadTag Thread;HANDLE hThread;unsigned int threadID;EnterCriticalSection(&cs);NewConnectionTag *NewConn = new NewConnectionTag;NewConn->pGenericServer = this;NewConn->s = s;hThread = (HANDLE)_beginthreadex(NULL, 0, ClientThread, NewConn, 0, &threadID);if(hThread){Thread.threadID = threadID;Thread.hThread = hThread;ThreadList.push_back(Thread);}elseLogMessage(LOGFILENAME, "_beginthreadex(...) failure", "AddClient", errno);LeaveCriticalSection(&cs);return TRUE;
}//启动服务器
BOOL CGenericServer::Run(int Port, int PersTO)
{if(bRun){LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Launch Thread", "Run", errno);return FALSE;}ServerPort = Port;PersistenceTO = PersTO;InitializeCriticalSection(&cs);InitializeCriticalSection(&_cs);Reset();ThreadLaunchedEvent = CreateEvent(NULL, FALSE, TRUE, NULL);// 启动接收线程ResetEvent(ThreadLaunchedEvent);ThreadA = (HANDLE)_beginthreadex(NULL, 0, AcceptThread, this, 0, &ThreadA_ID);if(!ThreadA){LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Launch Thread", "Run", errno);return FALSE;}if(WaitForSingleObject(ThreadLaunchedEvent, THREADWAIT_TO) != WAIT_OBJECT_0){LogMessage(LOGFILENAME, "Unable to get response from Accept Thread withing specified Timeout ->", "Run", THREADWAIT_TO);CloseHandle(ThreadLaunchedEvent);return FALSE;}// 启动帮助线程ResetEvent(ThreadLaunchedEvent);ThreadC = (HANDLE)_beginthreadex(NULL, 0, HelperThread, this, 0, &ThreadC_ID);if(!ThreadC){LogMessage(LOGFILENAME, "_beginthreadex(...) failure, for Helper Thread", "Run", errno);return FALSE;}if(WaitForSingleObject(ThreadLaunchedEvent, THREADWAIT_TO) != WAIT_OBJECT_0){LogMessage(LOGFILENAME, "Unable to get response from Helper Thread within specified Timeout ->", "Run", THREADWAIT_TO);CloseHandle(ThreadLaunchedEvent);return FALSE;}CloseHandle(ThreadLaunchedEvent);bRun = TRUE;return TRUE;
}//关闭服务
BOOL CGenericServer::Shutdown()
{if(!bRun)return FALSE;BOOL bResult = TRUE;HANDLE hArray[2];hArray[0] = ThreadA;hArray[1] = ThreadC;//// 关闭接收和helper线程//SetEvent(ShutdownEvent);DWORD n = WaitForMultipleObjects(2, hArray, TRUE, THREADKILL_TO);if(n == WAIT_TIMEOUT || n == WAIT_FAILED){LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) timed out", "Shutdown");if(!TerminateThread(ThreadA, THREADEXIT_SUCCESS))LogMessage(LOGFILENAME, "TerminateThread(.ThreadA.) failure, probably it is already terminated", "Shutdown", GetLastError());if(!TerminateThread(ThreadC, THREADEXIT_SUCCESS))LogMessage(LOGFILENAME, "TerminateThread(.ThreadC.) failure, probably it is already terminated", "Shutdown", GetLastError());bResult = FALSE;}CloseHandle(ThreadA); CloseHandle(ThreadC); //// 所有的客户线程都结束//THREADLIST::iterator it;while(ThreadList.size()){Sleep(100);}DeleteCriticalSection(&cs);DeleteCriticalSection(&_cs);bRun = FALSE;return bResult;
}void CGenericServer::Reset()
{//// Reset statistic values//Stats.nClientsConnected = 0;Stats.nErrosCount = 0;Stats.nTotalSent = 0;Stats.nTotalRecv = 0;Stats.nTotalHits = 0;Stats.nVisitors = 0;
}//清除线程
void CGenericServer::CleanupThread(WSAEVENT Event, SOCKET s, NewConnectionTag* pNewConn, DWORD dwThreadID)
{if(Event)WSACloseEvent(Event);closesocket(s);EnterCriticalSection(&cs);delete pNewConn;LeaveCriticalSection(&cs);THREADLIST::iterator it;it = find_if(ThreadList.begin(), ThreadList.end(), bind2nd(IDCompare(), dwThreadID));if(it != ThreadList.end()){EnterCriticalSection(&_cs);HANDLE aaa = (*it).hThread;HandleList.push_back((*it).hThread);ThreadList.erase(it);LeaveCriticalSection(&_cs);}elseLogMessage(LOGFILENAME, "Thread not found in the list", "ClientThread");
}//清除线程
void CGenericServer::CleanupThread(WSAEVENT Event, WSAEVENT ShutdownEvent, SOCKET s)
{if(Event)WSACloseEvent(Event);if(ShutdownEvent)WSACloseEvent(ShutdownEvent);if(s)closesocket(s);WSACleanup();
}UINT __stdcall CGenericServer::AcceptThread(LPVOID pParam)
{CGenericServer *pGenericServer = (CGenericServer*)pParam;SOCKET s; // 主线程WORD wVersionRequested;WSADATA wsaData;sockaddr_in saLocal;WSAEVENT Handles[2];WSANETWORKEVENTS NetworkEvents;sockaddr ClientAddr;INT addrlen = sizeof(ClientAddr);sockaddr_in sain;char cAddr[50];int result;saLocal.sin_family = AF_INET;saLocal.sin_port = htons(pGenericServer->ServerPort);saLocal.sin_addr.s_addr = INADDR_ANY;wVersionRequested = MAKEWORD(2, 2);result = WSAStartup(wVersionRequested, &wsaData);if(result != 0){pGenericServer->LogMessage(LOGFILENAME, "WSAStartup(...) failure", "AcceptThread", result);return THREADEXIT_SUCCESS;}if( LOBYTE(wsaData.wVersion) != 2 ||HIBYTE(wsaData.wVersion) != 2) {pGenericServer->LogMessage(LOGFILENAME, "Requested Socket version not exist", "AcceptThread");pGenericServer->CleanupThread(NULL, NULL, NULL);return THREADEXIT_SUCCESS; }s = WSASocket(AF_INET, SOCK_STREAM, 0, (LPWSAPROTOCOL_INFO)NULL, 0, WSA_FLAG_OVERLAPPED);if(s == INVALID_SOCKET){pGenericServer->LogMessage(LOGFILENAME, "WSASocket(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, NULL);return THREADEXIT_SUCCESS;}//// 绑定//result = ::bind(s, (struct sockaddr *)&saLocal, sizeof(saLocal));if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "bind(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, s);return THREADEXIT_SUCCESS;} //// 侦听//result = listen(s, SOMAXCONN);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "listen(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, s);return THREADEXIT_SUCCESS;} pGenericServer->ShutdownEvent = WSACreateEvent();if(pGenericServer->ShutdownEvent == WSA_INVALID_EVENT){pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for ShutdownEvent", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, NULL, NULL, s);return THREADEXIT_SUCCESS;} WSAEVENT Event = WSACreateEvent();if(Event == WSA_INVALID_EVENT){pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure for Event", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;} Handles[0] = pGenericServer->ShutdownEvent;Handles[1] = Event;result = WSAEventSelect(s, Event, FD_ACCEPT);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "WSAEventSelect(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}SetEvent(pGenericServer->ThreadLaunchedEvent);for(;;){DWORD EventCaused = WSAWaitForMultipleEvents(2,Handles, FALSE, WSA_INFINITE, FALSE);if(EventCaused == WAIT_FAILED || EventCaused == WAIT_OBJECT_0){if(EventCaused == WAIT_FAILED)pGenericServer->LogMessage(LOGFILENAME, "WaitForMultipleObjects(...) failure", "AcceptThread", GetLastError());pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}result = WSAEnumNetworkEvents(s, Event, &NetworkEvents);if(result == SOCKET_ERROR) {pGenericServer->LogMessage(LOGFILENAME, "WSAEnumNetworkEvents(...) failure", "AcceptThread", WSAGetLastError());pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;}if(NetworkEvents.lNetworkEvents == FD_ACCEPT){SOCKET ClientSocket = WSAAccept(s, &ClientAddr, &addrlen, NULL, NULL);memcpy(&sain, &ClientAddr, addrlen);sprintf(cAddr, "%d.%d.%d.%d", sain.sin_addr.S_un.S_un_b.s_b1, sain.sin_addr.S_un.S_un_b.s_b2, sain.sin_addr.S_un.S_un_b.s_b3, sain.sin_addr.S_un.S_un_b.s_b4);if(INVALID_SOCKET == ClientSocket){pGenericServer->LogMessage(LOGFILENAME, "WSAAccept(...) failure", "AcceptThread", WSAGetLastError());// 有一个文件错误continue; }else{if(!pGenericServer->AddClient(ClientSocket, cAddr, sain.sin_port)){pGenericServer->LogMessage(LOGFILENAME, "AddClient(...) failure", "AcceptThread");continue; // I think there is no reason to shutdown whole server if just one connection failed}}}}pGenericServer->CleanupThread(Event, pGenericServer->ShutdownEvent, s);return THREADEXIT_SUCCESS;
} //客户端线程
unsigned __stdcall CGenericServer::ClientThread(LPVOID pParam)
{NewConnectionTag *pNewConn = (NewConnectionTag*)pParam;CGenericServer *pGenericServer = pNewConn->pGenericServer;SOCKET s = pNewConn->s;int result;WSAEVENT EventArray[2];WSANETWORKEVENTS NetworkEvents;BOOL bResend = FALSE;WSABUF Buffer;DWORD NumberOfBytesSent;DWORD dwBytesSent;BOOL bKeepAlive = FALSE;string szRequest;string szResponse;WSAEVENT Event = WSACreateEvent();if(Event == WSA_INVALID_EVENT){pGenericServer->LogMessage(LOGFILENAME, "WSACreateEvent(...) failure", "ClientThread", WSAGetLastError());pGenericServer->CleanupThread(NULL, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}result = WSAEventSelect(s, Event, FD_READ | FD_WRITE | FD_CLOSE);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "WSAEventSelect(...) failure", "ClientThread", WSAGetLastError());pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}EventArray[0] = Event;EventArray[1] = pGenericServer->ShutdownEvent;for(;;){DWORD EventCaused = WSAWaitForMultipleEvents(2,EventArray, FALSE, pGenericServer->PersistenceTO ? pGenericServer->PersistenceTO : WSA_INFINITE, FALSE);if(WSA_WAIT_FAILED == EventCaused){pGenericServer->LogMessage(LOGFILENAME, "WSAWaitForMultipleEvents(...) failure", "ClientThread", WSAGetLastError());pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 检查是否服务器任务已经完成 //if(EventCaused == 1 || EventCaused == WSA_WAIT_TIMEOUT){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 分析什么网络事件产生//result = WSAEnumNetworkEvents(s, Event, &NetworkEvents);if(result == SOCKET_ERROR){pGenericServer->LogMessage(LOGFILENAME, "WSAEnumNetworkEvents(...) failure", "ClientThread", WSAGetLastError());continue; }//// 其他情况//if(!NetworkEvents.lNetworkEvents)continue;//// 处理事件// if(NetworkEvents.lNetworkEvents & FD_READ){//// 不需要接收接传入的数据,只需要传给继承类//DWORD NumberOfBytesRecvd;WSABUF Buffers;DWORD dwBufferCount = 1;char szBuffer[MAX_BUFFER];DWORD Flags = 0;Buffers.buf = szBuffer;Buffers.len = MAX_BUFFER;result = WSARecv(s,&Buffers,dwBufferCount,&NumberOfBytesRecvd,&Flags,NULL,NULL);if(result != SOCKET_ERROR){pGenericServer->Stats.nTotalRecv += (double)NumberOfBytesRecvd / 1024;//// 检测是否获得完整的请求//szRequest += string(szBuffer, NumberOfBytesRecvd);if(!pGenericServer->IsComplete(szRequest))continue;if(!pGenericServer->ParseRequest(szRequest, szResponse, bKeepAlive)){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 发送响应倒客户端//NumberOfBytesSent = 0;dwBytesSent = 0;do{Buffer.len = (szResponse.size() - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : szResponse.size() - dwBytesSent; Buffer.buf = (char*)((DWORD)szResponse.c_str() + dwBytesSent);result = WSASend(s, &Buffer, 1, &NumberOfBytesSent,0, 0, NULL);if(SOCKET_ERROR != result)dwBytesSent += NumberOfBytesSent;}while((dwBytesSent < szResponse.size()) && SOCKET_ERROR != result);if(WSAGetLastError() == WSAEWOULDBLOCK){bResend = TRUE;continue;}if(SOCKET_ERROR != result){pGenericServer->Stats.nTotalSent += (double)dwBytesSent / 1024;pGenericServer->DataSent(dwBytesSent);}else{pGenericServer->LogMessage(LOGFILENAME, "WSASend(...) failure", "ClientThread, Primary Send", WSAGetLastError());bKeepAlive = FALSE;}if(!bKeepAlive) {pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}//// 完成请求,清除缓冲区//szRequest.erase(0, string::npos);}elsepGenericServer->LogMessage(LOGFILENAME, "WSARecv(...) failure", "ClientThread", WSAGetLastError());}if((NetworkEvents.lNetworkEvents & FD_WRITE) && bResend){//// 发送响应倒客户端//do{Buffer.len = (szResponse.size() - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : szResponse.size() - dwBytesSent; Buffer.buf = (char*)((DWORD)szResponse.c_str() + dwBytesSent);result = WSASend(s, &Buffer, 1, &NumberOfBytesSent,0, 0, NULL);if(SOCKET_ERROR != result) dwBytesSent += NumberOfBytesSent;}while((dwBytesSent < szResponse.size()) && SOCKET_ERROR != result);if(WSAGetLastError() == WSAEWOULDBLOCK){bResend = TRUE;continue;}if(SOCKET_ERROR != result){pGenericServer->Stats.nTotalSent += (double)dwBytesSent / 1024;pGenericServer->DataSent(dwBytesSent);}else{pGenericServer->LogMessage(LOGFILENAME, "WSASend(...) failure", "ClientThread, Primary Send", WSAGetLastError());bKeepAlive = FALSE;}if(!bKeepAlive){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}bResend = FALSE;//// 清除缓冲区//szRequest.erase(0, string::npos);} if(NetworkEvents.lNetworkEvents & FD_CLOSE){pGenericServer->CleanupThread(Event, s, pNewConn, GetCurrentThreadId());return THREADEXIT_SUCCESS;}}return THREADEXIT_SUCCESS; //
} UINT __stdcall CGenericServer::HelperThread(LPVOID pParam)
{CGenericServer *pGenericServer = (CGenericServer*)pParam;HANDLELIST::iterator it;SetEvent(pGenericServer->ThreadLaunchedEvent);for(;;){if(WaitForSingleObject(pGenericServer->ShutdownEvent, TICK) == WAIT_TIMEOUT){EnterCriticalSection(&pGenericServer->_cs);while(pGenericServer->HandleList.size()){HANDLE h = pGenericServer->HandleList.front(); DWORD n = WaitForSingleObject(h, THREADKILL_TO);if(n == WAIT_TIMEOUT){pGenericServer->LogMessage(LOGFILENAME, "WaitForSingleObject(...) timed out", "HelperThread");if(!TerminateThread(h, THREADEXIT_SUCCESS))pGenericServer->LogMessage(LOGFILENAME, "TerminateThread(.h.) failure, probably it is already terminated", "HelperThread", GetLastError());}CloseHandle(h);pGenericServer->HandleList.pop_front();}LeaveCriticalSection(&pGenericServer->_cs);}elsereturn THREADEXIT_SUCCESS;//不是超时返回,说明服务关闭了。}return THREADEXIT_SUCCESS;
} 相关文章:
【网络编程】HTTP网络编程
13.1 HTTP 简介 HTTP(Hyper Text Transfer Protocol,超文本传输协议)是用于从万维网(WWW:World Wide Web) 服务器(简称Web 服务器)传输超文本到本地浏览器的传送协议,基于TCP/IP 通信协 议来传递数据 (HTML 文件、图片文件、查询结果等)。 13.2 HTTP 的工作原理 …...
【Qt】QWidget属性介绍
🏠个人主页:Yui_ 🍑操作环境:Qt Creator 🚀所属专栏:Qt 文章目录 前言1. enabled属性2.geometry属性2.1 改变控件位置2.2 女神表白程序2.3 知识补充——window frame 3. windowsTitle属性4. windowIcon属性…...
『Rust』Rust运行环境搭建
文章目录 rust编译工具rustupVisual Studio VS Code测试编译手动编译VSCode编译配置 参考完 rust编译工具rustup https://www.rust-lang.org/zh-CN/tools/install 换源 RUSTUP_DIST_SERVER https://rsproxy.cn RUSTUP_UPDATE_ROOT https://rsproxy.cn修改rustup和cargo的安…...
vue/react/vite前端项目打包的时候加上时间最简单版本,防止后端扯皮
如果你是vite项目,直接写一个vite的插件,通过这个插件可以动态注入环境变量,然后当打包的时候,自动注入这个时间到环境变量中,然后在项目中App.vue中或者Main.tsx中打印出来,这就知道是什么时候编译的项目了…...
基于大模型的上睑下垂手术全流程预测与方案优化研究报告
目录 一、引言 1.1 研究背景与目的 1.2 研究意义 1.3 研究方法与创新点 二、上睑下垂相关理论基础 2.1 上睑下垂的定义与分类 2.2 发病机制与影响 2.3 传统治疗方法概述 三、大模型技术原理与应用 3.1 大模型概述 3.2 在医疗领域的应用现状 3.3 用于上睑下垂预测的…...
Cadence学习笔记3
设置 PCB 层叠 初始我们有一个两层板,如果需要添加层叠怎么办? 点击进入层叠设置 首先右击 TOP 层下面的空白,然后鼠标右键进行 add layer 然后选择 Plane(一般层就是这个) 就好 然后 add就行 设置光标显示形式 在 setup ->…...
Linux系统下如何部署svmspro平台
上传svmspro服务 rz回车后选择svmspro.zip上传如果提示rz命令未找到,请先运行 yum install -y lrzsz 安装将svmspro.zip解压出来,并拷贝到/usr/目录下,命令如下: unzip svmspro.zip//解压程序包cp svmspro /usr/ -r//将svmspro文件…...
vue3:八、登录界面实现-忘记密码
一、页面效果 二、实现 1、视图层 <el-form-item class"flex flex-between"><el-checkbox label"记住密码" v-model"remember" /> </el-form-item> 参考 Checkbox 多选框 | Element Plus 2、逻辑层 首先设置记住密码的变…...
el-table树形表格合并相同的值
el-table树形表格合并相同的值 el-table树形表格合并相同的值让Ai进行优化后的代码 el-table树形表格合并相同的值 <style lang"scss" scoped> .tableBox {/deep/ &.el-table th:first-child,/deep/ &.el-table td:first-child {padding-left: 0;} } …...
Apache Tomcat漏洞,对其进行升级
我们付出一些成本,时间的或者其他,最终总能收获一些什么。 升级背景: 近日,新华三盾山实验室监测到 Apache 官方修复了一个远程代码执行漏洞 (CVE-2025-24813) ,其CVSS3 漏洞评分为 7.5 。 影响范围 9.0.0.M1 ≤…...
工程实践:如何使用SU17无人机来实现室内巡检任务
阿木实验室最近发布了科研开发者版本的无人机SU17,该无人机上集成了四目视觉,三维激光雷达,云台吊舱,高算力的机载计算机,是一个非常合适的平台用于室内外巡检场景。同时阿木实验室维护了多个和无人机相关的开源项目。…...
时序优化学习笔记
0.代码对应的底层调用 if-else的判定条件需要LUT实现,累加器的进位需要靠CARRY实现。 1.逻辑级数的概念 简单来讲就是组合逻辑串联的个数 逻辑级数查询命令 report_design_analysis -logic_level_distribution -logic_level_dist_paths 5000 -name design_analy…...
OSPF-3 1类LSA Router LSA
前面两期我们介绍了OSPF的邻居与邻接建立的关系及失败因素和原因 这章我们来说说OSPF是如何通过不同的LSA去描述拓扑的信息以及路由信息 一、概述 OSPF通过不同的LSA来构成LSDB链路状态数据库,再通过SPF算法来计算出最优的最短路径 二、LSA的分类 类型名称描述传播范围1类…...
纳米压印技术制备AR眼镜的参考步骤
纳米压印技术(Nanoimprint Lithography, NIL)在 AR(增强现实)眼镜中的应用主要集中在光学元件的制造上,例如衍射光栅、波导和微透镜阵列等。以下是使用纳米压印技术制备 AR 眼镜光学元件的详细步骤: 1. 设…...
FX-std::list
std::list 是 C 标准库中的一个双向链表容器,定义在 <list> 头文件中。它支持在任意位置高效地插入和删除元素,但不支持随机访问。以下是 std::list 的基本用法和一些常见操作: 1. 包含头文件 #include <list> 2. 定义和初始化…...
【清华大学第七版】DeepSeek赋能家庭教育的实操案例(批改作文+辅助语文/数学/科学学习+制定学习计划)
我用夸克网盘分享了「DeepSeek完整资料合集」,点击链接即可保存。打开「夸克APP」,无需下载在线播放视频,畅享原画5倍速,支持电视投屏。 链接:https://pan.quark.cn/s/621259e4af15 近日,清华大学发布了《…...
HCIA-ACL实验
前提条件:实现底层互通 转发层面 1、基本ACL ①要求PC3不能访问网段192.168.2.0的网段,PC4和客户端能正常访问服务器 ②AR2配置 acl 2000 rule deny source 192.168.1.1 0 匹配流量 int g 0/0/0 traffic-filter inbound acl 2000 接口调用…...
【Gee】项目总结:模仿 GIN 实现简单的 Golang Web 框架
文章目录 Gee 项目回顾Gee 项目总结Golang 已经具备基础的 web 功能,为什么还需要 web 框架?作为 web 框架,Gee 框架完成了哪些功能?如何用 Gee 来构建 web 项目? Gee 项目回顾 上个月月末我按照 Geektutu 的教程&…...
DeepLabv3+改进10:在主干网络中添加LSKBlock|动态调整其大型空间感受野,助力小目标识别
🔥【DeepLabv3+改进专栏!探索语义分割新高度】 🌟 你是否在为图像分割的精度与效率发愁? 📢 本专栏重磅推出: ✅ 独家改进策略:融合注意力机制、轻量化设计与多尺度优化 ✅ 即插即用模块:ASPP+升级、解码器 PS:订阅专栏提供完整代码 目录 论文简介 步骤一 步骤二…...
词向量:优维大模型语义理解的深度引擎
★ 放闸溯源 优维大模型「骨架级」技术干货 第二篇 ⇓ 词向量是Transformer突破传统NLP技术瓶颈的核心,它通过稠密向量空间映射,将离散符号转化为连续语义表示。优维大模型基于词向量技术,构建了运维领域的“语义地图”,实现从…...
编译原理:语法分析程序【附源码和超详细注释】
目录 一 、实验目的 二 、实验内容及步骤 三、程序分析 1. 需求分析 2. 功能分析 1. LL(1)文法功能分析 2. 算符优先文法功能分析 3. 信创分析-主要针对能力提升中国产操作系统上开发内容。 四、源代码 1. LL(1)文法代码 2. 算符优先文法 五、测试结果 1. LL(1)文…...
Go vs Rust vs C++ vs Python vs Java:谁主后端沉浮
一、核心性能对比(基于TechEmpower基准测试) 语言单核QPS延迟(ms)内存消耗适用场景Rust650,0000.1245MB高频交易/区块链C++720,0000.0932MB游戏服务器/实时渲染Go230,0000.45110MB微服务/API网关Java180,0001.2450MB企业ERP/银行系统Python12,0008.5220MBAI接口/快速原型技术…...
使用Flask和OpenCV 实现树莓派与客户端的视频流传输与显示
使用 Python 和 OpenCV 实现树莓派与客户端的视频流传输与显示 在计算机视觉和物联网领域,经常需要将树莓派作为视频流服务器,通过网络将摄像头画面传输到客户端进行处理和显示。本文将详细介绍如何利用picamera2库、Flask 框架以及 OpenCV 库ÿ…...
fs的proxy_media模式失效
概述 freeswitch是一款简单好用的VOIP开源软交换平台。 在fs的使用过程中,某些场景只需要对rtp媒体做透传,又不需要任何处理。 在fs1.6的版本中,我们可以使用proxy_media来代理媒体的转发,媒体的协商由AB路端对端处理ÿ…...
Linux 命名管道
文章目录 🚀 深入理解命名管道(FIFO)及其C实现一、命名管道核心特性1.1 🧩 基本概念 二、💻 代码实现解析2.1 📁 公共头文件(common.hpp)2.2 🖥️ 服务器端(s…...
HDU 学数数导致的
题目解析 首先,数对是有序的,<1,2>和<2,1>被视为不同的两组数字。 其次,数对<p,q>的p和q可以相等。 子序列为 p 0 p q,观察到,中间要出现一个0。那么,我们只需要找到第一个 p 满足与前…...
pjsip pjsua_media_config 结构体说明
clock_rate 描述:设置会议桥(conference bridge)的时钟频率(采样率)。 默认值:0(使用默认值 PJSUA_DEFAULT_CLOCK_RATE,通常为 16kHz)。 作用:影响音频的采样率,常见值有 8000(8kHz)、16000(16kHz)、48000(48kHz)等。snd_clock_rate 描述:设置音频设备的时钟…...
软件/硬件I2C读写MPU6050
MPU6050简介 6轴:3轴加速度,3轴角速度 9轴:3轴加速度,3轴角速度和3轴磁场强度 10轴:3轴加速度,3轴角速度和3轴磁场强度和一个气压强度 加速度计具有静态稳定性,不具有动态稳定性 欧拉角&…...
c++ union使用笔记
c union使用笔记 一、联合的简单使用二、联合与枚举结合三、匿名联合(Anonymous Union)四、关键注意事项五、C17 扩展:std::variant C联合(union)是一种特殊的数据结构,允许在相同内存位置存储不同的数据类…...
Android中的Wifi框架系列
Android wifi框架图 Android WIFI系统引入了wpa_supplicant,它的整个WIFI系统以wpa_supplicant为核心来定义上层接口和下层驱动接口。 Android WIFI主要分为六大层,分别是WiFi Settings层,Wifi Framework层,Wifi JNI 层ÿ…...
