c++簡單實現http協議服務器和客戶端

C++ 簡單實現HTTP GET/POST 請求

HTTP(超文本傳輸協議)是一種客戶端與服務端的傳輸協議,最早用於瀏覽器和服務器之間的通信,後來因爲其使用靈活、方便等特點,廣泛用於客戶端與服務端的通信。文章將簡單介紹HTTP協議,同時以C++方式分別實現HTTP GET、POST 請求

HTTP 請求報文

HTTP請求報文的一般格式由4部分組成:請求行、請求頭部、空行、請求數據。如下圖所示:

 

1.jpg

請求行:包含3部分內容:請求方法,URL,協議版本。形式如:GET /?aaa=1 HTTP/1.1。請求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS等。URL指請求服務端的地址,可以是相對地址或域名形式的絕對地址。協議版本主要有HTTP/1.1 HTTP/1.0 HTTP/0.9,後面兩種已很少使用了。

請求頭部:以key/value形式成對錶示頭部參數,以英文冒號分隔。key名稱的約定寫法爲Key,Key-Name,自定義key名稱一般以“X-”開頭。如php的聲明“X-Powered-By:PHP/5.5.4-1”

空行:用來標識請求頭部的數據已結束。

請求數據:可選項,這塊內容只在POST方式下使用,作爲POST的數據表示區域。使用這塊內容,要在請求頭部以Content-Length聲明請求數據長度,以Content-Type聲明請求數據類型。

HTTP POST請求

HTTP POST方式是把請求參數放到HTTP請求報文的請求數據中,爲了讓例子更容易看懂,僅保留HTTP Post關鍵參數,你還可以自定義一些參數,比如瀏覽器喜歡用的User-Agent,Accept,Connection等等

 

char *pHttpPost = "POST %s HTTP/1.1\r\n"
    "Host: %s:%d\r\n"
    "Content-Type: application/x-www-form-urlencoded\r\n"
    "Content-Length: %d\r\n\r\n"
    "%s";
 
char* addr = "http://localhost/post.php";
char* host = "127.0.0.1";
int port = 80;
char* msg = "aaa=1&bbb=2";
 
char strHttpPost[1024] = {0};
sprintf(strHttpPost, pHttpPost, addr, host, port, strlen(msg), msg);
 
//這裏忽略掉了socket連接代碼
 
send(sockClient, strHttpPost, strlen(strHttpPost), 0);

HTTP GET請求

HTTP GET方式是把請求參數放到HTTP請求報文的請求行URL中,所以請求行就是“GET /?aaa=1&bbb=2 HTTP/1.1\r\n”。URL最大長度通常瀏覽器取255,這和文件路徑最大長度有關。雖然HTTP允許更大長度,但不建議怎麼做,如果太長了,可以考慮換成POST方式

 

char *pHttpGet = "GET %s?%s HTTP/1.1\r\n"
    "Host: %s:%d\r\n\r\n";
 
char* addr = "http://localhost/get.php";
char* host = "127.0.0.1";
int post = 80;
char* msg = "aaa=1&bbb=2";
 
char strHttpGet[1024] = {0};
sprintf(strHttpGet, pHttpGet, addr, msg,  host, post);
 
//這裏忽略掉了socket連接代碼
 
send(sockClient, strHttpGet, strlen(strHttpGet), 0);

實現的HTTP

 

#include "HttpConnect.h"
 
