实验五多线程并发服务器编程.docx

上传人:b****5 文档编号:12127736 上传时间:2023-04-17 格式:DOCX 页数:16 大小:31.51KB
下载 相关 举报
实验五多线程并发服务器编程.docx_第1页
第1页 / 共16页
实验五多线程并发服务器编程.docx_第2页
第2页 / 共16页
实验五多线程并发服务器编程.docx_第3页
第3页 / 共16页
实验五多线程并发服务器编程.docx_第4页
第4页 / 共16页
实验五多线程并发服务器编程.docx_第5页
第5页 / 共16页
点击查看更多>>
下载资源
资源描述

实验五多线程并发服务器编程.docx

《实验五多线程并发服务器编程.docx》由会员分享,可在线阅读,更多相关《实验五多线程并发服务器编程.docx(16页珍藏版)》请在冰豆网上搜索。

实验五多线程并发服务器编程.docx

实验五多线程并发服务器编程

实验五、多线程并发服务器编程

一、实验目的

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,而我们在以前的实验中的编译程序

的方式使用的是标准库函数,不需要特别指定。

因此编译多线程的程序时必须使用-选项,

该选项后面直接跟库文件名称,但要去掉lib和后缀名,即-thread。

注意-l是lib的首字

母。

例如如果需要用到数学函数库libm.so则编译程序时需要使用选项-m。

按以前实验方式编译该程序发现错误,图1中红色部分为错误信息,该信息表明程序未

找到函数pthread_create的实现。

图2为编译正确的结果显示。

VIIdcjllkhL/hoinc/mql

文件编辑虫)终端转到fg)

[i■口cL1ueaILiut」門I]”gcc-vcxaiipIepthieadele.c

pthreadInfunctionivhin';

pihrfad.txanple.c:

12;varniupipassin^arg3nfp(tiread_

jftnp/ccKghiSr.d(.lex1^0x25}:

Lnfuncticiimiin':

undefmedrei'erencelcptkiead_creaie'

Iket2:

~IdreturnedIexitstatusJ

[root^loeaIhostnq叮H|

图1、未使用-lpthread选项错误

文i牛®編幄necv)终需cd转到垃)帮RKtp

[iqotMoca1hai1n^llffgcc一oe^arrplepttiread_exanple

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

**********

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > PPT模板 > 其它模板

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1