TCP预先创建线程服务器程序,主线程统一accept

最后一个使用线程的服务器程序设计示范是在程序启动阶段创建一个线程池之后只让主线程调用accept并把每个客户连接传递给池中某个可用线程。
            本设计示范的问题在于主线程如何把一个已连接套接字传递给线程池中某个可用线程。这里有多个实现手段。我们原本可以如前使用描述符传递,不过既然所有线程和所有描述符都在同一个进程之内,我们没有必要把一个描述符从一个线程传递到另一个线程。接收线程只需知道这个已连接套接字描述符的值,而描述符传递实际传递的并非这个值,而是对这个套接字的一个引用,因而将返回一个不同于原值的描述符(该套接字的引用计数也被递增)。

typedef struct {
pthread_t thread_tid; /* thread ID */
long thread_count; /* # connections handled */
} Thread;
Thread *tptr; /* array of Thread structures; calloc'ed */ #define MAXNCLI 32
int clifd[MAXNCLI], iget, iput;
pthread_mutex_t clifd_mutex;
pthread_cond_t clifd_cond;

定义存放已连接套接字描述符的共享数组
07-10        我们还定义一个clifd数组,由主线程往中存入已接受的已连接套接字描述符,并由线程池中的可用线程从中取出一个以服务相应的客户。iput是主线程将往该数组中存入的下一个元素的下标,iget是线程池中某个线程将从该数组中取出的下一个元素的下标。这个有所有线程共享的数据结构自然必须得到保护,我们使用互斥锁和条件变量做到这一点。

#include	"unpthread.h"
#include "pthread08.h" static int nthreads;
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER; int
main(int argc, char **argv)
{
int i, listenfd, connfd;
void sig_int(int), thread_make(int);
socklen_t addrlen, clilen;
struct sockaddr *cliaddr; if (argc == 3)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 4)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: serv08 [ <host> ] <port#> <#threads>");
cliaddr = Malloc(addrlen); nthreads = atoi(argv[argc-1]);
tptr = Calloc(nthreads, sizeof(Thread));
iget = iput = 0; /* 4create all the threads */
for (i = 0; i < nthreads; i++)
thread_make(i); /* only main thread returns */ Signal(SIGINT, sig_int); for ( ; ; ) {
clilen = addrlen;
connfd = Accept(listenfd, cliaddr, &clilen); Pthread_mutex_lock(&clifd_mutex);
clifd[iput] = connfd;
if (++iput == MAXNCLI)
iput = 0;
if (iput == iget)
err_quit("iput = iget = %d", iput);
Pthread_cond_signal(&clifd_cond);
Pthread_mutex_unlock(&clifd_mutex);
}
}
void
sig_int(int signo)
{
int i;
void pr_cpu_time(void); pr_cpu_time(); for (i = 0; i < nthreads; i++)
printf("thread %d, %ld connections\n", i, tptr[i].thread_count); exit(0);
}

创建线程池
29-30    使用thread_make创建池中的每个线程。

等待客户连接
34-46    主线程大部分时间阻塞在accept调用中,等待各个客户连接的到达。一旦某个客户连接到达,主线程就把它的已连接套接字描述符存入clifd数组的下一个元素,不过需事先获取保护该数组的互斥锁。主线程还检查iput下标没有赶上iget下标(若赶上则说明该数组不够大),并发送信号到条件变量信号,然后释放互斥锁,以允许线程池中某个线程为这个客户服务。

#include	"unpthread.h"
#include "pthread08.h" void
thread_make(int i)
{
void *thread_main(void *); Pthread_create(&tptr[i].thread_tid, NULL, &thread_main, (void *) i);
return; /* main thread returns */
} void *
thread_main(void *arg)
{
int connfd;
void web_child(int); printf("thread %d starting\n", (int) arg);
for ( ; ; ) {
Pthread_mutex_lock(&clifd_mutex);
while (iget == iput)
Pthread_cond_wait(&clifd_cond, &clifd_mutex);
connfd = clifd[iget]; /* connected socket to service */
if (++iget == MAXNCLI)
iget = 0;
Pthread_mutex_unlock(&clifd_mutex);
tptr[(int) arg].thread_count++; web_child(connfd); /* process request */
Close(connfd);
}
}

等待未之服务的客户描述符
21-31      线程池中每个线程都试图获取保护clifd数组的互斥锁。获得之后就测试iput与iget,若两者相等则无事可做,于是通过调用pthread_cond_wait睡眠在条件变量上。主线程接受一个连接后将待用pthread_cond_signal向条件变量发送信号,以唤醒睡眠在其上的线程。若测得iput与iget不等则从clifd数组中取出下一个元素以获得一个连接,然后调用web_child。

