基于线程的高并发服务器

本文介绍了一个使用多线程技术实现的网络服务器程序。该程序通过主线程监听客户端连接请求,并利用线程池处理客户端的数据交换。文章详细展示了如何进行错误处理、资源管理以及线程间的同步协调。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#define _GNU_SOURCE  
#include    <signal.h>  
#include    <pthread.h>  
#include    <unistd.h>  
#include    <string.h>  
#include    <stdio.h>  
#include    <errno.h>  
#include    <strings.h>  
#include    <stdlib.h>  
#include    <sys/socket.h>  
#include    <sys/types.h>  
#include    <netdb.h>  
#include    <syslog.h>  
#include    <stdarg.h>  
#include    <sys/resource.h>  
#define LISTENQ     1024    /* 2nd argument to listen() */  
#define MAXLINE     4096    /* max text line length */  
#define MAXN    16384       /* max # bytes to request from server */  
typedef void Sigfunc(int);  /* for signal handlers */  
int daemon_proc;        /* set nonzero by daemon_init() */  
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)  
{  
    int errno_save, n;  
    char buf[MAXLINE + 1];  

    errno_save = errno; /* value caller might want printed */  
#ifdef  HAVE_VSNPRINTF  
    vsnprintf(buf, MAXLINE, fmt, ap);   /* safe */  
#else  
    vsprintf(buf, fmt, ap); /* not safe */  
#endif  
    n = strlen(buf);  
    if (errnoflag)  
        snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));  
    strcat(buf, "\n");  

    if (daemon_proc) {  
        syslog(level, "%s", buf);  
    } else {  
        fflush(stdout); /* in case stdout and stderr are the same */  
        fputs(buf, stderr);  
        fflush(stderr);  
    }  
    return;  
}  

void err_quit(const char *fmt, ...)  
{  
    va_list ap;  

    va_start(ap, fmt);  
    err_doit(0, LOG_ERR, fmt, ap);  
    va_end(ap);  
    exit(1);  
}  

void err_sys(const char *fmt, ...)  
{  
    va_list ap;  

    va_start(ap, fmt);  
    err_doit(1, LOG_ERR, fmt, ap);  
    va_end(ap);  
    exit(1);  
}  

void pr_cpu_time(void)  
{  
    double user, sys;  
    struct rusage myusage, childusage;  

    if (getrusage(RUSAGE_SELF, &myusage) < 0)  
        err_sys("getrusage error");  
    if (getrusage(RUSAGE_CHILDREN, &childusage) < 0)  
        err_sys("getrusage error");  

    user = (double)myusage.ru_utime.tv_sec +  
        myusage.ru_utime.tv_usec / 1000000.0;  
    user += (double)childusage.ru_utime.tv_sec +  
        childusage.ru_utime.tv_usec / 1000000.0;  
    sys = (double)myusage.ru_stime.tv_sec +  
        myusage.ru_stime.tv_usec / 1000000.0;  
    sys += (double)childusage.ru_stime.tv_sec +  
        childusage.ru_stime.tv_usec / 1000000.0;  

    printf("\nuser time = %g, sys time = %g\n", user, sys);  
}  

void Setsockopt(int fd, int level, int optname, const void *optval,  
        socklen_t optlen)  
{  
    if (setsockopt(fd, level, optname, optval, optlen) < 0)  
        err_sys("setsockopt error");  
}  

void Close(int fd)  
{  
    if (close(fd) == -1)  
        err_sys("close error");  
}  

void Listen(int fd, int backlog)  
{  
    char *ptr;  

    /*4can override 2nd argument with environment variable */  
    if ((ptr = getenv("LISTENQ")) != NULL)  
        backlog = atoi(ptr);  

    if (listen(fd, backlog) < 0)  
        err_sys("listen error");  
}  

