2018-2019-1 20165315 实验三 实时系统
2018-2019-1 20165315 實驗三 實時系統
任務一 學習使用Linux命令wc(1)
基于Linux Socket程序設計實現wc(1)服務器(端口號是你學號的后6位)和客戶端,客戶端傳一個文本文件給服務器,服務器返加文本文件中的單詞數
實驗步驟
- 使用man wc命令查看wc(1)
wc命令詳解:
- 語法:wc [選項] 文件
- 選項含義
- -c:統計字節數
- -l:統計行數
- -w:統計字數
- wc代碼實現
- 實現文件傳輸功能
結合我們在網絡安全編程基礎中學習的內容,我們知道客戶端和服務器的通信過程如下:
- 完善代碼
服務器端:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h>#define MYPORT 165315void main(){ int serverfd, clientfd; struct sockaddr_in my_addr; struct sockaddr_in remote_addr;char buffer[BUFSIZ]; memset(&my_addr, 0, sizeof(my_addr)); my_addr.sin_family=AF_INET; my_addr.sin_addr.s_addr=INADDR_ANY; my_addr.sin_port=htons(MYPORT);if((serverfd=socket(PF_INET, SOCK_STREAM, 0))==-1){ perror("socket"); }if(bind(serverfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))<0){ perror("bind"); } listen(serverfd, 5); int addrlen=sizeof(struct sockaddr_in); while(1){ if((clientfd=accept(serverfd, (struct sockaddr *)&remote_addr, &addrlen))<0){ perror("accept"); } printf("accept client %s\n", inet_ntoa(remote_addr.sin_addr)); int len, i; long wordscount=0; int flag=1; while(1){ if((len=recv(clientfd, buffer, 1024, 0))>0){ for(i=0; i<len; i++){ if(flag==0){ switch(buffer[i]){ case ' ': wordscount++; break; case '\n': wordscount++; break; case '\r': wordscount++; break; default: break; } } if(buffer[i]== ' ' || buffer[i]=='\n' || buffer[i]=='\r') flag=1; else flag=0; } } if(len<1024) break; } send(clientfd, &wordscount, sizeof(long), 0); close(clientfd); } close(serverfd); }客戶端:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h>#define MYPORT 165315void main(){ int clientfd; struct sockaddr_in remote_addr; char buffer[BUFSIZ]; memset(&remote_addr, 0 , sizeof(remote_addr)); remote_addr.sin_family=AF_INET; remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1"); remote_addr.sin_port=htons(MYPORT);if((clientfd=socket(PF_INET,SOCK_STREAM,0))<0){ perror("socket"); }if(connect(clientfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr))<0){ perror("connect"); }int len; FILE *fp; char path[20]; gets(path); fp=fopen(path, "r"); char readch; int i=0; while((readch=fgetc(fp))!=EOF){ if(i<1024){ buffer[i]=readch; i++; } else{ i=0; int n=send(clientfd, buffer, 1024, 0); } } fclose(fp); if(i!=0) send(clientfd, buffer, i, 0); long wordscount; recv(clientfd, &wordscount, sizeof(long), 0); printf("%ld\n", wordscount); close(clientfd); }運行結果
客戶端:
服務器端:
任務二 多線程實現wc功能
使用多線程實現wc服務器并使用同步互斥機制保證計數正確,對比單線程版本的性能,并分析原因
實驗步驟
- 客戶端代碼不需要改變,只需要改變服務器代碼
- 服務器代碼需要增加兩個功能
- 增加多線程
- 使用同步互斥
- 代碼實現
服務器端:
#include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <pthread.h> #pragma comment(lib,"ws2_32.lib") pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER;#define MY_PORT 165315typedef struct socket_counter {SOCKET send_socket;char buffer[1024]; }socket_counter;void my_wc(socket_counter *my_counter); int main() {SOCKET listen_socket;//聲明Socket接口變量struct sockaddr_in my_addr;//聲明Socket地址變量int dummy,rev_length;//char buffer[1024];char file_name[100];pthread_t t;socket_counter *my_counter;WSADATA wsaDate;WSAStartup(MAKEWORD(1,1),&wsaDate);//啟動版本listen_socket=socket(AF_INET,SOCK_STREAM,0);//聲明為IPV4的TCPmy_addr.sin_family=AF_INET;//IPV4my_addr.sin_port=htons(MY_PORT);//端口my_addr.sin_addr.s_addr=htonl(INADDR_ANY);//可以連接任一電腦dummy = sizeof(SOCKADDR);memset(file_name,0,sizeof(file_name));if( bind(listen_socket,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))<0)//綁定本地IP與Socket{printf("Server Bind error!\n");}if(listen(listen_socket,5)<0)//等待序列,默認隊列最多為5個請求{printf("Server Listen error!\n");}while(1){my_counter->send_socket=accept(listen_socket,NULL,&dummy);//listen函數創建等待隊列后,accept函數處理客戶端發來的連接請求if(my_counter->send_socket==-1){printf("Server Accept failed!\n");break;}printf("Accept success!\n");pthread_create(&t, NULL, &my_wc,my_counter);pthread_join(&t, NULL);}closesocket(listen_socket);//依次關閉連接WSACleanup();//清除return 0; } void my_wc(socket_counter *my_counter){pthread_mutex_lock( &counter_mutex );int len, i;long wordscount=0;int flag=1;while(1){if((len=recv(my_counter->send_socket, my_counter->buffer, 1024, 0))>0){for(i=0; i<len; i++){if(flag==0){switch(my_counter->buffer[i]){case ' ':wordscount++;break;case '\n':wordscount++;break;case '\r':wordscount++;break;default:break;}}if(my_counter->buffer[i]== ' ' || my_counter->buffer[i]=='\n' || my_counter->buffer[i]=='\r')flag=1;elseflag=0;}}if(len<1024) break;}send(my_counter->send_socket, &wordscount, sizeof(long), 0);close(my_counter->send_socket);pthread_mutex_unlock( &counter_mutex ); }客戶端:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <pthread.h> #pragma comment(lib,"ws2_32.lib")#define MY_PORT 165315 #define DEST_IP "127.0.0.1" int main() {SOCKET con_socket;struct sockaddr_in remote_addr;char buffer[1024];char file_name[100];char readch;int i;WSADATA wsaDate;WSAStartup(MAKEWORD(1,1),&wsaDate);memset(file_name,0,sizeof(file_name));memset(buffer,0,sizeof(buffer));remote_addr.sin_family=AF_INET;remote_addr.sin_port=htons(MY_PORT);remote_addr.sin_addr.s_addr=inet_addr(DEST_IP);con_socket=socket(AF_INET,SOCK_STREAM,0);if(con_socket==-1){printf("Client socket failed!");exit(0);}printf("Please Input File Name:\n");scanf("%s", file_name);if(connect(con_socket,(struct sockaddr*)&remote_addr,sizeof(struct sockaddr))==-1){printf("Client connet failed!");}else{FILE *fp=fopen(file_name,"r");if(fp==NULL){printf("%s File not Found!\n",file_name);}else{while((readch=fgetc(fp))!=EOF){if(i<1024){buffer[i]=readch;i++;}else{i=0;int n=send(con_socket, buffer, 1024, 0);if(n==-1){printf("Send File Error!\n");}}}fclose(fp);printf("File:%s Transfer Finished!\n", file_name);}fclose(fp);long wordscount;recv(con_socket, &wordscount, sizeof(long), 0);printf("%ld\n", wordscount);}closesocket(con_socket);WSACleanup();return 0; }實驗分析
相比單線程,多線程運行時可以同時多個客戶端一起給服務器傳文件效率更高
實驗結果
實驗中出現的問題
- 客戶端始終無法與服務器端連接上
解決過程:
經過反復調試代碼排查,發現竟然是客戶端和服務器端的端口號設置的不同導致的(客戶端設置的是165315,服務器端設置的是5315),這樣的小錯誤耗費了大半個小時的時間,實在是非常不值得...
- 編譯過程沒有問題,但是在在運行過程中出現段錯誤(Segmentation Fault)
解決過程:
經過上網查詢,在一篇博客中找到了錯誤的原因:指針使用錯誤
實驗感想
本次實驗重點在實現Linux下wc指令的功能,即統計文本單詞的數量,并分別用單線程和多線程、客戶端和服務器端之間的傳輸實現,有一定難度,結合了網絡安全編程基礎的知識,這讓我更加感受到了這學期課程之間的融會貫通。在同組同學的相互幫助之下,借鑒了網路安全編程基礎的代碼,終于成功實現,也是有一定成就感的。
但是在實驗過程中,也犯了大大小小很多錯誤,導致差點想放棄,但是在同組同學的鼓勵下,我們還是堅持到了最后,堅持就是勝利!
轉載于:https://www.cnblogs.com/yh666/p/9976044.html
總結
以上是生活随笔為你收集整理的2018-2019-1 20165315 实验三 实时系统的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java知识博客网站(一些配置和学习的记
- 下一篇: group by 练习