時間:2022-04-15來源:www.outletmksalestore.com作者:電腦系統城
作為Linux下socket(TCP)網絡編程的練習,使用C語言代碼搭建一個簡單的HTTP服務器,完成與瀏覽器之間的交互,最終在瀏覽器上顯示一張圖片。
HTTP協議本身是基于TCP通信協議來傳遞數據(HTML 文件, 圖片文件-也叫超文本傳輸協議),HTTP協議必須工作在客戶端-服務端架構上(本身底層就是TCP),HTTP 默認端口號為 80(瀏覽器訪問默認就是80端口),但是你也可以改為 8080 或者其他端口(可以手動指定端口)。
HTTP協議是無連接的,也就是限制每次連接只處理一個請求;服務器處理完客戶的請求,并收到客戶的應答后,即斷開連接。采用這種方式可以節省傳輸時間。
客戶端向HTTP服務器發送的請求消息格式包括了4個部分:請求行(request line)、 請求頭部(header)、空行、請求數據
下面這個是瀏覽器的請求,可以對比上面這張圖的格式:
1 2 3 4 5 6 7 8 |
GET / HTTP /1 .1 Host: 10.0.0.6 Connection: keep-alive Upgrade-Insecure-Requests: 1 User-Agent: Mozilla /5 .0 (Windows NT 10.0; Win64; x64) AppleWebKit /537 .36 (KHTML, like Gecko) Chrome /94 .0.4606.81 Safari /537 .36 Accept: text /html ,application /xhtml +xml,application /xml ;q=0.9,image /avif ,image /webp ,image /apng ,*/*;q=0.8,application /signed-exchange ; v =b3;q=0.9 Accept-Encoding: gzip , deflate Accept-Language: zh-CN,zh;q=0.9 |
HTTP常用的請求是GET和POST 。
HTTP1.0 定義了三種請求方法: GET, POST 和 HEAD 方法。
HTTP1.1 新增了五種請求方法: OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
HTTP服務器向客戶端的響應也由四個部分組成,分別是:狀態行、消息報頭、空行、響應正文。
例如:
1 2 3 4 5 |
"HTTP/1.1 200 OK\r\n" "Content-type:image/jpeg\r\n" "Content-Length:1234\r\n" "\r\n" "...............正文............." |
上面列出的報文字段含義:
HTTP/1.0 200 OK: Http/1.0 表示當前協議為 Http。 1.0 是協議的版本。 200 表示成功
Content-type : 告訴瀏覽器回送的數據類型
Content-Length: 告訴瀏覽器報文中實體主體的大小,也就是返回的內容長度
上面字段里回復的狀態碼一般有好幾種,分別是:
200 - 請求成功
301 - 資源(網頁等)被永久轉移到其它 URL
404 - 請求的資源(網頁等)不存在
500 - 內部服務器錯誤
第一次請求是由HTTP客戶端(瀏覽器)發起的,HTTP服務器收到請求后,對請求進行解析,然后完成后續的交互。
如果要在瀏覽器上顯示一張圖片,那么交互的流程大致如下:
要讓瀏覽器在界面顯示一張圖片,還得編寫一個HTML代碼給瀏覽器,直接用一個圖片標簽即可。
當前程序使用的HTML代碼比較簡單,代碼下面貼出來了:
1 2 3 4 5 6 7 8 9 10 11 12 |
<! DOCTYPE HTML> < html > < head > < title >jpg</ title > < meta http-equiv = "content-type" content = "text/html; charset=iso-8859-1" /> </ head > < body > < center >< img src = "www/123.jpg" width = "512px" height = "384px" /> </ center > </ body > </ html > |
然后還得準備一張JPG圖片,作為資源文件,方便傳遞給瀏覽器,本地文件結構如下:
下面代碼采用多線程形式響應瀏覽器的請求。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
#include <stdio.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <dirent.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <signal.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <pthread.h> /* 函數功能: 服務器向客戶端發送響應數據 */ int HTTP_ServerSendFile( int client_fd, char *buff, char *type, char *file) { /*1. 打開文件*/ int fd=open(file,2); if (fd<0) return -1; /*2. 獲取文件大小*/ struct stat s_buff; fstat(fd,&s_buff); /*3. 構建響應頭部*/ sprintf (buff, "HTTP/1.1 200 OK\r\n" "Content-type:%s\r\n" "Content-Length:%d\r\n" "\r\n" ,type,s_buff.st_size); /*4. 發送響應頭*/ if (write(client_fd,buff, strlen (buff))!= strlen (buff)) return -2; /*5. 發送消息正文*/ int cnt; while (1) { cnt=read(fd,buff,1024); if (write(client_fd,buff,cnt)!=cnt) return -3; if (cnt!=1024) break ; } return 0; } /*線程工作函數*/ void *thread_work_func( void *argv) { int client_fd=*( int *)argv; free (argv); unsigned int cnt; unsigned char buff[1024]; //讀取瀏覽器發送過來的數據 cnt=read(client_fd,buff,1024); buff[cnt]= '\0' ; printf ( "%s\n" ,buff); if ( strstr (buff, "GET / HTTP/1.1" )) { HTTP_ServerSendFile(client_fd,buff, "text/html" , "www/image_text.html" ); } else if ( strstr (buff, "GET /www/123.jpg HTTP/1.1" )) { HTTP_ServerSendFile(client_fd,buff, "image/jpeg" , "www/888.jpg" ); } else if ( strstr (buff, "GET /favicon.ico HTTP/1.1" )) { HTTP_ServerSendFile(client_fd,buff, "image/x-icon" , "www/1.ico" ); } close(client_fd); //退出線程 pthread_exit(NULL); } int main( int argc, char **argv) { if (argc!=2) { printf ( "./app <端口號>\n" ); return 0; } signal (SIGPIPE,SIG_IGN); //忽略 SIGPIPE 信號--防止服務器異常退出 int sockfd; /*1. 創建socket套接字*/ sockfd=socket(AF_INET,SOCK_STREAM,0); int on = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)); /*2. 綁定端口號與IP地址*/ struct sockaddr_in addr; addr.sin_family=AF_INET; addr.sin_port=htons( atoi (argv[1])); // 端口號0~65535 addr.sin_addr.s_addr=INADDR_ANY; //inet_addr("0.0.0.0"); //IP地址 if (bind(sockfd,( const struct sockaddr *)&addr, sizeof ( struct sockaddr))!=0) { printf ( "服務器:端口號綁定失敗.\n" ); } /*3. 設置監聽的數量,表示服務器同一時間最大能夠處理的連接數量*/ listen(sockfd,20); /*4. 等待客戶端連接*/ int *client_fd; struct sockaddr_in client_addr; socklen_t addrlen; pthread_t thread_id; while (1) { addrlen= sizeof ( struct sockaddr_in); client_fd= malloc ( sizeof ( int )); *client_fd=accept(sockfd,( struct sockaddr *)&client_addr,&addrlen); if (*client_fd<0) { printf ( "客戶端連接失敗.\n" ); return 0; } printf ( "連接的客戶端IP地址:%s\n" ,inet_ntoa(client_addr.sin_addr)); printf ( "連接的客戶端端口號:%d\n" ,ntohs(client_addr.sin_port)); /*創建線程*/ if (pthread_create(&thread_id,NULL,thread_work_func,client_fd)) { printf ( "線程創建失敗.\n" ); break ; } /*設置線程的分離屬性*/ pthread_detach(thread_id); } /*5. 關閉連接*/ close(sockfd); return 0; } |
到此這篇關于Linux下搭建簡易的HTTP服務器完成圖片顯示的文章就介紹到這了,更多相關linux搭建http服務器內容請搜索腳本之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持腳本之家!
2022-04-19
CentOS8使用阿里云yum源異常問題及解決方法2022-04-15
手把手教小白CentOS7安裝GlusterFS集群的全過程圖解2022-04-15
服務器使用Nginx部署Vue項目教程圖解創建項目勾選Web Application勾選【JavaEE Application】名字不是主要的項目結構:創建一個測試的【servlet】 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ...
2022-04-15
proxy_pass 代理轉發 root 與 index root 與 alias location 執行過程 rewrite 重定向...
2022-04-15