pitiread_eicaTTplerc:
InfuncIionnaii';
pthread_exarrpIe.c:
12ruling】pansingarg3ofpthr^ad_crealeJfrem
Ippoints!
type
[root^OLo-csIho^trqII*
图2、正确使用-Ipthread选项编译结果
2、多线程并发服务器编程
类似于多进程服务器编程,本实验的多线程并发服务器也分为两部分程序:
服务器程序和客户端程序。
其中客户端部分和实验一中的客户端部分是一致的,在本实验中我们作了简
化;请注意本实验中的客户端只是为了测试并发服务器的功能,它们本身并不属于多线程并
发服务器的容。
服务器部分实现了多线程的功能,父线程不断地(for循环)等待客户端的
连接,一旦有客户端连接服务器,服务器则创建(pthread_create)一个子线程用于该客户
端的接收数据处理。
在验证结果阶段可以同时启动多个客户端,注意服务器只需要启动一次。
显而易见的是随着客户端数量的增加,服务器子线程的数量也将线性增加,这必将加重服务器硬件资源(存)的消耗,最终可导致服务器硬件资源耗尽而崩溃。
但与实验六中多进程并发服务器相比,对同等数量的多线程和多进程程序而言,多线程程序对存的需求明显低
于多进程程序。
本次实验中的客户端程序作了更多的简化,采用单文件方式。
客户端部分未用到多线程,
其编译方法与以前的实验一致。
服务器部分仍然采用多文件方式,因此在编译运行时要用到
多个C文件,注意编译时要加上-thread选项。
分析、运行下列代码,建议由两位同学分别运行服务器和客户端,然后再转换角色分析程序。
1)客户端程序源码
/******************************************************************************
文件名:
TCPSimpleClient.c
描述:
一个简单的TCP客户端例子,发送helloworld给服务器,并接收服务器返回
******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
intmain(intargc,char*argv[])
{
if(argc!
=3)
{
printf("使用方式必须为:
$>命令服务器地址服务器端口号\n");return1;
}
char*ip=argv[1];
char*port=argv[2];
intcs=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(cs<0)
{
printf("创建套接字失败\n”);
return-1;
}
structsockaddr_inserv;
memset(&serv,0,sizeof(serv));
serv.sin_port=htons(atoi(port));
inet_pton(AF」NET,ip,&serv.sin_addr.s_addr);
serv.sin_family=AF_INET;
printf("开始与服务器建立连接!
\n");
if(connect(cs,(structsockaddr*)&serv,sizeof(serv))<0)
{
printf("连接失败!
\n”);
return-1;
}
printf("连接成功!
\n");
charbuf[100]="helloworld";
send(cs,buf,strlen(buf),0);
memset(buf,0,sizeof(buf));
recv(cs,buf,sizeof(buf)-1,0);
printf("服务器返回信息:
%s\n",buf);
close(cs);
return0;
}
2)服务器源码
服务器源码涉及四个C文件和一个头文件,各文件容如下:
/*************************************************************************
文件名:
Practical.h
***************************************************************************/
#ifndefPRACTICAL_H_
#definePRACTICAL_H_
#include
#include
#include
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
*****************************************************************************/
#include
#include
voidDieWithUserMessage(constchar*msg,constchar*detail){
fputs(msg,stderr);
fputs(":
",stderr);
fputs(detail,stderr);
fputc('\n',stderr);
exit
(1);
voidDieWithSystemMessage(constchar*msg){perror(msg);
exit
(1);
}
/******************************************************************************文件名:
TCPServerUtility.c
******************************************************************************/
#include
#include
#include
#include
#include
#include"Practical.h"staticconstintMAXPENDING=5;//常量不是宏定义
intSetupTCPServerSocket(constchar*service){
structaddrinfoaddrCriteria;
memset(&addrCriteria,0,sizeof(addrCriteria));//Zerooutstructure
addrCriteria.ai_family=AF_UNSPEC;addrCriteria.ai_flags=AI_PASSIVE;addrCriteria.ai_socktype=SOCK_STREAM;addrCriteria.ai_protocol=IPPROTO_TCP;
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<0)
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)<0)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))<0){
printf("accept()failed");
exit
(1);
}
printf("Handlingclient%s\n",inet_ntoa(echoCIntAddr.sin_addr));
returnclntSock;
}
voidHandleTCPClient(intclntSocket)
{
charbuffer[BUFSIZE];
ssize_tnumBytesRcvd=recv(clntSocket,buffer,BUFSIZE,0);
if(numBytesRcvd<0)
DieWithSystemMessage("recv()failed");
while(numBytesRcvd>0)
//将收到的信息返回给客户端
if(send(clntSocket,buffer,numBytesRcvd,0)<0)DieWithSystemMessage("send()failed");
numBytesRcvd=recv(clntSocket,buffer,BUFSIZE,0);
if(numBytesRcvd<0)DieWithSystemMessage("recv()failed");
}
close(cIntSocket);
}
/*****************************************************************************
文件名:
AddressUtility.c
******************************************************************************/
#include
#include
#include
#include
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
numericAddress=&((structsockaddr_in6*)address)->sin6_addr;port=ntohs(((structsockaddr_in6*)address)->sin6_port);break;
default:
fputs("[unknowntype]",stream);
return;
if(inet_ntop(address->sa_family,numericAddress,addrBuffer,sizeof(addrBuffer))==
NULL)
fputs("[invalidaddress]",stream);//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;
elseif(addr1->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;
}
elseif(addr1->sa_family==AF_INET6)
{
structsockaddr_in6*ipv6Addr1=(structsockaddr_in6*)addr1;
structsockaddr_in6*ipv6Addr2=(structsockaddr_in6*)addr2;
returnmemcmp(&ipv6Addr1->sin6_addr,&ipv6Addr2->sin6_addr,sizeof(struct
in6_addr))==0
&&ipv6Addr1->sin6_port==ipv6Addr2->sin6_port;
}
else
returnfalse;
}
/******************************************************************************
文件名:
TCPEchoServer-Thread.c
**********