#ifdef WIN32
#pragma comment(lib,"ws2_32.lib")
#endif
HttpConnect::HttpConnect()
{
#ifdef WIN32
    //此處一定要初始化一下,否則gethostbyname返回一直爲空
    WSADATA wsa = { 0 };
    WSAStartup(MAKEWORD(2, 2), &wsa);
#endif
}
HttpConnect::~HttpConnect()
{
}
void HttpConnect::socketHttp(std::string host, std::string request)
{
    int sockfd;
    struct sockaddr_in address;
    struct hostent *server;
    sockfd = socket(AF_INET,SOCK_STREAM,0);
    address.sin_family = AF_INET;
    address.sin_port = htons(80);
    server = gethostbyname(host.c_str());
    memcpy((char *)&address.sin_addr.s_addr,(char*)server->h_addr, server->h_length);
    if(-1 == connect(sockfd,(struct sockaddr *)&address,sizeof(address))){
        DBG <<"connection error!"<<std::endl;
        return;
    }
    DBG << request << std::endl;
#ifdef WIN32
    send(sockfd, request.c_str(),request.size(),0);
#else
    write(sockfd,request.c_str(),request.size());
#endif
    char buf[1024*1024] = {0};
    int offset = 0;
    int rc;
#ifdef WIN32
    while(rc = recv(sockfd, buf+offset, 1024,0))
#else
    while(rc = read(sockfd, buf+offset, 1024))
#endif
    {
        offset += rc;
    }
#ifdef WIN32
    closesocket(sockfd);
#else
    close(sockfd);
#endif
    buf[offset] = 0;
    DBG << buf << std::endl;
}
void HttpConnect::postData(std::string host, std::string path, std::string post_content)
{
    //POST請求方式
    std::stringstream stream;
    stream << "POST " << path;
    stream << " HTTP/1.0\r\n";
    stream << "Host: "<< host << "\r\n";
    stream << "User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3\r\n";
    stream << "Content-Type:application/x-www-form-urlencoded\r\n";
    stream << "Content-Length:" << post_content.length()<<"\r\n";
    stream << "Connection:close\r\n\r\n";
    stream << post_content.c_str();
    socketHttp(host, stream.str());
}
void HttpConnect::getData(std::string host, std::string path, std::string get_content)
{
    //GET請求方式
    std::stringstream stream;
    stream << "GET " << path << "?" << get_content;
    stream << " HTTP/1.0\r\n";
    stream << "Host: " << host << "\r\n";
    stream <<"User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.3) Gecko/20100401 Firefox/3.6.3\r\n";
    stream <<"Connection:close\r\n\r\n";
    socketHttp(host, stream.str());
}

 

    HttpConnect *http = new HttpConnect();
    http->getData("127.0.0.1", "/login", "id=liukang&pw=123");
    http->postData("127.0.0.1", "/login","id=liukang&pw=123");


//***********************************
C++實現簡單http服務器
只要懂socket套接字,http請求和響應的格式,就行了



#include <winsock2.h>
#include <string>
#include <assert.h>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

#define PORT 9999

int main(){
    SOCKET sock;
    SOCKET connfd;
    WORD ver = MAKEWORD(2,2);//版本
    WSADATA dat;
    WSAStartup(ver, &dat);

    sock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if (sock == -1)
    {
        return false;
    }
    
    struct sockaddr_in sever_address;
    memset(&sever_address,0,sizeof(sever_address));
    sever_address.sin_family = AF_INET;
    sever_address.sin_addr.s_addr =htonl(INADDR_ANY);
    sever_address.sin_port = htons(PORT);

    int ret = bind(sock, (struct sockaddr*)&sever_address,sizeof(sever_address));
    assert(ret != -1);

    ret = listen(sock,10);
    assert(ret != -1);
    cout<<"wait\n";
    while (1)
    {
        struct sockaddr_in client_address;
        int len=sizeof client_address;
        connfd=accept(sock,(sockaddr*)&client_address,&len);
        char buf[1024];
        int n=recv(connfd,buf,sizeof buf,0);
        buf[n]='\0';
        printf("recv:\n%s\n",buf);
        char head[]="HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";

        string text1="<!DOCTYPE html>\n<html><head>\n<meta charset=\"gb2312\">\n<title>菜鳥教程(runoob.com)";
        string text2="</title>\n<body>\n<h1>我的第一個標題</h1>\n<p>我的第一個段落。</p>\n</body>\n</html>
string text=text1+text2;
        
       send(connfd,head,strlen(head),0);
       send(connfd,text.c_str(),strlen(text.c_str()),0);
       printf("send:\n");
       cout<<head<<text<<endl;

       closesocket(connfd);
    }
    closesocket(sock);
    getchar();
}


//********************************

