实验五、多线程并发服务器编程_第1页
实验五、多线程并发服务器编程_第2页
实验五、多线程并发服务器编程_第3页
实验五、多线程并发服务器编程_第4页
实验五、多线程并发服务器编程_第5页
已阅读5页,还剩7页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、实验五、多线程并发服务器编程一、实验目的1、学习Linux操作系统的多线程的基本概念以及进程与线程的区别;2、掌握编写多线程程序的一般方法;3、熟悉多线程并发服务器的设计思路,以及多线程程序的编译链接方法。二、实验内容线程(thread)技术早在60年代就被提出,但真正应用多线程到操作系统中去,是在80年代中期,solaris是这方面的佼佼者。现在多线程技术已经被许多操作系统所支持,包括Windows/NT以及Unix/Linux。 为什么有了进程的概念后,还要再引入线程呢?使用多线程到底有哪些好处?什么的系统应该选用多线程?使用多线程的理由之一是和进程相比,它是一种非常节俭的多任务操作方式。

2、在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这是一种昂贵的多任务工作方式。而运行于一个进程中的多个线程,它们彼此之间使用相同的地址空间,共享大部分数据,启动一个线程所花费的空间远远小于启动一个进程所花费的空间,而且线程间彼此切换所需的时间也远远小于进程间切换所需要的时间。据统计一个进程的开销大约是一个线程开销的30倍左右。 使用多线程的理由之二是线程间方便的通信机制。对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。线程则不然,由于同一进程下的线程之间共享数据空间

3、,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。 1、编写一个最简单的多线程程序请仔细运行、分析下列程序,指出多进程和多线程如何区分?何谓父线程和子线程?当父线程终止会导致子线程发生何种情况?/*文件名:pthread_example.c演示了pthread_create函数创建子线程的使用 */#include #include /创建多线程void childThread();int ma

4、in()int i = 0; pthread_t id; pthread_create(&id,NULL,childThread,NULL); printf(点击回车键结束运行n); getchar();void childThread()int i; for(i=0;)printf(child thread sleep %dn,i+1); sleep(1);编译链接多线程程序需要用到特殊的库文件libthread.so,而我们在以前的实验中的编译链接程序的方式使用的是标准库函数,不需要特别指定。因此编译链接多线程的程序时必须使用 l 选项,该选项后面直接跟库文件名称,但要去掉lib和后缀名,

5、即 lthread 。注意-l是lib的首字母。例如如果需要用到数学函数库 libm.so则编译链接程序时需要使用选项 lm 。按以前实验方式编译该程序发现错误,图1中红色部分为错误信息,该信息表明链接程序未找到函数pthread_create的实现。图2为编译正确的结果显示。图1、未使用-lpthread选项链接错误图2、正确使用-lpthread选项编译链接结果2、多线程并发服务器编程类似于多进程服务器编程,本实验的多线程并发服务器也分为两部分程序:服务器程序和客户端程序。其中客户端部分和实验一中的客户端部分是一致的,在本实验中我们作了简化;请注意本实验中的客户端只是为了测试并发服务器的功

6、能,它们本身并不属于多线程并发服务器的内容。服务器部分实现了多线程的功能,父线程不断地(for循环)等待客户端的连接,一旦有客户端连接服务器,服务器则创建(pthread_create)一个子线程用于该客户端的接收数据处理。在验证结果阶段可以同时启动多个客户端,注意服务器只需要启动一次。显而易见的是随着客户端数量的增加,服务器子线程的数量也将线性增加,这必将加重服务器硬件资源(内存)的消耗,最终可导致服务器硬件资源耗尽而崩溃。但与实验六中多进程并发服务器相比,对同等数量的多线程和多进程程序而言,多线程程序对内存的需求明显低于多进程程序。本次实验中的客户端程序作了更多的简化,采用单文件方式。客户

7、端部分未用到多线程,其编译链接方法与以前的实验一致。服务器部分仍然采用多文件方式,因此在编译运行时要用到多个C文件,注意编译时要加上 lthread选项。分析、运行下列代码,建议由两位同学分别运行服务器和客户端,然后再转换角色分析程序。1)客户端程序源码/*文件名:TCPSimpleClient.c描述:一个简单的TCP客户端例子,发送hello world给服务器,并接收服务器返回*/#include #include #include #include #include #include #include int main(int argc, char *argv) if (argc !=

