UNIX网络编程——客户/服务器程序设计示范(八)
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网络编程——客户/服务器程序设计示范(八)的更多相关文章
- UNIX网络编程——客户/服务器程序设计示范(总结)
(1)当系统负载较轻是,每来一个客户请求现场派生一个子进程为之服务的传统并发服务器程序模型就足够了.这个模型甚至可以与inetd结合使用,也就是inetd处理每个连接的接收.我们的其他意见是就重负荷运 ...
- UNIX网络编程——客户/服务器程序设计示范(七)
TCP预先创建线程服务器程序,每个线程各自accept 前面讨论过预先派生一个子进程池快于为每个客户线程派生一个子进程.在支持线程的系统上,我们有理由预期在服务器启动阶段预先创建一个线程池以取 ...
- UNIX网络编程——客户/服务器程序设计示范(六)
TCP并发服务器程序,每个客户一个线程 前面讲述了,每个客户一个进程的服务器,或为每个客户现场fork一个子进程,或者预先派生一定数目的子进程.如果服务器主机支持线程,我们就可以改用线程以取代子进程. ...
- UNIX网络编程——客户/服务器程序设计示范(五)
TCP预先派生子进程服务器程序,传递描述符 对预先派生子进程服务器程序的最后一个修改版本是只让父进程调用accept,然后把所接受的已连接套接字"传递"给某个子进程.这么做 ...
- UNIX网络编程——客户/服务器程序设计示范(三)
TCP预先派生子进程服务器程序,accept无上锁保护 我们的第一个"增强"型服务器程序使用称为预先派生子进程的技术.使用该技术的服务器不像传统意义的并发服务器那样为每个客户现场派 ...
- UNIX网络编程——客户/服务器程序设计示范(二)
TCP并发服务器程序,每个客户一个子进程 传统上并发服务器调用fork派生一个子进程来处理每个客户.这使得服务器能够同时为多个客户服务,每个进程一个客户.客户数目的唯一限制是操作系统对以其名义 ...
- UNIX网络编程——客户/服务器程序设计示范(一)
下面给出的是客户程序用于测试我们的服务器程序的各个变体. #include "unp.h" #define MAXN 16384 /* max # bytes to request ...
- UNIX网络编程——客户/服务器程序设计示范(四)
TCP预先派生子进程服务器程序,accept使用线程上锁保护 我们使用线程上锁保护accept,因为这种方法不仅适用于同一进程内各线程之间的上锁,而且适用于不同进程之间的上锁. ...
- UNIX网络编程——客户/服务器心搏函数
阅读此博客时,可以参考以前的博客<<UNIX网络编程--socket的keep-alive>>和<<UNIX网络编程--套接字选项(心跳检测.绑定地址复用)> ...
随机推荐
- bzoj3223Tyvj 1729 文艺平衡树 splay
3223: Tyvj 1729 文艺平衡树 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 5644 Solved: 3362[Submit][Sta ...
- Centos7发送邮件
Centos7发送邮件 $ yum -y install mailx sendmail $ vim /etc/mail.rc set from=xxxxxx@.com set smtp=smtp..c ...
- js时间比较,获取n天后(前)的日期
<html> <head> <meta http-equiv="Content-Type" content="textml; charset ...
- 程序包org.junit不存在
三种解决方法 第一种 maven的改法 <dependency> <groupId>junit</groupId> &l ...
- Head First Java设计模式思维导图总结
关于Head First Java设计模式的思维导图总结:
- 【ASP.NET Core】给中间件传参数的方法
最近博客更新频率慢了些,原因有三: 其一,最近老周每星期六都录 ASP.NET Core 的直播,有些内容在视频里讲过,就不太想在博客里面重复.有兴趣的话可以去老周的微博看,或者去一直播,直播帐号与微 ...
- ftp:connect:未知错误号
Linux下使用ftp命令时,提示:ftp: connect :未知错误号解决方法:service iptables stop或/etc/rc.d/init.d/iptables stop
- python解析json文件之简介
一.JSON简介 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.它基于JavaScript(Standard ECMA-262 3rd Edition ...
- ubuntu16.04下安装opencv
source url:http://blog.csdn.net/zhuiqiuk/article/details/5308505811 1 依赖包sudo apt-get install build- ...
- Mybatis源码分析--关联表查询及延迟加载原理(二)
在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...