2017-2018-1 《信息安全系统设计基础》实验三报告
2017-2018-1 《信息安全系統設計基礎》實驗三報告
本小組成員:20155303、20155213
————————CONTENTS————————
- 任務一 C語言模擬wc命令
- 任務二 實現傳送文本文件的服務器和客戶端
- 任務三 多線程實現傳送文本文件的服務器和客戶端
- 任務四 使用PC機和實驗箱模擬客戶端服務器并測試
- 實驗感想與體會
- 參考資料
任務一 C語言模擬wc命令
使用man wc命令查看wc命令的基本用法:
可知wc命令的功能為:統計指定文件中的字節數、字數、行數等,并將統計結果顯示輸出。常用的參數為:
- -c:統計字節數
- -l:統計行數
- -m:統計字符數,且不能與-c參數一起使用
- -w:統計字數,一個字被定義為由空白、跳格或換行字符分割的字符串
- -L:打印最長行的長度
- ......
但是,如果我們想統計某文件中出現過某個特定單詞的行數,只用wc命令是無法完成的。我們可以借助管道將wc命令與其他命令(如grep)串聯起來:
grep and test.txt | wc -l
上面命令實現了查找test.txt中所有出現過“and”這個單詞的行,并統計行數。
再進一步,如果想精確到個數(比如一行出現兩次,算作2),可以加上參數-o選項(only),表示只選中那些匹配的地方,結果為:
基于以上分析,代碼如下:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> void wc_func(char *file,int ism,int isw,int isl); int main(int argc ,char *argv[]){int ism,isw,isl,opt;ism = isw = isl = 0;int count = 0;while((opt=getopt(argc,argv,"mwl"))!=-1){count++;switch(opt){case 'm':ism=1;break;case 'w':isw=1;break;case 'l':isl=1;break;case '?':printf("請查看該指令說明文檔 %c\n",optopt);exit(0);}} if(count==0){ism=isw=isl=1;}if(optind==argc){printf("wc error: have no file!\n");}for(;optind<argc;optind++){wc_func(argv[optind],ism,isw,isl);} } void wc_func(char *file,int ism,int isw,int isl) {int t,m,w,l;int state = 0;FILE *in;if((in = fopen(file,"r"))==NULL){printf("wc %s:no this file or dir\n",file);return;}w=m=l=0;while((t=fgetc(in))!=EOF){/*if(t=='\t'||t==' '){ w++;}else if(t=='\n'){l++;}*/if(t == '\n') {l++;state = 0;continue;} else if(t == ' ') {state = 0;continue;} else if(t == '\r') {state = 0;continue;} else {if(state == 0) {state = 1;w++;}continue;}m++; }if(isl)printf("%-5d",l);if(isw)printf("%-5d",w);if(ism)printf("%-5d",m);printf("%-10s\n",file); }運行結果如下:
返回目錄
任務二 實現傳送文本文件的服務器和客戶端
雖然在網絡安全編程基礎課程上學習過網絡編程的相關知識,但基于的是Windows。將其移植到Linux下時需要注意以下幾個方面:
- 頭文件
Windows下winsock.h或winsock2.h;
Linux下netinet/in.h(包括大部分),unistd.h(包括close函數),sys/socket.h。
- 初始化
windows下需要用WSAStartup啟動Ws2_32.lib;
linux下不需要。
- 關閉socket
windows下使用closesocket();
linux下使用close()。
- 類型
windows下SOCKET;
linux下int。
- 多線程(下一個任務會用到)
windows下包含process.h,使用_beginthread和_endthread;
linux下包含pthread.h,使用pthread_create和pthread_exit。
- ......
以上是我在移植過程中遇到的問題,需格外注意。更多情況可參考Socket程序從windows移植到linux的注意事項等相關文章。
基于客戶端與服務器的通信流程,可分別寫出創建服務器和客戶端,以及客戶端和服務器連接的代碼:
/*創建服務器:*/ int start_server(int port, int type){//建立服務器套接字int ss = socket(AF_INET, type, 0);if(ss < 0){printf("create socket error\n");return -1;}//設置服務器地址struct sockaddr_in server_addr; //服務器地址結構bzero(&server_addr, sizeof(struct sockaddr_in)); //清零server_addr.sin_family = AF_INET; //協議族server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //ip地址server_addr.sin_port = htons(port); //端口//綁定地址結構到套接字描述符if(bind(ss, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0){printf("bind error\n");return -1;}//TCPif(SOCK_STREAM == type){//設置偵聽if(listen(ss, LISTEN_SIZE) < 0){printf("listen error\n");return -1;}printf("tcp server start\n");}elseprintf("udp server start\n");return ss; }int create_tcp_server(int port){start_server(port, SOCK_STREAM); }int create_udp_server(int port){start_server(port, SOCK_DGRAM); } /*接受客戶端連接:*/ socklen_t addrlen = sizeof(struct sockaddr); struct sockaddr_in client_addr; //客戶端地址結構 client_sock = accept(ss, (struct sockaddr*)&client_addr, &addrlen); if(client_sock < 0){printf("accept error\n"); }printf("accept success\n"); /*客戶端:*/ int connectsock(char* server_ip, int server_port, int type){int sock_fd = socket(AF_INET, type, 0);if(-1 == sock_fd){printf("create socket error\n");return -1;}struct sockaddr_in server_addr;//設置服務器地址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);inet_pton(AF_INET, server_ip, &server_addr.sin_addr);//連接服務器if(-1 == connect(sock_fd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr_in))){printf("connect server error\n");return -1;}printf("connect server success\n");return sock_fd; } int connect_tcp(char* server_ip, int server_port){return connectsock(server_ip, server_port, SOCK_STREAM); } int connect_udp(char* server_ip, int server_port){return connectsock(server_ip, server_port, SOCK_DGRAM); }搭建好客戶端和服務器,接下來考慮如何傳輸文件。
傳輸數據需要用到緩沖區??蛻舳耸紫容斎胛募?#xff0c;并判斷該文件是否存在。若不存在,則返回錯誤提示;存在,則發送至服務器。服務器接收到數據后,創建一個同樣名稱的文件。至此,完成了最基礎的一步。
傳輸文件內容的思路上面類似,但需注意:文件名稱一般很短,但文件內容大小并不確定。所以不要奢望一次性傳輸完所有的數據,而應設置循環,以固定大小傳輸數據,一直到傳輸結束。
傳輸數據部分的代碼如下:
/*客戶端:*/ char file_name[FILE_NAME_MAX_SIZE+1];bzero(file_name, FILE_NAME_MAX_SIZE+1);printf("Please Input File Name On Server:\t");scanf("%s", file_name);char buffer[BUFFER_SIZE];bzero(buffer,BUFFER_SIZE);strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));//向服務器發送buffer中的數據send(client_socket,buffer,BUFFER_SIZE,0);FILE * fp = fopen(file_name,"r");if(NULL == fp ){printf("File:\t%s Not Found\n", file_name);exit(1);}else{bzero(buffer, BUFFER_SIZE);int file_block_length = 0;while( (file_block_length = fread(buffer,sizeof(char),BUFFER_SIZE, fp))>0){//printf("file_block_length = %d\n",file_block_length);//發送buffer中的字符串到服務器if(send(client_socket,buffer,file_block_length,0)<0){printf("Send File:\t%s Failed\n", file_name);break;}bzero(buffer, BUFFER_SIZE);}}printf("Send File:\t %s To Server[%s] Finished\n",file_name, argv[1]);printf("The File has %d words.\n", wc_func(file_name));fclose(fp); /*服務器:*/char file_name[FILE_NAME_MAX_SIZE+1];bzero(file_name, FILE_NAME_MAX_SIZE+1);char buffer[BUFFER_SIZE];bzero(buffer,BUFFER_SIZE);recv(new_server_socket,file_name,BUFFER_SIZE,0);FILE * fp = fopen(file_name,"w");if(NULL == fp ){printf("File:\t%s Can Not Open To Write\n", file_name);exit(1);}//從客戶端接收數據到buffer中bzero(buffer,BUFFER_SIZE);int len = 0;while( len = recv(new_server_socket,buffer,BUFFER_SIZE,0)){if(len < 0){printf("Recieve Data From Client %s Failed!\n", argv[1]);break;}int write_length = fwrite(buffer,sizeof(char),len,fp);if (write_length<len){printf("File:\t%s Write Failed\n", file_name);break;}bzero(buffer,BUFFER_SIZE); }printf("File:\t%s Transfer Finished!\n",file_name);fclose(fp);按照要求,在服務器端調用計算單詞數的函數,并返回給客戶端即可。
【注:完成代碼已上傳至碼云】
返回目錄
任務三 多線程實現傳送文本文件的服務器和客戶端
通過man -k命令查看與創建進程相關的函數:
我們可以找到“pthread_create()”函數,使用man 3 pthread_create命令可以得知其用法:
說明:
- thread:線程標識符;
- attr:線程屬性設置;
- start_routine:線程函數的起始地址;
- arg:傳遞給start_routine的參數;
- 返回值:成功,返回0;出錯,返回-1。
- 注意:pthread庫不是Linux系統默認的庫,連接時需要使用靜態庫libpthread.a,所以在線程函數在編譯時,需要使用“-lpthread”鏈接庫函數。
因此,服務器需要循環檢測是否有新的連接。如果有,則調用pthread_create()函數創建新的進程,并執行相關代碼。這部分的代碼如下:
while(1){//接受客戶端連接socklen_t addrlen = sizeof(struct sockaddr);struct sockaddr_in client_addr; //客戶端地址結構int client_sock = accept(ss, (struct sockaddr*)&client_addr, &addrlen);if(client_sock < 0){printf("accept error\n");}printf("accept success\n");pthread_t pid;if(pthread_create(&pid, NULL, process_client, &client_sock) < 0){printf("pthread_create error\n");}}創建了新的線程,接下來就可以傳送文件了。過程與任務二類似。
【注:完成代碼已上傳至碼云】
返回目錄
任務四 使用PC機和實驗箱模擬客戶端服務器并測試(未完成)
返回目錄
實驗感想與體會
- 此次實驗首先在Linux下實現了客戶端與服務器傳送文本文件,并模擬wc命令統計文本文件中的單詞數。但一次只能為一個客戶端提供服務的迭代網絡是不現實的,因此,在任務二中創建了一個并發服務器,它為每一個客戶端創建一個單獨的邏輯流。這就允許服務器同時為多個客戶端服務,提高了其使用價值。
- 在完成任務三的過程中遇到了重重困難。本打算在自己筆記本上完成,也方便后續學習,但參考指導書《實驗開發環境使用說明-12.04》中“解決上網與本地網絡調試沖突”的相關講解完成配置后,實驗箱與主機仍然無法ping通。想到之前實驗一在實驗室的PC機上完成得比較順利,于是改用實驗室的PC機,但找不到正確的端口,更換了PC機和實驗箱后仍然解決不了,只能作罷。
- 近期學習效率低下,每周一章的任務常常不能按時完成,總需要后期抽時間補充?;叵肷蠈W期學習Java時,雖然也是一周學習一章,但并沒有覺得如此吃力,花十幾個小時就能完成當周的任務。尤其這次實驗結束,花費大量時間也沒有解決問題,心情失落的同時,不禁懷疑自己的自學能力。且不說與老師的要求相差甚遠,這種學習效率與狀態連自己都不能接受。課本內容較上學期更難更深是一方面,但更多的是自身的問題。比如沒有合理分配時間,做到門門課程兼顧;比如將精力浪費在一些無關緊要的事情上,等等。
- 課下了解到,不少同學也遇到了類似的問題。能及時趕上老師的進度還好,一旦某一陣略有松懈沒跟上節奏,就容易產生惡性循環,落下越來越多的內容。老師一直以來采取的都是自學為主,教學為輔的方式,這一點無可厚非,我認為自學能力可以讓人終生受益。不怕自學的過程中遇到困難,怕就怕在總是遇到無法解決的困難而逐漸放棄。
- 前幾天不慎扭傷了腳,不能在寢室、主樓和圖書館之間隨意跑的日子還真不適應,只能躺著休息,因此對好多任務都心有余而力不足...多虧朋友們悉心的照顧還有及時涂藥,腳在一天天康復,落下的事情也要一點點補回來。那么,就先從努力按時完成本周學習任務開始吧:)
返回目錄
參考資料
- Linux C TCP Socket實現客戶與服務器簡單通信
- Linux C TCPSocket 傳輸文件簡單實例-多線程實現
- wc:統計一個文件里出現某個單詞出現的次數
- Socket程序從Windows移植到Linux下的一些注意事項
- Linux下getopt()函數的簡單使用
- linux網絡編程:用C語言實現的聊天程序(同步通信)
- Linux下C編寫基本的多線程socket服務器
返回目錄
轉載于:https://www.cnblogs.com/Vivian517/p/7832469.html
總結
以上是生活随笔為你收集整理的2017-2018-1 《信息安全系统设计基础》实验三报告的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: css hover图片hover效果兼容
- 下一篇: 团队-石头,剪刀,布-设计文档