8、 3)printf(使用方式必须为:$命令 服务器地址 服务器端口号n); return 1; char *ip = argv1; char *port = argv2;int cs = socket ( AF_INET, SOCK_STREAM, IPPROTO_TCP); if (cs 0)printf(创建套接字失败n); return -1;struct sockaddr_in serv;memset (&serv, 0, sizeof (serv) ); serv.sin_port = htons(atoi(port); inet_pton(AF_INET,ip, &serv.sin

9、_addr.s_addr);serv.sin_family = AF_INET; printf(开始与服务器建立连接!n); if(connect (cs, (struct sockaddr *)&serv, sizeof(serv)0)printf(连接失败!n);return -1; printf(连接成功!n); char buf100 = hello world; send(cs, buf, strlen(buf), 0); memset(buf,0,sizeof(buf);recv(cs,buf,sizeof(buf)-1,0);printf(服务器返回信息:%sn,buf);clo

10、se(cs);return 0;2)服务器源码服务器源码涉及四个C文件和一个头文件,各文件内容如下:/* 文件名:Practical.h*/#ifndef PRACTICAL_H_#define PRACTICAL_H_#include #include #include void DieWithUserMessage(const char *msg, const char *detail);void DieWithSystemMessage(const char *msg);void PrintSocketAddress(const struct sockaddr *address, FIL

11、E *stream);bool SockAddrsEqual(const struct sockaddr *addr1, const struct sockaddr *addr2);int SetupTCPServerSocket(const char *service);int AcceptTCPConnection(int servSock);void HandleTCPClient(int clntSocket);int SetupTCPClientSocket(const char *server, const char *service);enum sizeConstants MAX

12、STRINGLENGTH = 128, BUFSIZE = 512,;#endif / PRACTICAL_H_/* 文件名:msg.c*/#include #include void DieWithUserMessage(const char *msg, const char *detail) fputs(msg, stderr); fputs(: , stderr); fputs(detail, stderr); fputc(n, stderr); exit(1);void DieWithSystemMessage(const char *msg) perror(msg); exit(1)

13、;/* 文件名:TCPServerUtility.c*/#include #include #include #include #include #include Practical.hstatic const int MAXPENDING = 5; /常量不是宏定义int SetupTCPServerSocket(const char *service) struct addrinfo addrCriteria; / Criteria for address match memset(&addrCriteria, 0, sizeof(addrCriteria); / Zero out str

14、ucture addrCriteria.ai_family = AF_UNSPEC; / Any address family addrCriteria.ai_flags = AI_PASSIVE; / Accept on any address/port addrCriteria.ai_socktype = SOCK_STREAM; / Only stream sockets addrCriteria.ai_protocol = IPPROTO_TCP; / Only TCP protocolstruct addrinfo *servAddr; / List of server addres

15、ses int rtnVal = getaddrinfo(NULL, service, &addrCriteria, &servAddr); if (rtnVal != 0) DieWithUserMessage(getaddrinfo() failed, gai_strerror(rtnVal);int servSock = -1; struct addrinfo *addr; for (addr = servAddr; addr != NULL; addr = addr-ai_next) servSock = socket(addr-ai_family, addr-ai_socktype,

16、addr-ai_protocol); if (servSock ai_addr, addr-ai_addrlen) = 0) &(listen(servSock, MAXPENDING) = 0) struct sockaddr_storage localAddr; socklen_t addrSize = sizeof(localAddr); if (getsockname(servSock, (struct sockaddr *) &localAddr, &addrSize) 0) DieWithSystemMessage(getsockname() failed); break; clo

17、se(servSock); servSock = -1; freeaddrinfo(servAddr);return servSock;int AcceptTCPConnection(int servSock) int clntSock; /* 客户连接返回的套接字 */ struct sockaddr_in echoClntAddr; /* 客户地址*/ unsigned int clntLen; clntLen = sizeof(echoClntAddr); /* 等待客户端连接 */ if (clntSock = accept(servSock, (struct sockaddr *)

18、&echoClntAddr,&clntLen) 0) printf(accept() failed); exit(1); printf(Handling client %sn, inet_ntoa(echoClntAddr.sin_addr); return clntSock;void HandleTCPClient(int clntSocket) char bufferBUFSIZE; ssize_t numBytesRcvd = recv(clntSocket, buffer, BUFSIZE, 0); if (numBytesRcvd 0) /将收到的信息返回给客户端 if(send(c

19、lntSocket, 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 void PrintSocketAddress(const struc

20、t sockaddr *address, FILE *stream) if (address = NULL | stream = NULL) return; void *numericAddress; char addrBufferINET6_ADDRSTRLEN; in_port_t port; switch (address-sa_family) case AF_INET: /IPV4 numericAddress = &(struct sockaddr_in *) address)-sin_addr; port = ntohs(struct sockaddr_in *) address)

21、-sin_port); break; case AF_INET6: /IPV6 numericAddress = &(struct sockaddr_in6 *) address)-sin6_addr; port = ntohs(struct sockaddr_in6 *) address)-sin6_port); break; default: fputs(unknown type, stream); return; if (inet_ntop(address-sa_family, numericAddress, addrBuffer, sizeof(addrBuffer) = NULL)f

22、puts(invalid address, stream); / Unable to convert else fprintf(stream, %s, addrBuffer); /往流中写字符串 if (port != 0) fprintf(stream, -%u, port); /u%表示无符号整数输出 bool SockAddrsEqual(const struct sockaddr *addr1, const struct sockaddr *addr2) if (addr1 = NULL | addr2 = NULL) return addr1 = addr2; else if (ad

23、dr1-sa_family != addr2-sa_family) return false; else if (addr1-sa_family = AF_INET) struct sockaddr_in *ipv4Addr1 = (struct sockaddr_in *) addr1; struct sockaddr_in *ipv4Addr2 = (struct sockaddr_in *) addr2; return ipv4Addr1-sin_addr.s_addr = ipv4Addr2-sin_addr.s_addr & ipv4Addr1-sin_port = ipv4Addr

24、2-sin_port; else if (addr1-sa_family = AF_INET6) struct sockaddr_in6 *ipv6Addr1 = (struct sockaddr_in6 *) addr1; struct sockaddr_in6 *ipv6Addr2 = (struct sockaddr_in6 *) addr2; return memcmp(&ipv6Addr1-sin6_addr, &ipv6Addr2-sin6_addr,sizeof(struct in6_addr) = 0 & ipv6Addr1-sin6_port = ipv6Addr2-sin6_port; else return false;/* 文件名:TCPEchoServer-Thread.c*/#include #include #include #include Practical.hvoid *ThreadMain(void *arg); /线程块struct ThreadArgs int clntSock;int main(int argc, char *argv)if(argc != 2) DieWithSystemMessage(运行命令和参数错误!); char *service = argv1; unsigned in

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论