20165310 實驗三-併發程式
20165310 實驗三-併發程式
任務一:學習使用Linux命令wc(1)
wc(1)
- 命令的作用:統計指定檔案中的位元組數、字數、行數,並將統計結果顯示輸出。該命令統計指定檔案中的位元組數、字數、行數。如果沒有給出檔名,則從標準輸入讀取。wc同時也給出所指定檔案的總統計數。
- man方法查詢結果:
利用wc命令統計文件text1.txt和text2.txt的字數,並且作為接下來實驗正確性的判斷標準
任務二:基於Linux Socket程式設計實現wc(1)伺服器和客戶端
實驗要求
- 客戶端傳一個文字檔案給伺服器
- 伺服器返加文字檔案中的單詞數
實驗程式
客戶端:
#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 165310 #define DEST_IP "172.20.10.4" 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; }
伺服器:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #define MYPORT 165310 void main() { int server_socket, client_socket; 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((server_socket=socket(PF_INET, SOCK_STREAM, 0))==-1) { perror("socket"); } if(bind(server_socket, (struct sockaddr *)&my_addr, sizeof(struct sockaddr))<0) { perror("bind"); } listen(server_socket, 5); int addrlen=sizeof(struct sockaddr_in); while(1) { if((client_socket =accept(server_socket, (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(client_socket, 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(client_socket, &wordscount, sizeof(long), 0); close(client_socket); } close(server_socket); }
實驗結果截圖
任務三:多執行緒實現wc
實驗要求
使用多執行緒實現wc伺服器並使用同步互斥機制保證計數正確
實驗程式
客戶端:
#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 165310 #define DEST_IP "172.20.10.4" 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; }
伺服器:
#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") pthread_mutex_t counter_mutex = PTHREAD_MUTEX_INITIALIZER; #define MY_PORT 165310 typedef 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的TCP my_addr.sin_family=AF_INET;//IPV4 my_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; else flag=0; } } if(len<1024) break; } send(my_counter->send_socket, &wordscount, sizeof(long), 0); close(my_counter->send_socket); pthread_mutex_unlock( &counter_mutex ); }
執行結果截圖
C語言多執行緒函式
多執行緒變數宣告:
pthread_t <變數名>
建立執行緒的函式
pthread_create
: 函式宣告:
int pthread_create(pthread_t *tidp,const
pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg);
函式說明:類Unix作業系統(Unix、Linux、Mac OS X等)的建立執行緒的函式。它的功能是建立執行緒(實際上就是確定呼叫該執行緒函式的入口點),線上程建立以後,就開始執行相關的執行緒函式。表示成功,返回0;表示出錯,返回表示-1。
引數說明:第一個引數為指向執行緒識別符號的指標。第二個引數用來設定執行緒屬性。第三個引數是執行緒執行函式的起始地址。最後一個引數是執行函式的引數。
執行緒回收函式
pthread_join
: 函式宣告:
int pthread_join(pthread_t thread, void **retval);
函式說明:以阻塞的方式等待thread指定的執行緒結束。當函式返回時,被等待執行緒的資源被收回。如果執行緒已經結束,那麼該函式會立即返回。 0代表成功。 失敗,返回的則是錯誤號。
引數說明:第一個引數為執行緒識別符號,即執行緒ID,標識唯一執行緒。第二個引數為 使用者定義的指標,用來儲存被等待執行緒的返回值。
互斥鎖函式
pthread_mutex_lock
: 函式宣告:
int pthread_mutex_lock(pthread_mutex_t *mutex);
函式說明:執行緒呼叫該函式讓互斥鎖上鎖,如果該互斥鎖已被另一個執行緒鎖定和擁有,則呼叫該執行緒將阻塞,直到該互斥鎖變為可用為止。 成功完成之後會返回零。其他任何返回值都表示出現了錯誤。
遇到問題與解決方法
pthread_create
函式傳參只能傳一個值,然而程式中需要傳入兩個值解決方法:利用C語言結構體,傳入結構體作為唯一引數
計算出單詞個數不正確,少於正確值
解決方法:只計算了空格,沒有計算'\n'、'\r'等特殊字元