- 实验目的
熟练掌握服务器端并发处理的方法。
- 实验要求
在TCP文件传输代码的基础上,利用多进程和多线程实现服务器端的并发处理。
- 实验步骤
1.利用多进程实现服务器端的并发处理
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <memory.h>
#include <stdlib.h> //for malloc
#include<arpa/inet.h>
#include<sys/wait.h>
#include<signal.h>
#define MAXLINE 4096
#define BUFFER_SIZE 1024
void recvfilename(int temp)
{
char filename[100];
char filepath[100];
memset(filename,'\0',sizeof(filename));
memset(filepath,'\0',sizeof(filepath));
char *buffer;//file buffer
buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE);
bzero(buffer,BUFFER_SIZE);
int lenfilepath;
int fileTrans;
lenfilepath = recv(temp,filepath,100,0);
printf("filepath :%s\n",filepath);
FILE *fp;
int writelength;
if(lenfilepath<0)
{
printf("recv error!\n");
}
//从路径中提取出文件名
else
{
int i=0,k=0;
for(i=strlen(filepath); i>=0; i--)
{
if(filepath[i]!='/')
{
k++;
}
else
break;
}
strcpy(filename,filepath+(strlen(filepath)-k)+1);
}
printf("filename :%s\n",filename);
//以写的方式打开文件
fp = fopen(filename,"w");
if(fp!=NULL)
{
//接受文件内容buffer,存在buffer中
while(fileTrans =recv(temp,buffer,BUFFER_SIZE,0))
{
if(fileTrans<0)
{
printf("recv error!\n");
break;
}
//把buffter中的文件内容写到fp指针指的文件中
writelength = fwrite(buffer,sizeof(char),fileTrans,fp);
if(writelength <fileTrans)
{
printf("write error!\n");
break;
}
bzero(buffer,BUFFER_SIZE);
//memset(buffer,0,sizeof(buffer));
}
printf("recv finished!\n");
fclose(fp);
}
exit(0);
}
int main()
{
int temp;//accept
struct sockaddr_in client;
socklen_t addrlen;
int sockfd;
//创建套接字
sockfd = passiveTCP("6666", 10);
//(void) signal(SIGCHLD,reaper);
addrlen = sizeof(client);
while(1)
{
temp = accept(sockfd,(struct sockaddr*)&client,&addrlen);
if(temp <= 0)
{
printf("accept error!\n");
close(temp);
}
else
{
printf("client IP: %s\n",inet_ntoa(client.sin_addr));
}
switch(fork())
{
case 0: /*child*/
(void) close(sockfd);
recvfilename(temp);
default: /*parent*/
(void) close(temp);
break;
case -1:
printf("fork error");
}
//接受文件路径,返回文件路径的长度
// recvfilename(temp);
}
return 0;
}
在windows下写了个aa.txt文本内容是hello
运行windows下客户端代码 输入相对路径下的aa.txt文本文件 发现发送成功
在linux下服务器
2.一个进程多个线程实现
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <errno.h>
#include <memory.h>
#include <stdlib.h> //for malloc
#include<arpa/inet.h>
#include<pthread.h>
#define MAXLINE 4096
#define BUFFER_SIZE 1024
void recvfilename(int temp)
{
char filename[100];
char filepath[100];
memset(filename,'\0',sizeof(filename));
memset(filepath,'\0',sizeof(filepath));
char *buffer;//file buffer
buffer = (char *)malloc(sizeof(char)*BUFFER_SIZE);
bzero(buffer,BUFFER_SIZE);
int lenfilepath;
int fileTrans;
lenfilepath = recv(temp,filepath,100,0);
printf("filepath :%s\n",filepath);
FILE *fp;
int writelength;
if(lenfilepath<0)
{
printf("recv error!\n");
}
//从路径中提取出文件名
else
{
int i=0,k=0;
for(i=strlen(filepath); i>=0; i--)
{
if(filepath[i]!='/')
{
k++;
}
else
break;
}
strcpy(filename,filepath+(strlen(filepath)-k)+1);
}
printf("filename :%s\n",filename);
//以写的方式打开文件
fp = fopen(filename,"w");
if(fp!=NULL)
{
//接受文件内容buffer,存在buffer中
while(fileTrans =recv(temp,buffer,BUFFER_SIZE,0))
{
if(fileTrans<0)
{
printf("recv error!\n");
break;
}
//把buffter中的文件内容写到fp指针指的文件中
writelength = fwrite(buffer,sizeof(char),fileTrans,fp);
if(writelength <fileTrans)
{
printf("write error!\n");
break;
}
bzero(buffer,BUFFER_SIZE);
//memset(buffer,0,sizeof(buffer));
}
printf("recv finished!\n");
fclose(fp);
}
}
int main()
{
int temp;//accept
struct sockaddr_in client;
socklen_t addrlen;
int sockfd;
//线程
pthread_t th;
pthread_attr_t ta;
pthread_attr_init(&ta);
pthread_attr_setdetachstate(&ta, PTHREAD_CREATE_DETACHED);
//创建套接字
sockfd = passiveTCP("6666", 10);
//if(pthread_create(&th, &ta, (void* (*)(void*))prstats, 0) < 0)
//errexit("pthread_create(prstats): %s\n", strerror(errno));
addrlen = sizeof(client);
while(1)
{
temp = accept(sockfd,(struct sockaddr*)&client,&addrlen);
if(temp <= 0)
{
printf("accept error!\n");
close(temp);
}
else
{
printf("client IP: %s\n",inet_ntoa(client.sin_addr));
}
pthread_create(&th, &ta, (void *(*)(void *))recvfilename, (void *)temp);
//errexit("pthread_create: %s\n", strerror(errno));
//接受文件路径,返回文件路径的长度
// recvfilename(temp);
}
return 0;
}
多进程实现服务器简单的调用了fork函数,在每次连接到达时创建一个新的进程去处理,主进程不处理传输过程,只是接受连接。如果并发服务器用一个进程多个线程来实现的话,优点相较单线程多进程来说上下文开销更小,但缺点是一个线程的动作可能会对其他线程产生影响