C++ http服務器和客戶端代碼(無報文格式)
服務器代碼
    SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,0);
    int send_len = 0;
    int recv_len = 0;
    //定義服務端套接字,接受請求套接字
    SOCKET s_server;
    //服務端地址客戶端地址
    SOCKADDR_IN server_addr;
    //初始化套接字庫
    WSADATA wsadata;
    WSAStartup(0x22, &wsadata);    
    //填充服務端地址信息
    //填充服務端信息
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(80);
    std::string msgstr="";
    //創建套接字
    s_server = socket(AF_INET, SOCK_STREAM, 0);
    int result = bind(s_server,(struct sockaddr *) &server_addr,sizeof(server_addr));
    if (result == SOCKET_ERROR) /* 綁定失敗 */
    {
        closesocket(s_server);
        printf("[Web] Fail to bind, error = %d\n", WSAGetLastError());
        return -1; 
    }
    if (listen(s_server,SOMAXCONN)==-1) {
        int err = GetLastError();
        std::cout << "服務器啓動失敗!" << std::endl;
        std::cout << err << std::endl;
    }
    else {
        std::cout << "服務器啓動成功!" << std::endl;
    }
    while(1){
        SOCKADDR_IN from_addr;   /* 客戶端地址  */
        socklen_t from_len = sizeof(from_addr);
        std::string reponse_data="404";
        SOCKET acpt_soc = accept(s_server,(struct sockaddr *) &from_addr,&from_len);
        if (acpt_soc == INVALID_SOCKET) /* 接受失敗 */
        {    int err = GetLastError();
            printf("接收失敗1", WSAGetLastError());
            std::cout << err << std::endl;
            break; 
        }
            char recv_buf [1025] = "";
            recv_len = recv(acpt_soc,recv_buf,1025, 0);
            if (recv_len < 0) {
            std::cout << "接收失敗2!" << std::endl;
            }else{
            std::cout << "接收成功!" << std::endl;
            std::cout << recv_buf << std::endl;
            }
            send_len = send(acpt_soc,recv_buf,strlen(recv_buf), 0);
            if (send_len < 0) {
                std::cout << "發送失敗!" << std::endl;
                closesocket(acpt_soc);
            }else{
                std::cout << "發送成功!" << std::endl;
            }
    }
    //關閉套接字
    closesocket(s_server);
    //釋放DLL資源
    WSACleanup();

