多线程并发服务器编程

ゝ一纸荒年。 2022-08-05 12:16 359阅读 0赞

多线程并发服务器编程

一、实验目的

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

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

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

二、实验平台

ubuntu-8.04操作系统

三、实验内容

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

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

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

四、实验原理

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

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

1**pthread_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包括:优先级、初始栈大小、是否应该是守护线程等等。线程的执行函数和调用参数分别是:func和arg。

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

常见的返回错误值:

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

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

EINVAL:无效的属性attr值。

2**pthread_join()函数**

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







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

intpthread_join(pthread_t tid, void **status);

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

—————————————————————————————————-

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

3**pthread_detach()函数**

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







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

intpthread_detach(pthread_t tid)

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

—————————————————————————————————-

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

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

4**pthread_self()函数**

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







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

pthread_tpthread_self(void);

返回:调用线程的线程id

—————————————————————————————————-

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

pthread_detach(pthread_self());

5**pthread_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.c和mproc_client.c所在目录。

4、执行命令gcc–omthread_servermthread_server.c-lpthread生成可执行文件mthread_server。

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

6、打开第2个“终端”,执行命令进入mthread_server.c和mproc_client.c所在目录。

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

8、打开第3个“终端”,执行命令进入mthread_server.c和mproc_client.c所在目录。

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

10、程序运行结果如下:

服务器端:

0_1323654475PWYZ.gif

客户**1**:

0_1323654504W0sM.gif

客户**2**:

0_1323654522K88A.gif

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

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

六、参考程序(mthread_server.c

[cpp] view plain copy

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

发表评论

表情:
评论列表 (有 0 条评论,359人围观)

还没有评论,来说两句吧...

相关阅读

    相关 [并发编程专题]线

    程是执行线程的缩写。程序员可以将他的工作拆分到线程中,这些线程同时运行并共享同一内存上下文。 多线程应该在多处理器或者多核机器上执行,将每个CPU核上并行化每个线程执...

    相关 Java线并发编程

    Java线程:概念与原理 一、操作系统中线程和进程的概念 > 现在的操作系统是多任务操作系统。多线程是实现多任务的一种方式。 > > 进程是指一个内存中运行的应用程序,每

    相关 Java 线 并发编程

    一、多线程 1、操作系统有两个容易混淆的概念,进程和线程。 进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地址空

    相关 Java 线 并发编程 整理

    一、多线程 1、操作系统有两个容易混淆的概念,进程和线程。 进程:一个计算机程序的运行实例,包含了需要执行的指令;有自己的独立地址空间,包含程序内容和数据;不同进程的地