int tcp_listen(const char *host, const char *serv, socklen_t * addrlenp)  
{  
    int listenfd, n;  
    const int on = 1;  
    struct addrinfo hints, *res, *ressave;  

    bzero(&hints, sizeof(struct addrinfo));  
    hints.ai_flags = AI_PASSIVE;  
    hints.ai_family = AF_UNSPEC;  
    hints.ai_socktype = SOCK_STREAM;  

    if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)  
        err_quit("tcp_listen error for %s, %s: %s",  
             host, serv, gai_strerror(n));  
    ressave = res;  

    do {  
        listenfd =  
            socket(res->ai_family, res->ai_socktype, res->ai_protocol);  
        if (listenfd < 0)  
            continue;   /* error, try next one */  

        Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));  
        if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)  
            break;  /* success */  

        Close(listenfd);    /* bind error, close and try next one */  
    } while ((res = res->ai_next) != NULL);  

    if (res == NULL)    /* errno from final socket() or bind() */  
        err_sys("tcp_listen error for %s, %s", host, serv);  

    Listen(listenfd, LISTENQ);  

    if (addrlenp)  
        *addrlenp = res->ai_addrlen; /* return size of protocol address */  

    freeaddrinfo(ressave);  

    return (listenfd);  
}  

int Tcp_listen(const char *host, const char *serv, socklen_t * addrlenp)  
{  
    return (tcp_listen(host, serv, addrlenp));  
}  

void *Malloc(size_t size)  
{  
    void *ptr;  

    if ((ptr = malloc(size)) == NULL)  
        err_sys("malloc error");  
    return (ptr);  
}  

Sigfunc *signal(int signo, Sigfunc * func)  
{  
    struct sigaction act, oact;  

    act.sa_handler = func;  
    sigemptyset(&act.sa_mask);  
    act.sa_flags = 0;  
    if (signo == SIGALRM) {  
#ifdef  SA_INTERRUPT  
        act.sa_flags |= SA_INTERRUPT;   /* SunOS 4.x */  
#endif  
    } else {  
#ifdef  SA_RESTART  
        act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */  
#endif  
    }  
    if (sigaction(signo, &act, &oact) < 0)  
        return (SIG_ERR);  
    return (oact.sa_handler);  
}  

Sigfunc *Signal(int signo, Sigfunc * func)  
{               /* for our signal() function */  
    Sigfunc *sigfunc;  

    if ((sigfunc = signal(signo, func)) == SIG_ERR)  
        err_sys("signal error");  
    return (sigfunc);  
}  

int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)  
{  
    int n;  

 again:  
    if ((n = accept(fd, sa, salenptr)) < 0) {  
#ifdef  EPROTO  
        if (errno == EPROTO || errno == ECONNABORTED)  
#else  
        if (errno == ECONNABORTED)  
#endif  
            goto again;  
        else  
            err_sys("accept error");  
    }  
    return (n);  
}  

static int read_cnt;  
static char *read_ptr;  
static char read_buf[MAXLINE];  
static ssize_t my_read(int fd, char *ptr)  
{  

    if (read_cnt <= 0) {  
 again:  
        if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {  
            if (errno == EINTR)  
                goto again;  
            return (-1);  
        } else if (read_cnt == 0)  
            return (0);  
        read_ptr = read_buf;  
    }  

    read_cnt--;  
    *ptr = *read_ptr++;  
    return (1);  
}  

ssize_t readline(int fd, void *vptr, int maxlen)  
{  
    ssize_t n, rc;  
    char c, *ptr;  

    ptr = vptr;  
    for (n = 1; n < maxlen; n++) {  
        if ((rc = my_read(fd, &c)) == 1) {  
            *ptr++ = c;  
            if (c == '\n')  
                break;  /* newline is stored, like fgets() */  
        } else if (rc == 0) {  
            *ptr = 0;  
            return (n - 1); /* EOF, n - 1 bytes were read */  
        } else  
            return (-1);    /* error, errno set by read() */  
    }  

    *ptr = 0;       /* null terminate like fgets() */  
    return (n);  
}  

ssize_t Readline(int fd, void *ptr, size_t maxlen)  
{  
    ssize_t n;  

    if ((n = readline(fd, ptr, maxlen)) < 0)  
        err_sys("readline error");  
    return (n);  
}  

