20145312《資訊安全系統設計基礎》實驗五 網絡通信
實驗目的與要求
- 掌握在 ARM 開發闆實作一個簡單 WEB 伺服器的過程。
- 學習在 ARM 開發闆上的 SOCKET 網絡程式設計。
- 學習 Linux 下的 signal()函數的使用。
實驗内容
- 學習使用 socket 進行通訊程式設計的過程,了解一個實際的網絡通訊應用程式整體設計,閱讀 HTTP 協定的相關内容,學習幾個重要的網絡函數的使用方法。
- 讀懂 HTTPD.C 源代碼。在此基礎上增加一些其他功能。在 PC 計算機上使用浏覽器測試嵌入式 WEB 伺服器的功能。
實驗步驟
1. 搭建實驗環境
- 連接配接arm開發闆
- 建立超級終端
- 啟動實驗平台(redhat虛拟機)
- 配置同網段IP
- 安裝arm編譯器(bc共享檔案夾)
- 配置環境變量(redhat虛拟機中)
2. 共享代碼檔案
- 将
目錄下的07_httpd檔案夾拷貝到共享檔案夾bc中\experiment\exp5\exp5\ws
3. 編譯應用程式
- 運作make産生可執行檔案httpd
4. 下載下傳調試
- 使用 NFS 服務方式将 httpd 下載下傳到開發闆上,指令
mount -t nfs -o nolock 192.168.0.234:/home/bc /host
-
運作可執行檔案httpd./httpd
5. 本機測試
- 在桌上型電腦的浏覽器中輸入
(192.168.0.121為 UP-CUP S2410 實驗闆的 IP位址,可以使用ifconfig在arm中檢視),觀察在客戶機的浏覽器中的連接配接請求結果和在開發闆上的伺服器的列印資訊。http://192.168.0.121
- 此時超級終端顯示連接配接成功
程式設計與分析
接口設計
int HandleConnect(int fd)
{
FILE *f;
char buf[160];
char buf1[160];
f = fdopen(fd,"a+");
if (!f) {
fprintf(stderr, "httpd: Unable to open httpd input fd, error %d\n", errno);
alarm(TIMEOUT);
close(fd);
alarm(0);
return 0;
}
setbuf(f, 0);
alarm(TIMEOUT);
if (!fgets(buf, 150, f)) {
fprintf(stderr, "httpd: Error reading connection, error %d\n", errno);
fclose(f);
alarm(0);
return 0;
}
#ifdef DEBUG
printf("buf = '%s'\n", buf);
#endif
alarm(0);
referrer[0] = '\0';
content_length = -1;
alarm(TIMEOUT);
//read other line to parse Rrferrer and content_length infomation
while (fgets(buf1, 150, f) && (strlen(buf1) > 2)) {
alarm(TIMEOUT);
#ifdef DEBUG
printf("Got buf1 '%s'\n", buf1);
#endif
if (!strncasecmp(buf1, "Referer:", 8)) {
char * c = buf1+8;
while (isspace(*c))
c++;
strcpy(referrer, c);
}
else if (!strncasecmp(buf1, "Referrer:", 9)) {
char * c = buf1+9;
while (isspace(*c))
c++;
strcpy(referrer, c);
}
else if (!strncasecmp(buf1, "Content-length:", 15)) {
content_length = atoi(buf1+15);
}
}
alarm(0);
if (ferror(f)) {
fprintf(stderr, "http: Error continuing reading connection, error %d\n", errno);
fclose(f);
return 0;
}
ParseReq(f, buf);
alarm(TIMEOUT);
fflush(f);
fclose(f);
alarm(0);
return 1;
}
- 客戶連接配接處理
- 函數名:int HandleConnect(int fd)
- 參數:客戶連接配接檔案描述字
解析客戶請求
int ParseReq(FILE *f, char *r)
{
char *bp;
struct stat stbuf;
char * arg;
char * c;
int e;
int raw;
#ifdef DEBUG
printf("req is '%s'\n", r);
#endif
while(*(++r) != ' '); /*skip non-white space*/
while(isspace(*r))
r++;
while (*r == '/')
r++;
bp = r;
while(*r && (*(r) != ' ') && (*(r) != '?'))
r++;
#ifdef DEBUG
printf("bp='%s' %x, r='%s' \n", bp, *bp,r);
#endif
if (*r == '?')
{
char * e;
*r = 0;
arg = r+1;
if (e = strchr(arg,' '))
{
*e = '\0';
}
} else
{
arg = 0;
*r = 0;
}
c = bp;
/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/
if (c[0] == 0x20){
c[0]='.';
c[1]='\0';
}
/*zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz*/
if(c[0] == '\0') strcat(c,".");
if (c && !stat(c, &stbuf))
{
if (S_ISDIR(stbuf.st_mode))
{
char * end = c + strlen(c);
strcat(c, "/index.html");
if (!stat(c, &stbuf))
{
DoHTML(f, c);
}
else
{
*end = '\0';
DoDir(f,c);
}
}
else if (!strcmp(r - 4, ".gif"))
DoGif(f,c);
else if (!strcmp(r - 4, ".jpg") || !strcmp(r - 5, ".jpeg"))
DoJpeg(f,c);
else if (!strcmp(r - 4, ".htm") || !strcmp(r - 5, ".html"))
DoHTML(f,c);
else
DoText(f,c);
}
else{
PrintHeader(f,'h');
alarm(TIMEOUT);
fprintf(f, "<html><head><title>404 File Not Found</title></head>\n");
fprintf(f, "<body>The requested URL was not found on this server</body></html>\n");
alarm(0);
}
return 0;
}
- 函數名:int ParseReq(FILE f, char r)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。
- 參數 2:字元串指針,待解析的字元串。
發送目前目錄檔案清單資訊
- 函數名:int DoDir(FILE f, char name)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。用于寫入目錄檔案資訊資料。
- 參數 2:目錄名,表示客戶請求的目錄資訊。
發送 HTML 檔案内容
int DoHTML(FILE *f, char *name)
{
char *buf;
FILE *infile;
int count;
char * dir = 0;
if (!(infile = fopen(name,"r"))) {
alarm(TIMEOUT);
fprintf(stderr, "Unable to open HTML file %s, %d\n", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'h');
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
- 函數名:int DoHTML(FILE f, char name)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。用于寫入檔案資訊資料。
- 參數 2:客戶請求的檔案名。
發送純文字(TXT)檔案内容
int DoText(FILE *f, char *name)
{
char *buf;
FILE *infile;
int count;
if (!(infile = fopen(name,"r"))) {
alarm(TIMEOUT);
fprintf(stderr, "Unable to open text file %s, %d\n", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'t');
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
- 函數名:int DoText(FILE f, char name)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。用于寫入檔案資訊資料。
- 參數 2:客戶請求的檔案名。
發送 JPEG 圖像檔案内容
int DoJpeg(FILE *f, char *name)
{
char *buf;
FILE * infile;
int count;
if (!(infile = fopen(name, "r"))) {
alarm(TIMEOUT);
fprintf(stderr, "Unable to open JPEG file %s, %d\n", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'j');
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
- 函數名:int DoJpeg(FILE f, char name)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。用于寫入檔案資訊資料。
- 參數 2:客戶請求的檔案名。
發送 GIF 圖像檔案内容
int DoGif(FILE *f, char *name)
{
char *buf;
FILE * infile;
int count;
if (!(infile = fopen(name, "r"))) {
alarm(TIMEOUT);
fprintf(stderr, "Unable to open GIF file %s, %d\n", name, errno);
fflush(f);
alarm(0);
return -1;
}
PrintHeader(f,'g');
copy(infile,f); /* prints the page */
alarm(TIMEOUT);
fclose(infile);
alarm(0);
return 0;
}
- 函數名:int DoGif(FILE f, char name)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。用于寫入檔案資訊資料。
- 參數 2:客戶請求的檔案名。
發送 HTTP 協定資料頭子產品設計
- 根據參數的不同,發送不同的 HTTP 協定頭資訊。
- 函數名:int PrintHeader(FILE *f, int content_type)
- 參數:
- 參數 1:檔案流 FILE 結構指針,用于表示客戶連接配接的檔案流指針。用于寫入 HTTP協定資料頭資訊。
- 參數 2:資訊類型,用于确定發送的 HTTP 協定資料頭資訊。
- 算法
- 函數定義為:int PrintHeader(FILE *f, int content_type)
- 發送請求成功資訊:HTTP/1.0 200 OK。
- 根據文檔類型發送相應的資訊:fprintf(),函數中的第一個參數 f 為客戶連接配接檔案流句柄。
int PrintHeader(FILE *f, int content_type)
{
alarm(TIMEOUT);
fprintf(f,"HTTP/1.0 200 OK\n");
switch (content_type)
{
case 't':
fprintf(f,"Content-type: text/plain\n");
break;
case 'g':
fprintf(f,"Content-type: image/gif\n");
break;
case 'j':
fprintf(f,"Content-type: image/jpeg\n");
break;
case 'h':
fprintf(f,"Content-type: text/html\n");
break;
}
fprintf(f,"Server: uClinux-httpd 0.2.2\n");
fprintf(f,"Expires: 0\n");
fprintf(f,"\n");
alarm(0);
return(0);
}
- 發送伺服器資訊:
fprintf(f,"Server: AMRLinux-httpd 0.2.4\n");
- 發送檔案過期為永不過期:
fprintf(f,"Expires: 0\n");
主程式設計
int main(int argc, char *argv[])
{
int fd, s;
int len;
volatile int true = 1;
struct sockaddr_in ec;
struct sockaddr_in server_sockaddr;
pthread_t th_key;
void * retval;
signal(SIGCHLD, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
signal(SIGALRM, sigalrm);
chroot(HTTPD_DOCUMENT_ROOT);
printf("starting httpd...\n");
printf("press q to quit.\n");
// chdir("/");
if (argc > 1 && !strcmp(argv[1], "-i")) {
/* I'm running from inetd, handle the request on stdin */
fclose(stderr);
HandleConnect(0);
exit(0);
}
if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
perror("Unable to obtain network");
exit(1);
}
if((setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (void *)&true,
sizeof(true))) == -1) {
perror("setsockopt failed");
exit(1);
}
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_port = htons(SERVER_PORT);
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(s, (struct sockaddr *)&server_sockaddr,
sizeof(server_sockaddr)) == -1) {
perror("Unable to bind socket");
exit(1);
}
if(listen(s, 8*3) == -1) { /* Arbitrary, 8 files/page, 3 clients */
perror("Unable to listen");
exit(4);
}
pthread_create(&th_key, NULL, key, 0);
/* Wait until producer and consumer finish. */
printf("wait for connection.\n");
while (1) {
len = sizeof(ec);
if((fd = accept(s, (void *)&ec, &len)) == -1) {
exit(5);
close(s);
}
HandleConnect(fd);
}
pthread_join(th_key, &retval);
}
功能說明
- 系統的總入口,也是系統的主要控制函數。分别完成如下功能:
建立環境設定。
設定信号處理方式。
建立偵聽 TCP 流方式 SOCKET 并綁定 80 端口。
建立連接配接偵聽及客戶連接配接處理調用主循環。
算法流程圖
實驗過程中遇到的問題
問題:
- 在按照實驗指導書中使用make編譯時,出現無法編譯的問題。
解決:
- 找到了原因,是拷代碼時沒有将一個Rules.mak的MAK類型檔案一起拷過來,導緻make指令不能使用
問題:
- 按照實驗步驟,先搭建ARM環境,統一ARM,主機和虛拟機三者保證它們在同一網段之後,就可以共享檔案夾了,然後修改PATH變量,保證armv4l-unknown-linux-gcc工具的使用,然後進入測試代碼的檔案夾,make 一下後發現本應該出現下圖的界面,但是最後一行自動編譯指令變成了亂碼
解決:
- 手動輸入了最後一行:armv4l-unknown-linux-gcc -o http httpd.o copy.o -lpthread問題就解決了,ls一下,發現httpd存在于檔案夾目錄下了,然後将它使用NFS服務下載下傳到開發闆上,并運作它。最後在我們的本機浏覽器上輸入實驗闆的IP位址
實驗心得
本次實驗是基于實驗1環境的搭建下進行得實驗,在實驗中我自以為熟絡實驗環境搭建,隻想着同一網段實作檔案的共享,卻忘記将armv4l工具放入共享檔案夾,也沒有設定PATH變量,導緻實驗出現錯誤,本來補救是很簡單的事,但是在緊迫的時間中對我和小夥伴增加了壓力,怎麼實驗做一步錯一步,老是得不到應該出現的回報,這個時候團隊合作精神就非常重要了,不要慌張也不要彼此責備,包括後面兩個人實驗報告的合作也一樣,這次實驗除了對WEB伺服器的了解,更多的事我對團隊合作的了解。
轉載于:https://www.cnblogs.com/yx20145312/p/6132395.html