客戶端代碼
    SOCKET clientSocket=socket(AF_INET,SOCK_STREAM,0);
        //printf("客戶端嵌套字已經打開!\n");
    int send_len = 0;
    int recv_len = 0;
    //定義服務端套接字,接受請求套接字
    SOCKET s_server;
    //服務端地址客戶端地址
    SOCKADDR_IN server_addr;
    //初始化套接字庫
    WSADATA wsadata;
    WSAStartup(0x22, &wsadata);    
    //填充服務端地址信息
    //填充服務端信息
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(12580);
    string msgstr="";
    //創建套接字
    s_server = socket(AF_INET, SOCK_STREAM, 0);
    int nTimeout = 120000;
    //設置接收超時爲1000ms
    if (SOCKET_ERROR == setsockopt(s_server, SOL_SOCKET, SO_RCVTIMEO, (char *)&nTimeout, sizeof(int)))
    {
        fprintf(stderr, "Set SO_RCVTIMEO error !\n");
    }
    if (connect(s_server, (SOCKADDR *)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
        cout << "服務器連接失敗!" << endl;
        closesocket(s_server);
        //釋放DLL資源
        WSACleanup();
        return 0;
    }
    else {
        cout << "服務器連接成功!" << endl;
    }
    //發送,接收數據
        cout << "開始傳輸桌面信息:"<< endl;
    while(1){
            char send_buf [1025] = "111";
            send_len = send(s_server,send_buf,strlen(send_buf), 0);
                if (send_len < 0) {
                    cout << "發送失敗!" << endl;
                    closesocket(s_server);
                    //釋放DLL資源
                    WSACleanup();
                    return 0;
                }else{
                    cout << "發送成功!" << endl;
                }
                char recv_buf [1025] = "";
                recv_len = recv(s_server,recv_buf,1025, 0);
                if (recv_len < 0) {
                    cout << "接收失敗!" << endl;
                    closesocket(s_server);
                    //釋放DLL資源
                    WSACleanup();
                    return 0;
                }else{
                    cout <<recv_buf<< endl;
                    cout << "接收成功!" << endl;
                }
    }
    //關閉套接字
    closesocket(s_server);
    //釋放DLL資源
    WSACleanup();

//************************************
C++實現簡單的HTTP服務器

#include <Winsock2.h> #include <windows.h> #include <malloc.h> #include <stdio.h> #include <string.h> #include <time.h> #pragma comment (lib,"ws2_32") #define uPort 80 #define MAX_BUFFER 100000 #define SENDBLOCK 200000 #define SERVERNAME "AcIDSoftWebServer/0.1b" #define FileName "HelloWorld.html" typedef struct _NODE_ { SOCKET s; sockaddr_in Addr; _NODE_* pNext; }Node,*pNode; //多線程處理多個客戶端的連接 typedef struct _THREAD_ { DWORD ThreadID; HANDLE hThread; _THREAD_* pNext; }Thread,*pThread; pNode pHead = NULL; pNode pTail = NULL; pThread pHeadThread = NULL; pThread pTailThread = NULL; bool InitSocket();//線程函數 DWORD WINAPI AcceptThread(LPVOID lpParam); DWORD WINAPI ClientThread(LPVOID lpParam); bool IoComplete(char* szRequest); //數據包的校驗函數 bool AddClientList(SOCKET s,sockaddr_in addr); bool AddThreadList(HANDLE hThread,DWORD ThreadID); bool ParseRequest(char* szRequest, char* szResponse, BOOL &bKeepAlive); //我們存放Html文件的目錄 char HtmlDir[512]={0}; void main() { if (!InitSocket()) { printf("InitSocket Error\n"); return; } GetCurrentDirectory(512,HtmlDir); strcat(HtmlDir,"\\HTML\\"); strcat(HtmlDir,FileName); //啓動一個接受線程 HANDLE hAcceptThread = CreateThread(NULL,0,AcceptThread,NULL,0,NULL); //在這裏我們使用事件模型來實現我們的Web服務器 //創建一個事件 WaitForSingleObject(hAcceptThread,INFINITE); } DWORD WINAPI AcceptThread(LPVOID lpParam) //接收線程 { //創建一個監聽套接字 SOCKET sListen = WSASocket(AF_INET,SOCK_STREAM,0,NULL,0,WSA_FLAG_OVERLAPPED); //使用事件重疊的套接字 if (sListen==INVALID_SOCKET) { printf("Create Listen Error\n"); return -1; } //初始化本服務器的地址 sockaddr_in LocalAddr; LocalAddr.sin_addr.S_un.S_addr = INADDR_ANY; LocalAddr.sin_family = AF_INET; LocalAddr.sin_port = htons(uPort); //綁定套接字 80端口 int Ret = bind(sListen,(sockaddr*)&LocalAddr,sizeof(LocalAddr)); if (Ret==SOCKET_ERROR) { printf("Bind Error\n"); return -1; } //監聽 listen(sListen,5); //創建一個事件 WSAEVENT Event = WSACreateEvent(); if (Event==WSA_INVALID_EVENT) { printf("Create WSAEVENT Error\n"); closesocket(sListen); CloseHandle(Event); //創建事件失敗 關閉套接字 關閉事件 return -1; } //將我們的監聽套接字與我們的事件進行關聯屬性爲Accept WSAEventSelect(sListen,Event,FD_ACCEPT); WSANETWORKEVENTS NetWorkEvent; sockaddr_in ClientAddr; int nLen = sizeof(ClientAddr); DWORD dwIndex = 0; while (1) { dwIndex = WSAWaitForMultipleEvents(1,&Event,FALSE,WSA_INFINITE,FALSE); dwIndex = dwIndex - WAIT_OBJECT_0; if (dwIndex==WSA_WAIT_TIMEOUT||dwIndex==WSA_WAIT_FAILED) { continue; } //如果有真正的事件我們就進行判斷 WSAEnumNetworkEvents(sListen,Event,&NetWorkEvent); ResetEvent(&Event); // if (NetWorkEvent.lNetworkEvents == FD_ACCEPT) { if (NetWorkEvent.iErrorCode[FD_ACCEPT_BIT]==0) { //我們要爲新的連接進行接受並申請內存存入鏈表中 SOCKET sClient = WSAAccept(sListen, (sockaddr*)&ClientAddr, &nLen, NULL, NULL); if (sClient==INVALID_SOCKET) { continue; } else { //如果接收成功我們要把用戶的所有信息存放到鏈表中 if (!AddClientList(sClient,ClientAddr)) { continue; } } } } } return 0; } DWORD WINAPI ClientThread(LPVOID lpParam) { //我們將每個用戶的信息以參數的形式傳入到該線程 pNode pTemp = (pNode)lpParam; SOCKET sClient = pTemp->s; //這是通信套接字 WSAEVENT Event = WSACreateEvent(); //該事件是與通信套接字關聯以判斷事件的種類 WSANETWORKEVENTS NetWorkEvent; char szRequest[1024]={0}; //請求報文 char szResponse[1024]={0}; //響應報文 BOOL bKeepAlive = FALSE; //是否持續連接 if(Event == WSA_INVALID_EVENT) { return -1; } int Ret = WSAEventSelect(sClient, Event, FD_READ | FD_WRITE | FD_CLOSE); //關聯事件和套接字 DWORD dwIndex = 0; while (1) { dwIndex = WSAWaitForMultipleEvents(1,&Event,FALSE,WSA_INFINITE,FALSE); dwIndex = dwIndex - WAIT_OBJECT_0; if (dwIndex==WSA_WAIT_TIMEOUT||dwIndex==WSA_WAIT_FAILED) { continue; } // 分析什麼網絡事件產生 Ret = WSAEnumNetworkEvents(sClient,Event,&NetWorkEvent); //其他情況 if(!NetWorkEvent.lNetworkEvents) { continue; } if (NetWorkEvent.lNetworkEvents & FD_READ) //這裏很有意思的 { DWORD NumberOfBytesRecvd; WSABUF Buffers; DWORD dwBufferCount = 1; char szBuffer[MAX_BUFFER]; DWORD Flags = 0; Buffers.buf = szBuffer; Buffers.len = MAX_BUFFER; Ret = WSARecv(sClient,&Buffers,dwBufferCount,&NumberOfBytesRecvd,&Flags,NULL,NULL); //我們在這裏要檢測是否得到的完整請求 memcpy(szRequest,szBuffer,NumberOfBytesRecvd); if (!IoComplete(szRequest)) //校驗數據包 { continue; } if (!ParseRequest(szRequest, szResponse, bKeepAlive)) //分析數據包 { //我在這裏就進行了簡單的處理 continue; } DWORD NumberOfBytesSent = 0; DWORD dwBytesSent = 0; //發送響應到客戶端 do { Buffers.len = (strlen(szResponse) - dwBytesSent) >= SENDBLOCK ? SENDBLOCK : strlen(szResponse) - dwBytesSent; Buffers.buf = (char*)((DWORD)szResponse + dwBytesSent); Ret = WSASend( sClient, &Buffers, 1, &NumberOfBytesSent, 0, 0, NULL); if(SOCKET_ERROR != Ret) dwBytesSent += NumberOfBytesSent; } while((dwBytesSent < strlen(szResponse)) && SOCKET_ERROR != Ret); } if(NetWorkEvent.lNetworkEvents & FD_CLOSE) { //在這裏我沒有處理,我們要將內存進行釋放否則內存泄露 } } return 0; } bool InitSocket() { WSADATA wsadata; if (WSAStartup(MAKEWORD(2,2),&wsadata)==0) //使用Socket前必須調用 參數 作用 返回值 { return true; } return false; } bool AddClientList(SOCKET s,sockaddr_in addr) { pNode pTemp = (pNode)malloc(sizeof(Node)); HANDLE hThread = NULL; DWORD ThreadID = 0; if (pTemp==NULL) { printf("No Memory\n"); return false; } else { pTemp->s = s; pTemp->Addr = addr; pTemp->pNext = NULL; if (pHead==NULL) { pHead = pTail = pTemp; } else { pTail->pNext = pTemp; pTail = pTail->pNext; } //我們要爲用戶開闢新的線程 hThread = CreateThread(NULL,0,ClientThread,(LPVOID)pTemp,0,&ThreadID); if (hThread==NULL) { free(pTemp); return false; } if (!AddThreadList(hThread,ThreadID)) { free(pTemp); return false; } } return true; } bool AddThreadList(HANDLE hThread,DWORD ThreadID) { pThread pTemp = (pThread)malloc(sizeof(Thread)); if (pTemp==NULL) { printf("No Memory\n"); return false; } else { pTemp->hThread = hThread; pTemp->ThreadID = ThreadID; pTemp->pNext = NULL; if (pHeadThread==NULL) { pHeadThread = pTailThread = pTemp; } else { pTailThread->pNext = pTemp; pTailThread = pTailThread->pNext; } } return true; } //校驗數據包 bool IoComplete(char* szRequest) { char* pTemp = NULL; //定義臨時空指針 int nLen = strlen(szRequest); //請求數據包長度 pTemp = szRequest; pTemp = pTemp+nLen-4; //定位指針 if (strcmp(pTemp,"\r\n\r\n")==0) //校驗請求頭部行末尾的回車控制符和換行符以及空行 { return true; } return false; } //分析數據包 bool ParseRequest(char* szRequest, char* szResponse, BOOL &bKeepAlive) { char* p = NULL; p = szRequest; int n = 0; char* pTemp = strstr(p," "); //判斷字符串str2是否是str1的子串。如果是,則該函數返回str2在str1中首次出現的地址;否則,返回NULL。 n = pTemp - p; //指針長度 // pTemp = pTemp + n - 1; //將我們的指針下移 //定義一個臨時的緩衝區來存放我們 char szMode[10]={0}; char szFileName[10]={0}; memcpy(szMode,p,n); //將請求方法拷貝到szMode數組中 if (strcmp(szMode,"GET")==0) //一定要將Get寫成大寫 { //獲取文件名 pTemp = strstr(pTemp," "); pTemp = pTemp + 1; //只有調試的時候才能發現這裏的祕密 memcpy(szFileName,pTemp,1); if (strcmp(szFileName,"/")==0) { strcpy(szFileName,FileName); } else { return false; } } else { return false; } // 分析鏈接類型 pTemp = strstr(szRequest,"\nConnection: Keep-Alive"); //協議版本 n = pTemp - p; if (p>0) { bKeepAlive = TRUE; } else //這裏的設置是爲了Proxy程序的運行 { bKeepAlive = TRUE; } //定義一個回顯頭 char pResponseHeader[512]={0}; char szStatusCode[20]={0}; char szContentType[20]={0}; strcpy(szStatusCode,"200 OK"); strcpy(szContentType,"text/html"); char szDT[128]; struct tm *newtime; long ltime; time(<ime); newtime = gmtime(<ime); strftime(szDT, 128,"%a, %d %b %Y %H:%M:%S GMT", newtime); //讀取文件 //定義一個文件流指針 FILE* fp = fopen(HtmlDir,"rb"); fpos_t lengthActual = 0; int length = 0; char* BufferTemp = NULL; if (fp!=NULL) { // 獲得文件大小 fseek(fp, 0, SEEK_END); fgetpos(fp, &lengthActual); fseek(fp, 0, SEEK_SET); //計算出文件的大小後我們進行分配內存 BufferTemp = (char*)malloc(sizeof(char)*((int)lengthActual)); length = fread(BufferTemp,1,(int)lengthActual,fp); fclose(fp); // 返回響應 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, szDT, SERVERNAME, length, bKeepAlive ? "Keep-Alive" : "close", szContentType); //響應報文 } //如果我們的文件沒有找到我們將引導用戶到另外的錯誤頁面 else { } strcpy(szResponse,pResponseHeader); strcat(szResponse,BufferTemp); free(BufferTemp); BufferTemp = NULL; return true; }

複製代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章