ssize_t             /* Write "n" bytes to a descriptor. */  
writen(int fd, const void *vptr, size_t n)  
{  
    size_t nleft;  
    ssize_t nwritten;  
    const char *ptr;  

    ptr = vptr;  
    nleft = n;  
    while (nleft > 0) {  
        if ((nwritten = write(fd, ptr, nleft)) <= 0) {  
            if (nwritten < 0 && errno == EINTR)  
                nwritten = 0;   /* and call write() again */  
            else  
                return (-1);    /* error */  
        }  

        nleft -= nwritten;  
        ptr += nwritten;  
    }  
    return (n);  
}  

void Writen(int fd, void *ptr, int nbytes)  
{  
    if (writen(fd, ptr, nbytes) != nbytes)  
        err_sys("writen error");  
}  

void web_child(int sockfd)  
{  
    int ntowrite;  
    ssize_t nread;  
    char line[MAXLINE], result[MAXN];  

    for (;;) {  
        if ((nread = Readline(sockfd, line, MAXLINE)) == 0)  
            return; /* connection closed by other end */  

        /* 4line from client specifies #bytes to write back */  
        ntowrite = atol(line);  
        if ((ntowrite <= 0) || (ntowrite > MAXN))  
            err_quit("client request for %d bytes", ntowrite);  

        Writen(sockfd, result, ntowrite);  
    }  
}  

void Pthread_detach(pthread_t tid)  
{  
    int n;  

    if ((n = pthread_detach(tid)) == 0)  
        return;  
    errno = n;  
    err_sys("pthread_detach error");  
}  

void Pthread_create(pthread_t * tid, const pthread_attr_t * attr,  
            void *(*func) (void *), void *arg)  
{  
    int n;  

    if ((n = pthread_create(tid, attr, func, arg)) == 0)  
        return;  
    errno = n;  
    err_sys("pthread_create error");  
}  

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;  

void *Calloc(size_t n, size_t size)  
{  
    void *ptr;  

    if ((ptr = calloc(n, size)) == NULL)  
        err_sys("calloc error");  
    return (ptr);  
}  

void Pthread_mutex_lock(pthread_mutex_t * mptr)  
{  
    int n;  

    if ((n = pthread_mutex_lock(mptr)) == 0)  
        return;  
    errno = n;  
    err_sys("pthread_mutex_lock error");  
}  

void Pthread_mutex_unlock(pthread_mutex_t * mptr)  
{  
    int n;  

    if ((n = pthread_mutex_unlock(mptr)) == 0)  
        return;  
    errno = n;  
    err_sys("pthread_mutex_unlock error");  
}  

void Pthread_cond_wait(pthread_cond_t * cptr, pthread_mutex_t * mptr)  
{  
    int n;  

    if ((n = pthread_cond_wait(cptr, mptr)) == 0)  
        return;  
    errno = n;  
    err_sys("pthread_cond_wait error");  
}  
//子线程函数  
void *thread_main(void *arg)  
{  
    int connfd;  

    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);  
    }  
}  
//创建线程池中的每个线程 
void thread_make(int i)  
{  
    Pthread_create(&tptr[i].thread_tid, NULL, &thread_main, (void *)i);  
    return;         /* main thread returns */  
}  

pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;  

static int nthreads;  
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;  
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER;  
//处理ctrl c 信号函数  
void sig_int(int signo)  
{  
    int i;  

    pr_cpu_time();  

    for (i = 0; i < nthreads; i++)  
        printf("thread %d, %ld connections\n", i, tptr[i].thread_count);  

    exit(0);  
}  
//条件变量发送信号函数  
void Pthread_cond_signal(pthread_cond_t * cptr)  
{  
    int n;  

    if ((n = pthread_cond_signal(cptr)) == 0)  
        return;  
    errno = n;  
    err_sys("pthread_cond_signal error");  
}  

int main(int argc, char **argv)  
{  
    int i, listenfd, connfd;  
    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);  
    }  
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值