網路程式設計——4.利用多程序和多執行緒實現伺服器端的併發處理
阿新 • • 發佈:2018-12-21
一、實驗要求
在TCP檔案傳輸程式碼的基礎上,利用單執行緒程序併發模型和多執行緒併發模型實現伺服器端的併發處理。
二、實驗分析
- 多執行緒與多程序相比,使用多執行緒相比多程序有以下兩個優點:更高的效率和共享儲存器,效率的提高源於上下文切換次數的減少。
- 但是採用多執行緒的方式也有缺點:一個執行緒的動作可能會對同一個程序內的其他執行緒產生影響、並且多執行緒缺乏健壯性。
- 具體使用哪種伺服器還要根據不同情況進行選擇,實驗實現多執行緒和多程序的伺服器。
三、實驗步驟
- 單執行緒程序併發模型:
實驗程式碼:
//tcp_server.c #include <sys/types.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <signal.h> #include <errno.h> /*for errno*/ #define BUFFER_SIZE 1024 #define QLEN 32 void get_filename(char *filepath,char *filename); int process_conn_server(int sd) { ssize_t size = 0; char buffer[BUFFER_SIZE]; FILE *stream; char filepath[100]; strcpy(buffer,"please enter a path!\n"); send(sd,buffer,BUFFER_SIZE,0); int length = 0; memset(filepath,'\0',sizeof(filepath)); length = recv(sd,filepath,100,0); if(length < 0){ printf("recv error!\n"); } else { char filename[100] = {'\0'}; get_filename(filepath,filename); printf("server: filename:%s\n",filename); if( (stream=fopen(filename, "w")) == NULL){ printf("server:open file error!\n"); return; } while(1){/*讀取檔案並寫入檔案流*/ size = recv(sd, buffer, BUFFER_SIZE,0); printf("server:size:%d\n",size); if(size <= 0){ break; } int write_len=fwrite(buffer, sizeof(char), size, stream); } printf("recv finished!\n"); fclose(stream); } return 0; } int main(int argc, char *argv[]){ char *service = "8888"; struct sockaddr_in client; //the address of a client unsigned int addr_len; // the length of client's address int msock; //master server socket int ssock; //slave server socket switch(argc) { case 1: break; case 2: service = argv[1]; break; default: errexit("usage: TCPfiletransfer [port]\n"); } msock = passiveTCP(service, QLEN); printf("waiting...\n"); /*顯示核是sigchld訊號*/ //(void) signal(SIGCHLD, reaper); if(signal(SIGCHLD, SIG_IGN) == SIG_ERR){ perror("signal error"); return EXIT_SUCCESS; } while(1){ socklen_t addr_len = sizeof(struct sockaddr); if( (ssock = accept(msock, (struct sockaddr*)&client, &addr_len)) < 0) { if(errno == EINTR) continue; errexit("accept: %s\n", strerror(errno)); } printf("server:accept\n"); switch(fork()){ case 0: /*child*/ close(msock);/*在子程序中關閉伺服器的監聽*/ exit(process_conn_server(ssock)); default: /*parent*/ close(ssock); break; case -1: errexit("fork: %s\n", strerror(errno)); } } return 0; }
//passiveTCP.c
int passivesock(const char *service, const char *transport,
int qlen);
int passiveTCP(const char *service, int qlen)
{
return passivesock(service, "tcp", qlen);
}
//passivesock,c //created by gregory #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <string.h> #include <stdlib.h> #include <errno.h> int errexit(const char *format, ...); unsigned short portbase = 0; int passivesock(const char *service, const char *transport, int qlen) { struct hostent *phe; struct servent *pse; struct protoent *ppe; struct sockaddr_in sin; int s, type; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; /*map port*/ if ( pse = getservbyname(service, transport) ) sin.sin_port = htons(ntohs((unsigned short)pse->s_port) + portbase); else if ( (sin.sin_port = htons((unsigned short)atoi(service))) == 0) errexit("can't get \"%s\" service entry\n", service); /*map protocol*/ if ( (ppe = getprotobyname(transport)) == 0) errexit("can't get \"%s\" protocol entry\n", transport); /*use protocol to chose a socket type*/ if(strcmp(transport,"udp") == 0) type = SOCK_DGRAM; else type = SOCK_STREAM; /*allocate a socket*/ s = socket(PF_INET, type, ppe->p_proto); if (s < 0) errexit("can't create socket: %s\n", strerror(errno)); /*加入此程式碼是為了避免再次開啟伺服器程式出現bind error的錯誤*/ int on = 1; int ret = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); /*bind the socket*/ if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) errexit("can't bind to %s port: %s\n", service, strerror(errno)); if(type == SOCK_STREAM && listen(s, qlen) < 0) errexit("can't listen on %s port: %s\n", service, strerror(errno)); return s; }
//errexit.c
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
int errexit(const char *format, ...)
{
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
exit(1);
}
//tcp_client.c #include <sys/types.h> #include <string.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <errno.h> /*for errno*/ #define BUFFER_SIZE 1024 void process_conn_client(int s) { ssize_t size = 0; char buffer[BUFFER_SIZE]; FILE *stream; int length = 0; char filepath[100] = {'\0'}; size = read(s, buffer, BUFFER_SIZE); printf("%s",buffer); scanf("%s",filepath); write(s,filepath,100); if( (stream = fopen(filepath,"r")) == NULL) { printf("client:open file error!\n"); return; } printf("sending!\n"); while(1){ size = fread(buffer,sizeof(char),BUFFER_SIZE,stream); if(size <= 0){ break; } write(s,buffer,size); } printf("send finished!\n"); fclose(stream); } int main(int argc, char* argv[]){ char *host = "localhost"; char *service = "8888"; switch(argc) { case 1: host = "localhost"; break; case 3: service = argv[2]; case 2: host = argv[1]; break; default: fprintf(stderr, "usage: TCPfiletransfer [ host [port]]\n"); exit(1); } int socket = connectTCP(host,service); process_conn_client(socket); close(socket); }
編譯:
開啟伺服器端:
開啟客戶端:
2.多執行緒併發模型:
伺服器程式碼:
//tcp_server_threads.c
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <sys/wait.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h> /*for errno*/
#include <time.h>
#define BUFFER_SIZE 1024
#define QLEN 32
void get_filename(char *filepath,char *filename);
int process_conn_server(int sd)
{
ssize_t size = 0;
char buffer[BUFFER_SIZE];
FILE *stream;
char filepath[100];
strcpy(buffer,"please enter a path!\n");
send(sd,buffer,BUFFER_SIZE,0);
int length = 0;
memset(filepath,'\0',sizeof(filepath));
length = recv(sd,filepath,100,0);
if(length < 0){
printf("recv error!\n");
}
else
{
time_t start,end;
time(&start);
char filename[100] = {'\0'};
get_filename(filepath,filename);
printf("server: filename:%s\n",filename);
if( (stream=fopen(filename, "w")) == NULL){
printf("server:open file error!\n");
return;
}
while(1){/*讀取檔案並寫入檔案流*/
size = recv(sd, buffer, BUFFER_SIZE,0);
if(size <= 0){
break;
}
int write_len=fwrite(buffer, sizeof(char), size, stream);
}
time(&end);
int use_time = end - start;
printf("recv finished!\n");
printf("time:%d\n",use_time);
fclose(stream);
}
return 0;
}
int main(int argc, char *argv[]){
char *service = "8888";
struct sockaddr_in client; //the address of a client
unsigned int addr_len; // the length of client's address
int msock; //master server socket
int ssock; //slave server socket
switch(argc)
{
case 1:
break;
case 2:
service = argv[1];
break;
default:
errexit("usage: TCPfiletransfer [port]\n");
}
msock = passiveTCP(service, QLEN);
printf("waiting...\n");
/*顯示核是sigchld訊號*/
//(void) signal(SIGCHLD, reaper);
if(signal(SIGCHLD, SIG_IGN) == SIG_ERR){
perror("signal error");
return EXIT_SUCCESS;
}
pthread_t th;
pthread_attr_t ta;
pthread_attr_init(&ta);
pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
while(1)
{
addr_len = sizeof(client);
if( (ssock = accept(msock, (struct sockaddr*)&client, &addr_len)) < 0)
{
if(errno == EINTR)
continue;
errexit("accept: %s\n", strerror(errno));
}
if(pthread_create(&th, &ta, (void * (*)(void *))process_conn_server,(void *)ssock) < 0)
{
errexit("pthread_create: %s\n", strerror(errno));;
}
}
return 0;
}
執行伺服器:
執行客戶端: