多线程并发服务器编程

多线程并发服务器编程

一、实验目的

理解线程的创建和终止方法;

学会编写基本的多线程并发服务器程序和客户程序;

理解多线程与多进程的区别。

二、实验平台

ubuntu-8.04操作系统

三、实验内容

编写多线程并发服务器程序和客户程序,具体功能如下:

1、服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。

2、客户首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,继续等待用户输入字符串,直到用户输入Ctrl+D,客户关闭连接并退出。

四、实验原理

多进程方式使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制;其次,fork子进程后,需要用进程间通信在父进程和子进程间传递信息,从子进程返回信息给父进程需要做较多的工作。多线程有助于解决以上两个问题。

线程是进程内的独立执行实体和调度单元,又称为轻量级进程(lightwightprocess);创建线程比进程快10~100倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题)。


1pthread_create()函数

pthread_create()函数用于创建新线程。当一个程序开始运行时,系统产生一个称为初始线程或主线程的单个线程,额外的线程需要由pthread_create()函数创建。

-------------------------------------------------------------------
#include<pthread.h>

intpthread_create(pthread_t *tid, const pthread_attr_t *attr, void*(*func)(void *), void *arg);

返回:成功时为0;出错时非0

-------------------------------------------------------------------

新线程由线程id标识:tid,新线程的属性attr包括:优先级、初始栈大小、是否应该是守护线程等等。线程的执行函数和调用参数分别是:funcarg

由于线程的执行函数的参数和返回值类型均为void*,因此可传递和返回指向任何类型的指针。

常见的返回错误值:

EAGAIN:超过了系统线程数目的限制。

ENOMEN:没有足够的内存产生新的线程。

EINVAL:无效的属性attr值。


2pthread_join()函数

pthread_join()函数用于等待一个线程终止。

-------------------------------------------------------------------
#inlcude<pthread.h>

intpthread_join(pthread_t tid, void **status);

返回:成功时为0;出错时返回正的错误码。

-------------------------------------------------------------------

该函数类似与waitpid函数,但必须指定等待线程的ID(由参数tid指定),该函数不能等待任意一个线程结束。


3pthread_detach()函数

pthread_detach()函数将指定的线程变成脱离的。

-------------------------------------------------------------------
#include<pthread.h>

intpthread_detach(pthread_t tid)

返回:成功时为0;出错时返回错误码。

-------------------------------------------------------------------

线程或者是可汇合的(joinable)(默认),或者是脱离的(detached)。当可汇合的线程终止时,其线程id和退出状态将保留,直到另外一个线程调用pthread_join。脱离的线程则像守护进程,当它终止时,释放所有资源,我们不能等待它终止。

参数tid指定要设置为脱离的线程ID

4pthread_self()函数

每一个线程都有一个IDpthread_self()函数返回自己的线程ID

-------------------------------------------------------------------
#include<pthread.h>

pthread_tpthread_self(void);

返回:调用线程的线程id

-------------------------------------------------------------------

线程可以通过如下语句将自己设置为可脱离的:

pthread_detachpthread_self());

5pthread_exit()函数

pthread_exit()函数用于终止当前线程,并返回状态值。如果当前线程是可汇合的,将保留线程id和退出状态供pthread_join()函数调用。

-------------------------------------------------------------------
#include<pthread.h>

void pthread_exit(void*status);

无返回值;

-------------------------------------------------------------------

指针status:指向线程的退出状态。不能指向一个局部变量,因为线程终止时其所有的局部变量将被撤销。

还有其他两种方法可使线程终止:

1)启动线程的函数pthread_create()的第3个参数返回。其返回值便是线程的终止状态;

2)如果进程的main()函数返回,或者当前进程中,任一线程调用了exit()函数,将终止该进程中所有线程。


五、实验步骤

1、登陆进入ubuntu操作系统,新建一个文件,命名为mthread_server.c

2、在mthread_server.c中编写相应代码并保存,作为服务器端程序。客户端程序代码同mproc_client.c一致。blog:http://blog.csdn.net/yueguanghaidao/article/details/7060350

3、打开一个终端,执行命令进入mthread_server.cmproc_client.c所在目录。

4、执行命令gccomthread_servermthread_server.c-lpthread生成可执行文件mthread_server

5、执行命令./mthread_server,运行服务器端。

6、打开第2终端,执行命令进入mthread_server.cmproc_client.c所在目录。

