C/C++實現(xiàn)高并發(fā)http服務(wù)器的代碼示例
http高并發(fā)服務(wù)器實現(xiàn)
基礎(chǔ)知識
html,全稱為html markup language,超文本標記語言。
http,全稱hyper text transfer protocol,超文本傳輸協(xié)議。用于從萬維網(wǎng)(WWW:World Wide Web)服務(wù)器傳輸超文本到本地瀏覽器的傳送協(xié)議。
客戶端請求的格式:
請求方法有:GET、POST等。URL:請求地址。協(xié)議版本:HTTP的版本。
服務(wù)器響應(yīng)的格式:
----- | 響應(yīng)代號 | 代號描述 |
---|---|---|
服務(wù)器上存在請求的內(nèi)容,并可以響應(yīng)給客戶端 | 200 | OK |
客戶端的請求有異常,方法有問題 | 501 | Method Not Implemented |
服務(wù)器收到請求后,因為自生的問題沒法響應(yīng) | 500 | Internal Server Error |
請求的內(nèi)容不存在 | 404 | NOT FOUND |
客戶端發(fā)送的請求格式有問題等(一般不存在) | 400 | BAD REQUEST |
http服務(wù)器實現(xiàn)
文件概念
文件的Inode元信息
表示文件的索引節(jié)點,存儲著文件的元信息,例如文件得創(chuàng)建者,文件創(chuàng)建日期,文件大小等。每個inode都有一個號碼,操作系統(tǒng)用inode號碼來識別不同的文件,使用命令ls -i
可以查看inode號碼。
stat函數(shù)
stat
是C++用于讀取文件資源管理器的庫函數(shù),頭文件為:
#include<sys/stat.h> #include<sys/types.h> #include<unisted.h> int stat(const char *path,struct stat *buf); int fstat(int fd,struct stat *buf); int lstat(const char *path,struct stat *buf); parameter: path:文件路徑 buf:傳入的保存文件狀態(tài)的指針,用于保存文件的狀態(tài) fd:文件描述符 return:成功返回0,失敗返回-1,并設(shè)置errno
stat
的結(jié)構(gòu)體內(nèi)容如下所示:
struct stat { dev_t st_dev; /* ID of device containing file */ ino_t st_ino; /* inode number */ mode_t st_mode; /* S_ISREG(st_mode) 是一個普通文件 S_ISDIR(st_mode) 是一個目錄*/ nlink_t st_nlink; /* number of hard links */ uid_t st_uid; /* user ID of owner */ gid_t st_gid; /* group ID of owner */ dev_t st_rdev; /* device ID (if special file) */ off_t st_size; /* total size, in bytes */ blksize_t st_blksize; /* blocksize for filesystem I/O */ blkcnt_t st_blocks; /* number of 512B blocks allocated */ time_t st_atime; /* time of last access */ time_t st_mtime; /* time of last modification */ time_t st_ctime; /* time of last status change */ };
并發(fā)和并行
并發(fā)與并行的區(qū)別簡單來說所謂的并發(fā)指的是多個進程按照一定的時間間隔進行,只不過這個時間間隔很小,人類難以感受到而已,實際上在微觀角度,進程的并發(fā)執(zhí)行還是順序執(zhí)行。
高并發(fā):高并發(fā)是互聯(lián)網(wǎng)分布式框架設(shè)計中必須要考慮的因素之一,通常指的是,通過設(shè)計系統(tǒng)能夠同時并行處理很多請求。
線程可以并行的執(zhí)行任務(wù)
//頭文件 #include<pthread.h> //函數(shù) int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg); pthread_t:當前Linux中可理解為:typedef unsigned long int pthread_t args1:傳出參數(shù),保存系統(tǒng)為我們分配好的線程ID; args2:通常傳NULL,表示使用線程默認屬性。若想使用具體屬性也可以修改該參數(shù)。 args3:函數(shù)指針,指向線程主函數(shù)(線程體),函數(shù)運行結(jié)束,則線程結(jié)束。 args4:線程主函數(shù)執(zhí)行期間所需要使用的函數(shù)。
在一個線程中調(diào)用pthread_create()
創(chuàng)建新的線程后,當前線程從pthread_create()
返回繼續(xù)往下執(zhí)行,而新的線程所執(zhí)行的代碼由我們傳給pthread_create
的函數(shù)指針start_routine
決定。start_routine
函數(shù)接收一個參數(shù),是通過pthread_create
的arg
參數(shù)傳遞給它的,該參數(shù)的類型為void *
,這個指針按什么類型解釋由調(diào)用者自己定義。start_routine
的返回值類型也是void *
,這個指針的含義同樣由調(diào)用者自己定義。start_routine
返回時,這個線程就退出了,其它線程可以調(diào)用pthread_join
得到start_routine
的返回值。
pthread_create成功返回后,新創(chuàng)建的線程的id被填寫到thread參數(shù)所指向的內(nèi)存單元。
attr參數(shù)表示線程屬性。
pthread_exit (status)
pthread_exit
用于顯式地退出一個線程。通常情況下,pthread_exit()
函數(shù)是在線程完成工作后無需繼續(xù)存在時被調(diào)用。
如果 main()
是在它所創(chuàng)建的線程之前結(jié)束,并通過 pthread_exit()
退出,那么其他線程將繼續(xù)執(zhí)行。否則,它們將在 main()
結(jié)束時自動被終止。
gcc/g++
編譯時需要添加 -pthread
進行編譯。
gcc test.c -pthread -o test
簡單的多線程實例:
#include <iostream> #include <cstdlib> #include <pthread.h> using namespace std; #define NUM_THREADS 5 void *PrintHello(void *threadid) { // 對傳入的參數(shù)進行強制類型轉(zhuǎn)換,由無類型指針變?yōu)檎螖?shù)指針,然后再讀取 int tid = *((int*)threadid); cout << "Hello Runoob! 線程 ID, " << tid << endl; pthread_exit(NULL); } int main () { pthread_t threads[NUM_THREADS]; int indexes[NUM_THREADS];// 用數(shù)組來保存i的值 int rc; int i; for( i=0; i < NUM_THREADS; i++ ){ cout << "main() : 創(chuàng)建線程, " << i << endl; indexes[i] = i; //先保存i的值 // 傳入的時候必須強制轉(zhuǎn)換為void* 類型,即無類型指針 rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&(indexes[i])); if (rc){ cout << "Error:無法創(chuàng)建線程," << rc << endl; exit(-1); } } pthread_exit(NULL); }
最終代碼
服務(wù)器代碼樣例:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <sys/stat.h> #include <ctype.h> #include <arpa/inet.h> #include <errno.h> #include<pthread.h> #define SERVER_PORT 80 void do_http_request(int client_sock); int get_line(int client_sock, char *buf, int size); void do_http_response(int client_sock, const char *path); void headers(int client_sock, FILE *resource); void cat(int client_sock, FILE *resource); void not_found(int client_sock); void inner_error(int client_sock); int main(void) { int sock; struct sockaddr_in server_addr; sock = socket(AF_INET, SOCK_STREAM, 0); // printf("wait \n"); bzero(&server_addr, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_addr.s_addr = htonl(INADDR_ANY); server_addr.sin_port = htons(SERVER_PORT); bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)); listen(sock, 128); printf("wait client connect\n"); int done = 1; while (done) { struct sockaddr_in client; int client_sock, len, i; char client_ip[64]; char buf[256]; socklen_t client_addr_len; client_addr_len = sizeof(client); client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len); printf("client ip: %s \t port is : %d \n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port)); pthread_t tid; int* ptr_int=NULL; int err=0; ptr_int=(int*)malloc(sizeof(int)); *ptr_int=client_sock; if(err=pthread_create(&tid,NULL,do_http_request,(void*)ptr_int)){ printf(stderr,"cannot create thread. reason: %s\n",strerror(errno)); if(ptr_int) free(ptr_int); } // do_http_request(client_sock); // close(client_sock); } close(sock); return 0; } void* do_http_request(void* p_client_sock) { int len = 0; char buf[256], method[64], url[256], path[256]; int client_sock=*(int *)p_client_sock; struct stat st; // http response struct: request_method url protocol_version \r\n len = get_line(client_sock, buf, sizeof(buf)); if (len > 0) { int i = 0, j = 0; while (!isspace(buf[j]) && i < sizeof(method) - 1) { method[i] = buf[j]; i++; j++; } // set end tag method[i] = '\0'; printf("method: %s\n", method); // method is GET request if (strncasecmp(method, "GET", i) == 0) { printf("request method is GET\n"); while (isspace(buf[j])) { j++; } i = 0; while (!isspace(buf[j]) && i < sizeof(url) - 1) { url[i] = buf[j]; i++; j++; } url[i] = '\0'; if (strncasecmp(url, "/favicon.ico", i) == 0) { strcpy(url, "/hello.html"); } printf("url:%s\n", url); // read surplus request do { len = get_line(client_sock, buf, sizeof(buf)); printf("%s\n", buf); } while (len > 0); // get local url file, and process ? in url, eg. url=128.0.0.2/hel.html?wang=dedefe char *pos = strchr(url, '?'); if (pos) { // \0 represent string end tag *pos = '\0'; printf("real url: %s\n", url); } sprintf(path, "./html_doc%s", url); printf("path:%s\n", path); // execute http response // if file is exist, to response 200, ok,and send html file,else response 404 NOT FOUND if (stat(path, &st) == -1) { printf("--------------------"); fprintf(stderr, "stat %s failed. reason :%s\n", strerror(errno)); not_found(client_sock); } else { printf("*****************"); if (S_ISDIR(st.st_mode)) { strcat(path, "/index.html"); } do_http_response(client_sock, path); } } else { // request method is not GET,read http head, and response client request fprintf(stderr, "warning, other request [%s]\n", method); do { len = get_line(client_sock, buf, sizeof(buf)); printf("%s\n", buf); } while (len > 0); // unimplement() } } else { printf("method is error"); } close(client_sock); if(p_client_sock) free(p_client_sock); } void do_http_response(int client_sock, const char *path) { FILE *resource = NULL; resource = fopen(path, "r"); if (resource == NULL) { not_found(client_sock); return; } // send http head headers(client_sock, resource); // send http body cat(client_sock, resource); // printf("end response!!!!"); fclose(resource); } void headers(int client_sock, FILE *resource) { struct stat st; int fileid = 0; char temp[64]; char buf[1024] = {0}; strcpy(buf, "HTTP/1.0 200 OK\r\n"); strcat(buf, "Server: Martin Server\r\n"); strcat(buf, "Content-Type: text/html\r\n"); strcat(buf, "Connection: Close\r\n"); fileid = fileno(resource); /* fstat: Get file attributes for the file, device, pipe, or socket that file descriptor FD is open on and put them in BUF. */ if (fstat(fileid, &st) == -1) { inner_error(client_sock); } snprintf(temp, 64, "Content-Length:%d\r\n\r\n", st.st_size); strcat(buf, temp); printf(stdout, "header: %s", buf); if (send(client_sock, buf, strlen(buf), 0) < 0) { fprintf(stderr, "send fail,data %s, reason %s", buf, strerror(errno)); } } void cat(int client_sock, FILE *resource) { char buf[1024]; fgets(buf, sizeof(buf), resource); while (!feof(resource)) { int len = write(client_sock, buf, strlen(buf)); if (len < 0) { fprintf(stderr, "send boady error. reason %s\n", strerror(errno)); break; } fprintf(stdout, "%s", buf); fgets(buf, sizeof(buf), resource); } } int get_line(int client_sock, char *buf, int size) { int count = 0; char ch = '\0'; int len = 0; while (count < size - 1 && ch != '\n') { len = read(client_sock, &ch, 1); if (len == 1) { if (ch == '\r') continue; else if (ch == '\n') break; buf[count] = ch; count++; } else if (len == -1) { perror("read fail"); count = -1; break; } else { fprintf(stderr, "client close.\n"); count = -1; break; } } if (count >= 0) buf[count] = '\0'; return count; } void not_found(int client_sock) { const char *reply = "HTTP/1.0 404 NOT FOUND\r\n\ Content-Type: text/html\r\n\ \r\n\ <HTML lang=\"zh-CN\">\r\n\ <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\ <HEAD>\r\n\ <TITLE>NOT FOUND</TITLE>\r\n\ </HEAD>\r\n\ <BODY>\r\n\ <P>文件不存在!\r\n\ <P>The server could not fulfill your request because the resource specified is unavailable or none xistent.\r\n\ </BODY>\r\n\ </HTML>"; int len = write(client_sock, reply, strlen(reply)); fprintf(stdout, reply); if (len < 0) { fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno)); } } void inner_error(int client_sock) { const char *reply = "HTTP/1.0 500 Internal Sever Error\r\n\ Content-Type: text/html\r\n\ \r\n\ <HTML lang=\"zh-CN\">\r\n\ <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\ <HEAD>\r\n\ <TITLE>Inner Error</TITLE>\r\n\ </HEAD>\r\n\ <BODY>\r\n\ <P>服務(wù)器內(nèi)部出錯.\r\n\ </BODY>\r\n\ </HTML>"; int len = write(client_sock, reply, strlen(reply)); fprintf(stdout, reply); if (len <= 0) { fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno)); } }
客戶端代碼樣例:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #define SERVER_PORT 666 #define SERVER_IP "127.0.0.1" int main(int argc, char *argv[]) { int sockfd; char *message; struct sockaddr_in servaddr; int n; char buf[64]; if (argc != 2) { fputs("Usage: ./echo_client message \n", stderr); exit(1); } message = argv[1]; printf("message: %s\n", message); sockfd = socket(AF_INET, SOCK_STREAM, 0); // 重置結(jié)構(gòu)體的內(nèi)存空間 memset(&servaddr, '\0', sizeof(struct sockaddr_in)); servaddr.sin_family = AF_INET; inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr); servaddr.sin_port = htons(SERVER_PORT); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); write(sockfd, message, strlen(message)); n = read(sockfd, buf, sizeof(buf) - 1); if (n > 0) { buf[n] = '\0'; printf("receive: %s\n", buf); } else { perror("error!!!"); } printf("finished.\n"); close(sockfd); return 0; }
到此這篇關(guān)于C/C++實現(xiàn)高并發(fā)http服務(wù)器的代碼示例的文章就介紹到這了,更多相關(guān)C/C++實現(xiàn)高并發(fā)http服務(wù)器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺析C++中dynamic_cast和static_cast實例語法詳解
這篇文章主要介紹了淺析C++中dynamic_cast和static_cast實例演示,包括static_cast語法知識和static_cast的作用講解,namic_cast 語法詳解,需要的朋友可以參考下2021-07-07對比分析C語言中的gcvt()和ecvt()以及fcvt()函數(shù)
這篇文章主要介紹了對比分析C語言中的gcvt和ecvt以及fcvt函數(shù),都是將數(shù)字轉(zhuǎn)化為字符串,注意其之間的功能區(qū)別,需要的朋友可以參考下2015-08-08一篇文章教你用C語言模擬實現(xiàn)字符串函數(shù)
這篇文章主要介紹了C語言模擬實現(xiàn)字符串函數(shù),開發(fā)程序的時候經(jīng)常使用到一些字符串函數(shù),例如求字符串長度,拷貝字符串……,需要的朋友可以參考下2021-09-09C語言實現(xiàn)圖書管理系統(tǒng)課程設(shè)計
這篇文章主要為大家詳細介紹了C語言實現(xiàn)圖書管理系統(tǒng)課程設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07C++實現(xiàn)LeetCode(172.求階乘末尾零的個數(shù))
這篇文章主要介紹了C++實現(xiàn)LeetCode(172.求階乘末尾零的個數(shù)),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-08-08