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. 【UVA–11997 K Smallest Sums 】

    ·哦,这题要用优先队列?那大米饼就扔一个手写堆上去吧! ·英文题,述大意:       输入n个长度为n的序列(题中是k,2<=k<=750).一种结果定义为:从每个序列中都要挑选一个数加 ...

  2. hdu5666 BestCoder Round #80

    Segment  Accepts: 418  Submissions: 2020  Time Limit: 2000/1000 MS (Java/Others)  Memory Limit: 6553 ...

  3. hihiocoder 1255(搜索)(2015ACM/ICPC北京站)

    题意: 给你四个矩形,判断能否从中选出3个组成一个矩形 思路: 1.搜索,如果两个能组成一个新的,则将他们合并,继续搜索 2.暴力判断 最开始没注意到3,一直以为要用4个,WR #include &l ...

  4. ssh远程登陆

    ssh远程登录命令简单实例   ssh命令用于远程登录上Linux主机.   常用格式:ssh [-l login_name] [-p port] [user@]hostname 更详细的可以用ssh ...

  5. struts2 可以用ognl拿到值而不可以用el拿到值的解决方法

    错误debug后 得到了There is no read method for container的错误 于是我new了一个实体类 package com.unity; public class St ...

  6. 59. Spiral Matrix II(中等,同54题)

    Given an integer \(n\), generate a square matrix filled with elements from 1 to \(n^2\) in spiral or ...

  7. 百度音乐flac 下载

    破解百度音乐白金会员 为了给Play Music上传标签齐全的正版的mp3,本屌翻遍网络苦思冥想.现在终于被我找到了破解之法——破解百度白金会员!破解之后可以直接从百度下载正版授权的320k文件,更可 ...

  8. sourcestress 问题解决方案

    描述:在Windows系统下,在保证GitHub上的账号和密码正确的情况下,在push时候,输入正确的账号和密码后,却是提醒无效的账户密码. 解决方法:在C:\Users\...\AppData\Lo ...

  9. ACM 继续畅通工程

    Problem Description 省政府"畅通工程"的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可).现得到城镇道路统计 ...

  10. Python实现爬取需要登录的网站完整示例

    from selenium import webdriver dirver = webdriver.Firefox() dirver.get('https://music.douban.com/') ...