UNIX网络编程——客户/服务器程序设计示范(八)的更多相关文章

  1. UNIX网络编程——客户/服务器程序设计示范(总结)

    (1)当系统负载较轻是,每来一个客户请求现场派生一个子进程为之服务的传统并发服务器程序模型就足够了.这个模型甚至可以与inetd结合使用,也就是inetd处理每个连接的接收.我们的其他意见是就重负荷运 ...

  2. UNIX网络编程——客户/服务器程序设计示范(七)

        TCP预先创建线程服务器程序,每个线程各自accept 前面讨论过预先派生一个子进程池快于为每个客户线程派生一个子进程.在支持线程的系统上,我们有理由预期在服务器启动阶段预先创建一个线程池以取 ...

  3. UNIX网络编程——客户/服务器程序设计示范(六)

    TCP并发服务器程序,每个客户一个线程 前面讲述了,每个客户一个进程的服务器,或为每个客户现场fork一个子进程,或者预先派生一定数目的子进程.如果服务器主机支持线程,我们就可以改用线程以取代子进程. ...

  4. UNIX网络编程——客户/服务器程序设计示范(五)

        TCP预先派生子进程服务器程序,传递描述符 对预先派生子进程服务器程序的最后一个修改版本是只让父进程调用accept,然后把所接受的已连接套接字"传递"给某个子进程.这么做 ...

  5. UNIX网络编程——客户/服务器程序设计示范(三)

    TCP预先派生子进程服务器程序,accept无上锁保护 我们的第一个"增强"型服务器程序使用称为预先派生子进程的技术.使用该技术的服务器不像传统意义的并发服务器那样为每个客户现场派 ...

  6. UNIX网络编程——客户/服务器程序设计示范(二)

        TCP并发服务器程序,每个客户一个子进程 传统上并发服务器调用fork派生一个子进程来处理每个客户.这使得服务器能够同时为多个客户服务,每个进程一个客户.客户数目的唯一限制是操作系统对以其名义 ...

  7. UNIX网络编程——客户/服务器程序设计示范(一)

    下面给出的是客户程序用于测试我们的服务器程序的各个变体. #include "unp.h" #define MAXN 16384 /* max # bytes to request ...

  8. UNIX网络编程——客户/服务器程序设计示范(四)

        TCP预先派生子进程服务器程序,accept使用线程上锁保护 我们使用线程上锁保护accept,因为这种方法不仅适用于同一进程内各线程之间的上锁,而且适用于不同进程之间的上锁.        ...

  9. UNIX网络编程——客户/服务器心搏函数

    阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...

随机推荐

  1. [poj1279]Art Gallery

    题意:求多边形的核的面积. 敲一下半平面交模板........  然后我wa了一早上就因为写了%lf  不知道poj什么破机制还不能用lf的,真的想跳楼 #include<iostream> ...

  2. Windows下使用notepad++对文本进行行列转换

    行转列: Ctrl + F  选择替换 查找目标:填写指定的内容 替换为:\r\n 查找模式:正则表达式 单击替换或全部替换按钮 列转行: Ctrl + F  选择替换 查找目标:\r\n 替换为:不 ...

  3. python MySQLdb pymsql

    参考文档 https://www.python.org/dev/peps/pep-0249/#nextset 本节内容 MySQLdb  pymysql MySQLdb和pymysql分别为Pytho ...

  4. Java Servlet 笔记4

    Servlet 客户端 HTTP 请求 当浏览器请求网页时,它会向 Web 服务器发送特定信息,这些信息不能被直接读取,因为这些信息是作为 HTTP 请求的头的一部分进行传输的. 读取 HTTP 头的 ...

  5. Python中模块之shutil及zipfile&tarfile的功能介绍

    shutil的功能介绍及其他打包.压缩模块 1. shutil模块的方法 chown 更改指定路径的属组 2. copy 拷贝文件和权限 方法:shutil.copy(src,dst,*,follow ...

  6. dva-quickstart 与 create-react-app 比较(一)

    最近在学习 React ,  现对 dva-quickstart   与  create-react-app 比较 1. 安装, 两个都需要安装工具包:npm install -g create-re ...

  7. Linux学习之CentOS(四)----Linux文件属性、所有者、群组、其他组及文件权限操作简要总结

    Linux文件属性.所有者.群组.其他组及文件权限操作简要总结 首先介绍一个重要的知识点:文件属性控制权限 [root@www ~]# ls -al total 156 drwxr-x--- 4 ro ...

  8. oracle循环插入1万条数据

    declare maxnumber constant number:=10000; i number :=1; begin for i in 1..maxnumber loop insert into ...

  9. 安装Leanote极客范的云笔记

    前言 在这个互联网知识呈爆炸增长的时代,作为一个程序员要掌握的知识越来越多,然再好的记性也不如烂笔头,有了笔记我们就是可以时常扒拉扒拉以前的知识,顺便可以整理下自己的知识体系. 如今市面上云笔记产品, ...

  10. 用burpsuite暴力破解后台

    [实验原理] Burp Suite是Web应用程序测试的最佳工具之一,其多种功能执行各种任务.请求的拦截和修改,扫描web应用程序漏洞,以暴力破解登录表单,执行会话令牌等多种的随机性检查. Burp ...