实验五多线程并发服务器编程.docx
- 文档编号:28648558
- 上传时间:2023-07-19
- 格式:DOCX
- 页数:16
- 大小:88.95KB
实验五多线程并发服务器编程.docx
《实验五多线程并发服务器编程.docx》由会员分享,可在线阅读,更多相关《实验五多线程并发服务器编程.docx(16页珍藏版)》请在冰豆网上搜索。
实验五多线程并发服务器编程
实验五、多线程并发效劳器编程
一、实验目的
1、学习Linux操作系统的多线程的根本概念以及进程与线程的区别;
2、掌握编写多线程程序的一般方法;
3、熟悉多线程并发效劳器的设计思路,以及多线程程序的编译方法。
二、实验容
线程〔thread〕技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。
现在多线程技术已经被许多操作系统所支持,包括Windows/NT以及Unix/Linux。
为什么有了进程的概念后,还要再引入线程呢?
使用多线程到底有哪些好处?
什么的系统应该选用多线程?
使用多线程的理由之一是和进程相比,它是一种非常"节省"的多任务操作方式。
在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种"昂贵"的多任务工作方式。
而运行于一个进程中的多个线程,它们彼此之间使用一样的地址空间,共享大局部数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。
据统计一个进程的开销大约是一个线程开销的30倍左右。
使用多线程的理由之二是线程间方便的通信机制。
对不同进程来说,它们具有独立的数据空间,要进展数据的传递只能通过通信的方式进展,这种方式不仅费时,而且很不方便。
线程那么不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
1、编写一个最简单的多线程程序
请仔细运行、分析以下程序,指出多进程和多线程如何区分?
何谓父线程和子线程?
当父线程终止会导致子线程发生何种情况?
/*****************************************************************
文件名:
pthread_example.c
演示了pthread_create函数创立子线程的使用
*******************************************************************/
#include
#include
voidchildThread();
intmain()
{
inti=0;
pthread_tid;
pthread_create(&id,NULL,childThread,NULL);
printf("点击回车键完毕运行\n");
getchar();
}
voidchildThread()
{
inti;
for(i=0;;)
{
printf("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给效劳器,并接收效劳器返回
******************************************************************************/
#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_INET,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;//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<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(echoClntAddr.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(clntSocket);
}
/*****************************************************************************
文件名:
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(structin6_addr))==0
&&ipv6Addr1->sin6_port==ipv6Addr2->sin6_port;
}
else
returnfalse;
}
/******************************************************************************
文件名:
TCPEchoServer-Thread.c
******************************************************************************/
#include
#include
#include
#include"Practical.h"
void*ThreadMain(void*arg);//线程块
structThreadArgs{
intclntSock;
};
intmain(intargc,char*argv[])
{
if(argc!
=2)
DieWithSystemMessage("运行命令和参数错误!
");
char*service=argv[1];
unsignedintprocessLimit=atoi(argv[2]);
intservSock=SetupTCPServerSocket(service);
if(servSock<0)
DieWithSystemMessage("创立套接字失败!
");
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验 多线程 并发 服务器 编程
![提示](https://static.bdocx.com/images/bang_tan.gif)