下面程式功能為 server 端可以同時接收多個 client 連上來要求檔案,利用 thread 功能達成『同時、多個』的要求!
使用方法為先執行 server 程式,不要關閉,server 會讀取同一個資料夾底下的 TEST.MP3 這個檔案
然後在同一台機器上面執行 client 並且給予 argv 參數作為存檔的檔名
如果沒有給檔名的話,預設會用 GET.MP3 作為存檔名稱
/* * Name : server.c * Author : Wen chi-ching * Date : 2009/10/15 * Send file with multi thread */ #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_THREADS 10 void *thread_function(void *); int sockfd, new_fd[MAX_THREADS], res, times=0, i, num=0, *p_num; socklen_t sin_size; struct sockaddr_in my_addr; struct sockaddr_in their_addr; struct stat filestat; pthread_t accept_thread[MAX_THREADS]; void *thread_result; int main(){ p_num = # //Get file stat if ( lstat("TEST.MP3", &filestat) < 0){ exit(1); } printf("The file size is %lun", filestat.st_size); //TCP socket if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){ perror("socket"); exit(1); } //Initail, bind to port 2324 my_addr.sin_family = AF_INET; my_addr.sin_port = htons(2324); my_addr.sin_addr.s_addr = htonl(INADDR_ANY); bzero( &(my_addr.sin_zero), 8 ); //binding if ( bind(sockfd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) == -1 ){ perror("bind"); exit(1); } while(1){ //Start listening if ( listen(sockfd, 10) == -1 ){ perror("listen"); exit(1); } printf("nWait for connectn"); //Connect if ( (new_fd[num] = accept(sockfd, (struct sockaddr*)&their_addr, &sin_size)) == -1 ){ perror("accept"); exit(1); } //Create thread res = pthread_create( &(accept_thread[times]), NULL, thread_function, (void *)p_num ); if (res != 0){ perror("Thread create failed!n"); exit(EXIT_FAILURE); } num++; times++; } close(sockfd); return 0; } void *thread_function(void *arg){ int numbytes=0, th_num=0, *p_th_num; char buf[1000000]; FILE *fp; p_th_num = (int *)arg; th_num = *p_th_num; fp = fopen("TEST.MP3", "rb"); //Sending file while(!feof(fp)){ numbytes = fread(buf, sizeof(char), sizeof(buf), fp); printf("thread# is %d : fread %d bytes, ", th_num, numbytes); numbytes = write(new_fd[th_num], buf, numbytes); printf("Sending %d bytesn",numbytes); } printf("Sending file Finished!n"); fclose(fp); close(new_fd[th_num]); return NULL; }
server 程式的重點在於 ,因為要同時給多個 client 下載,所以要開 thread ,但是是什麼時間點要開呢?
bind 的時候?listen 的時候?accept 的時候?
其實搞不好都可以。
我選擇在 accept 之後開 thread,並且把 accept 回傳的檔案描述子放到一個陣列去,再把這個陣列的 index 值傳給開出來的 thread ,又,因為 while 裡面有 num++ 的動作,所以每次開 thread 出來的 accept 檔案描述子不會重複,如此也才不會造成 server 傳檔案給 client 的時候檔案指標寫入錯誤的檔案描述子。
整理一下重點,免得自己以後不知道在做啥…
1.accept 的回傳值放到一個 int 陣列去
2.pthread_create 的第四個參數 p_num 存放 num 的址,轉型為 void 是函數的參數型態規定
/* * Name : client.c * Author : Wen chi-ching * Date : 2009/10/14 * Recieve file */ #include #include #include #include #include #include #include #include #include #include #include int main(int argc, char* argv[]){ int sockfd, numbytes; char buf[1000000],filename[10]; struct sockaddr_in address; FILE *fp; //TCP socket if ( ( sockfd = socket(AF_INET, SOCK_STREAM, 0) ) == -1 ){ perror("socket"); exit(1); } //Initial, connect to port 2323 address.sin_family = AF_INET; address.sin_port = htons(2324); address.sin_addr.s_addr = inet_addr("127.0.0.1"); bzero( &(address.sin_zero), 8 ); //Connect to server if ( connect(sockfd, (struct sockaddr*)&address, sizeof(struct sockaddr)) == -1){ perror("connect"); exit(1); } //Open file if(argc > 1){ strncpy(filename, argv[1], sizeof(filename) ); } else{ strncpy(filename, "GET.MP3", sizeof("GET.MP3")); } if ( (fp = fopen(filename, "wb")) == NULL){ perror("fopen"); exit(1); } //Receive file from server while(1){ numbytes = read(sockfd, buf, sizeof(buf)); printf("read %d bytes, ", numbytes); if(numbytes == 0){ printf("n"); break; } numbytes = fwrite(buf, sizeof(char), numbytes, fp); printf("fwrite %d bytesn", numbytes); sleep(1); } fclose(fp); close(sockfd); return 0; }
Makefile
all : client.c server.c gcc -Wall -g -o client client.c gcc -Wall -g -o server server.c -lpthread clean : rm client rm server
加入了 #include <arpa/inet.h> 在這兩個程式裡面,解決「implicit declaration of function ‘inet_addr’」這個 warning
int sin_size 改為 socklen_t sin_size;,解決「pointer targets in passing argument 3 of ‘accept’ differ in signedness」這個 warning
2010/07/07
參考:
Linux c socket,client,server transfer file傳送檔案
Linux c socket,client,server.透過網路傳送文字訊息
server 中有個小 bug ,若 thread_function 在 write 前 , sleep 3 則 main
thread 的 *p_mnu 的 vaule 已被 num++, 但 thread_function new_fd[th_num] 已被改變其值 , 指到下一個 socket (註:使用同一指標 p_num,原因所致)