实验五多线程并发服务器编程Word文件下载.docx
《实验五多线程并发服务器编程Word文件下载.docx》由会员分享,可在线阅读,更多相关《实验五多线程并发服务器编程Word文件下载.docx(14页珍藏版)》请在冰豆网上搜索。
*******************************************************************/
#include<
stdio.h>
pthread.h>
//创建多线程
voidchildThread();
intmain()
{
inti=0;
pthread_tid;
pthread_create(&
id,NULL,childThread,NULL);
printf("
点击回车键结束运行\n"
);
getchar();
}
voidchildThread()
inti;
for(i=0;
;
)
{
childthreadsleep%d\n"
i+1);
sleep
(1);
}
编译链接多线程程序需要用到特殊的库文件libthread.so,而我们在以前的实验中的编译链接程序的方式使用的是标准库函数,不需要特别指定。
因此编译链接多线程的程序时必须使用–l选项,该选项后面直接跟库文件名称,但要去掉lib和后缀名,即–lthread。
注意-l是lib的首字母。
例如如果需要用到数学函数库libm.so则编译链接程序时需要使用选项–lm。
按以前实验方式编译该程序发现错误,图1中红色部分为错误信息,该信息表明链接程序未找到函数pthread_create的实现。
图2为编译正确的结果显示。
图1、未使用-lpthread选项链接错误
图2、正确使用-lpthread选项编译链接结果
2、多线程并发服务器编程
类似于多进程服务器编程,本实验的多线程并发服务器也分为两部分程序:
服务器程序和客户端程序。
其中客户端部分和实验一中的客户端部分是一致的,在本实验中我们作了简化;
请注意本实验中的客户端只是为了测试并发服务器的功能,它们本身并不属于多线程并发服务器的内容。
服务器部分实现了多线程的功能,父线程不断地(for循环)等待客户端的连接,一旦有客户端连接服务器,服务器则创建(pthread_create)一个子线程用于该客户端的接收数据处理。
在验证结果阶段可以同时启动多个客户端,注意服务器只需要启动一次。
显而易见的是随着客户端数量的增加,服务器子线程的数量也将线性增加,这必将加重服务器硬件资源(内存)的消耗,最终可导致服务器硬件资源耗尽而崩溃。
但与实验六中多进程并发服务器相比,对同等数量的多线程和多进程程序而言,多线程程序对内存的需求明显低于多进程程序。
本次实验中的客户端程序作了更多的简化,采用单文件方式。
客户端部分未用到多线程,其编译链接方法与以前的实验一致。
服务器部分仍然采用多文件方式,因此在编译运行时要用到多个C文件,注意编译时要加上–lthread选项。
分析、运行下列代码,建议由两位同学分别运行服务器和客户端,然后再转换角色分析程序。
1)客户端程序源码
/******************************************************************************
文件名:
TCPSimpleClient.c
描述:
一个简单的TCP客户端例子,发送helloworld给服务器,并接收服务器返回
******************************************************************************/
stdlib.h>
string.h>
unistd.h>
sys/types.h>
sys/socket.h>
netdb.h>
intmain(intargc,char*argv[])
if(argc!
=3)
使用方式必须为:
$>
命令服务器地址服务器端口号\n"
return1;
char*ip=argv[1];
char*port=argv[2];
intcs=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(cs<
0)
创建套接字失败\n"
return-1;
structsockaddr_inserv;
memset(&
serv,0,sizeof(serv));
serv.sin_port=htons(atoi(port));
inet_pton(AF_INET,ip,&
serv.sin_addr.s_addr);
serv.sin_family=AF_INET;
printf("
开始与服务器建立连接!
\n"
if(connect(cs,(structsockaddr*)&
serv,sizeof(serv))<
0)
连接失败!
连接成功!
charbuf[100]="
helloworld"
send(cs,buf,strlen(buf),0);
memset(buf,0,sizeof(buf));
recv(cs,buf,sizeof(buf)-1,0);
服务器返回信息:
%s\n"
buf);
close(cs);
return0;
2)服务器源码
服务器源码涉及四个C文件和一个头文件,各文件内容如下:
/*************************************************************************
Practical.h
***************************************************************************/
#ifndefPRACTICAL_H_
#definePRACTICAL_H_
stdbool.h>
voidDieWithUserMessage(constchar*msg,constchar*detail);
voidDieWithSystemMessage(constchar*msg);
voidPrintSocketAddress(conststructsockaddr*address,FILE*stream);
boolSockAddrsEqual(conststructsockaddr*addr1,conststructsockaddr*addr2);
intSetupTCPServerSocket(constchar*service);
intAcceptTCPConnection(intservSock);
voidHandleTCPClient(intclntSocket);
intSetupTCPClientSocket(constchar*server,constchar*service);
enumsizeConstants{
MAXSTRINGLENGTH=128,
BUFSIZE=512,
};
#endif//PRACTICAL_H_
/****************************************************************************
msg.c
*****************************************************************************/
voidDieWithUserMessage(constchar*msg,constchar*detail){
fputs(msg,stderr);
fputs("
:
"
stderr);
fputs(detail,stderr);
fputc('
\n'
exit
(1);
voidDieWithSystemMessage(constchar*msg){
perror(msg);
TCPServerUtility.c
arpa/inet.h>
#include"
Practical.h"
staticconstintMAXPENDING=5;
//常量不是宏定义
intSetupTCPServerSocket(constchar*service)
structaddrinfoaddrCriteria;
//Criteriaforaddressmatch
memset(&
addrCriteria,0,sizeof(addrCriteria));
//Zerooutstructure
addrCriteria.ai_family=AF_UNSPEC;
//Anyaddressfamily
addrCriteria.ai_flags=AI_PASSIVE;
//Acceptonanyaddress/port
addrCriteria.ai_socktype=SOCK_STREAM;
//Onlystreamsockets
addrCriteria.ai_protocol=IPPROTO_TCP;
//OnlyTCPprotocol
structaddrinfo*servAddr;
//Listofserveraddresses
intrtnVal=getaddrinfo(NULL,service,&
addrCriteria,&
servAddr);
if(rtnVal!
=0)
DieWithUserMessage("
getaddrinfo()failed"
gai_strerror(rtnVal));
intservSock=-1;
structaddrinfo*addr;
for(addr=servAddr;
addr!
=NULL;
addr=addr->
ai_next)
{
servSock=socket(addr->
ai_family,addr->
ai_socktype,addr->
ai_protocol);
if(servSock<
continue;
if((bind(servSock,addr->
ai_addr,addr->
ai_addrlen)==0)&
&
(listen(servSock,MAXPENDING)==0))
structsockaddr_storagelocalAddr;
socklen_taddrSize=sizeof(localAddr);
if(getsockname(servSock,(structsockaddr*)&
localAddr,&
addrSize)<
DieWithSystemMessage("
getsockname()failed"
break;
}
close(servSock);
servSock=-1;
freeaddrinfo(servAddr);
returnservSock;
intAcceptTCPConnection(intservSock)
intclntSock;
/*客户连接返回的套接字*/
structsockaddr_inechoClntAddr;
/*客户地址*/
unsignedintclntLen;
clntLen=sizeof(echoClntAddr);
/*等待客户端连接*/
if((clntSock=accept(servSock,(structsockaddr*)&
echoClntAddr,&
clntLen))<
accept()failed"
Handlingclient%s\n"
inet_ntoa(echoClntAddr.sin_addr));
returnclntSock;
voidHandleTCPClient(intclntSocket)
charbuffer[BUFSIZE];
ssize_tnumBytesRcvd=recv(clntSocket,buffer,BUFSIZE,0);
if(numBytesRcvd<
DieWithSystemMessage("
recv()failed"
while(numBytesRcvd>
0)
//将收到的信息返回给客户端
if(send(clntSocket,buffer,numBytesRcvd,0)<
send()failed"
numBytesRcvd=recv(clntSocket,buffer,BUFSIZE,0);
close(clntSocket);
/*****************************************************************************
AddressUtility.c
voidPrintSocketAddress(conststructsockaddr*address,FILE*stream)
if(address==NULL||stream==NULL)
return;
void*numericAddress;
charaddrBuffer[INET6_ADDRSTRLEN];
in_port_tport;
switch(address->
sa_family)
caseAF_INET:
//IPV4
numericAddress=&
((structsockaddr_in*)address)->
sin_addr;
port=ntohs(((structsockaddr_in*)address)->
sin_port);
break;
caseAF_INET6:
//IPV6
((structsockaddr_in6*)address)->
sin6_addr;
port=ntohs(((structsockaddr_in6*)address)->
sin6_port);
default:
fputs("
[unknowntype]"
stream);
if(inet_ntop(address->
sa_family,numericAddress,addrBuffer,sizeof(addrBuffer))==NULL)
[invalidaddress]"
//Unabletoconvert
else
fprintf(stream,"
%s"
addrBuffer);
//往流中写字符串
if(port!
=0)
fprintf(stream,"
-%u"
port);
//u%表示无符号整数输出
boolSockAddrsEqual(conststructsockaddr*addr1,conststructsockaddr*addr2)
if(addr1==NULL||addr2==NULL)
returnaddr1==addr2;
elseif(addr1->
sa_family!
=addr2->
sa_family)
returnfalse;
sa_family==AF_INET)
structsockaddr_in*ipv4Addr1=(structsockaddr_in*)addr1;
structsockaddr_in*ipv4Addr2=(structsockaddr_in*)addr2;
returnipv4Addr1->
sin_addr.s_addr==ipv4Addr2->
sin_addr.s_addr&
ipv4Addr1->
sin_port==ipv4Addr2->
sin_port;
}
sa_family==AF_INET6)
structsockaddr_in6*ipv6Addr1=(structsockaddr_in6*)addr1;
structsockaddr_in6*ipv6Addr2=(structsockaddr_in6*)addr2;
returnmemcmp(&
ipv6Addr1->
sin6_addr,&
ipv6Addr2->
sin6_addr,sizeof(structin6_addr))==0
&
ipv6Addr1->
sin6_port==ipv6Addr2->
sin6_port;
else
TCPEchoServer-Thread.c
void*ThreadMain(void*arg);
//线程块
structThreadArgs{
intmain(intargc,char*argv[])
if(argc!
=2)
运行命令和参数错误!
"
char*service=argv[1];
unsignedintprocessLimit=atoi(argv[2]);
intservSock=SetupTCPServerSocket