7、执行命令./mproc_client127.0.0.1,模拟客户1

8、打开第3终端,执行命令进入mthread_server.cmproc_client.c所在目录。

9、执行命令./mproc_client127.0.0.1,模拟客户2

10、程序运行结果如下:


服务器端:


客户1



客户2


11、在客户端按下Ctrl+D,关闭客户连接。

12、认真分析源代码,体会多线程并发服务器程序的编写。

六、参考程序(mthread_server.c

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <unistd.h>  
  5. #include <sys/types.h>  
  6. #include <sys/socket.h>  
  7. #include <netinet/in.h>  
  8. #include <arpa/inet.h>  
  9. #include <pthread.h>  
  10.   
  11. #define PORT 1234  
  12. #define BACKLOG 5  
  13. #define MAXDATASIZE 1000  
  14.   
  15. void process_cli(int connfd, struct sockaddr_in client);  
  16. void *function(void* arg);  
  17. struct ARG {  
  18. int connfd;  
  19. struct sockaddr_in client;  
  20. };  
  21.   
  22. main()  
  23. {  
  24. int listenfd,connfd;  
  25. pthread_t  tid;  
  26. struct ARG *arg;  
  27. struct sockaddr_in server;  
  28. struct sockaddr_in client;  
  29. socklen_t  len;  
  30.   
  31. if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {  
  32. perror("Creatingsocket failed.");  
  33. exit(1);  
  34. }  
  35.   
  36. int opt =SO_REUSEADDR;  
  37. setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
  38.   
  39. bzero(&server,sizeof(server));  
  40. server.sin_family=AF_INET;  
  41. server.sin_port=htons(PORT);  
  42. server.sin_addr.s_addr= htonl (INADDR_ANY);  
  43. if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {  
  44. perror("Bind()error.");  
  45. exit(1);  
  46. }  
  47.   
  48. if(listen(listenfd,BACKLOG)== -1){  
  49. perror("listen()error\n");  
  50. exit(1);  
  51. }  
  52.   
  53. len=sizeof(client);  
  54. while(1)  
  55. {  
  56. if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {  
  57. perror("accept() error\n");  
  58. exit(1);  
  59. }  
  60. arg = (struct ARG *)malloc(sizeof(struct ARG));  
  61. arg->connfd =connfd;  
  62. memcpy((void*)&arg->client, &client, sizeof(client));  
  63.   
  64. if(pthread_create(&tid, NULL, function, (void*)arg)) {  
  65. perror("Pthread_create() error");  
  66. exit(1);  
  67. }  
  68. }  
  69. close(listenfd);  
  70. }  
  71.   
  72. void process_cli(int connfd, struct sockaddr_in client)  
  73. {  
  74. int num;  
  75. char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];  
  76.   
  77. printf("Yougot a connection from %s. \n ",inet_ntoa(client.sin_addr) );  
  78. num = recv(connfd,cli_name, MAXDATASIZE,0);  
  79. if (num == 0) {  
  80. close(connfd);  
  81. printf("Clientdisconnected.\n");  
  82. return;  
  83. }  
  84. cli_name[num - 1] ='\0';  
  85. printf("Client'sname is %s.\n",cli_name);  
  86.   
  87. while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {  
  88. recvbuf[num] ='\0';  
  89. printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);  
  90. int i;  
  91. for (i = 0; i <num - 1; i++) {  
  92. if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))  
  93. {  
  94. recvbuf[i]=recvbuf[i]+ 3;  
  95. if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))  
  96. recvbuf[i]=recvbuf[i]- 26;  
  97. }  
  98. sendbuf[i] =recvbuf[i];  
  99. }  
  100. sendbuf[num -1] = '\0';  
  101. send(connfd,sendbuf,strlen(sendbuf),0);  
  102. }  
  103. close(connfd);  
  104. }  
  105.   
  106. void *function(void* arg)  
  107. {  
  108. struct ARG *info;  
  109. info = (struct ARG*)arg;  
  110. process_cli(info->connfd,info->client);  
  111. free (arg);  
  112. pthread_exit(NULL);  
  113. }  

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Qt是一款强大的跨平台开发框架,它提供了丰富的库和工具,方便开发者进行多线程编程。下面我将以一个简单的Qt多线程并发服务器demo作为例子介绍。 首先,我们需要创建一个继承自QObject的服务器类,例如Server。在Server的构造函数,我们可以初始化服务器的一些配置,比如监听的端口号等。 接下来,我们需要为Server类添加一个用于启动服务器的函数,例如startServer()。在该函数,我们可以创建一个QTcpServer对象,并监听指定的端口号。当有客户端连接时,会触发新的连接信号,我们可以通过连接信号的槽函数来处理新的连接请求。 为了支持多线程,我们可以使用Qt的QThread类,将耗时的处理逻辑放在一个独立的线程执行。在槽函数,我们可以创建一个新的QThread对象,并将需要处理的任务包装成一个QObject对象,通过moveToThread()函数将任务对象移动到新线程。 如果需要在不同线程之间进行通信,我们可以使用Qt提供的信号和槽机制。在任务对象,可以定义一些信号和槽函数,用于与其他对象进行数据交换。 在多线程环境下,为了避免资源冲突,我们需要使用互斥锁(QMutex)或者读写锁(QReadWriteLock)来对共享资源进行保护。 最后,记得在主程序创建一个QCoreApplication对象,并运行事件循环,以便接收和处理服务器的事件和信号。 综上所述,以上便是一个简单的Qt多线程并发服务器demo的实现方式。通过合理的使用Qt提供的多线程和信号槽机制,我们可以方便地实现高效并发服务器应用程序。当然,实际的服务器开发还需要考虑更多的细节,如线程管理、异常处理等。 ### 回答2: 在Qt使用多线程实现并发服务器的示例可以通过以下步骤完成: 首先,我们需要创建一个继承自QThread的自定义线程类(比如ServerThread),用于处理客户端连接请求。在这个类,我们需要重写run()函数,在其实现服务器的主逻辑。 在run()函数,我们可以通过调用QTcpServer的listen()函数来监听指定的端口,同时使用connect()函数将QTcpServer的newConnection()信号与一个自定义的槽函数进行绑定。这样,当有新的客户端连接请求时,QTcpServer就会发出newConnection()信号,进而调用我们自定义的槽函数。 在自定义的槽函数,我们可以通过调用QTcpServer的nextPendingConnection()函数获取当前连接的QTcpSocket对象,并将其移交给一个新创建的自定义线程类(比如ClientThread)进行处理。 在ClientThread类,我们同样需要重写run()函数,在其处理每个客户端的请求。可以通过调用QTcpSocket的read()函数来接收客户端发送的数据,并根据需求进行相应的处理。如果需要向客户端发送数据,可以使用QTcpSocket的write()函数实现。 最后,我们需要在主线程创建一个ServerThread对象,并调用其start()函数启动服务器线程。这样,服务器就会开始监听并处理客户端连接请求。 需要注意的是,在进行多线程编程时,需要注意线程安全的问题。比如,对于共享资源的访问需要加锁保护,以避免竞态条件的发生。 这就是一个简单的Qt多线程并发服务器的示例,通过利用多线程的特性,可以实现同时处理多个客户端连接请求,提高服务器并发性能。 ### 回答3: Qt是一个跨平台的应用程序开发框架,提供了丰富的多线程并发编程的支持。在Qt,可以通过使用QThread类来创建多个线程,并通过信号与槽机制来实现线程间的通信。 创建一个Qt多线程并发服务器的demo可以采用以下步骤: 1. 创建一个继承自QThread的自定义线程类,用于处理客户端的请求。在该线程类的run()函数,可以编写处理客户端请求的业务逻辑。 2. 在主线程创建一个QTcpServer对象,用于监听并接收客户端连接请求。 3. 在QTcpServer的newConnection信号的槽函数创建一个新的自定义线程对象,并将新连接客户端套接字传递给该线程对象。 4. 在自定义线程类,通过重写其构造函数,接收并保存客户端套接字。然后可以在run()函数使用该套接字与客户端进行通信。 5. 在run()函数,使用QTcpSocket的waitForReadyRead()函数来等待并接收客户端的请求数据。然后根据具体的业务逻辑进行处理,并通过QTcpSocket的write()函数将处理结果返回给客户端。 6. 在主线程启动QTcpServer对象的监听功能,等待客户端连接。 通过以上步骤,我们可以实现一个基于Qt的多线程并发服务器demo,能够同时处理多个客户端的请求。每个客户端连接都会被分配一个独立的线程来处理,从而实现并发处理。使用Qt的多线程并发编程的支持,能够提高服务器的性能和响应速度,同时也能简化开发和维护的工作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值