Unix环境高级编程之多线程并发服务器开发

1、 线程介绍

在操作系统原理的术语中,线程是进程的一条执行路径。线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,所有的线程都是在同一进程空间运行,这也意味着多条线程将共享该进程中的全部系统资源,如虚拟地址空间,文件描述符和信号处理等等。
但同一进程中的多个线程有各自的调用栈(call stack),自己的寄存器环境 (register context),自己的线程本地存储(thread-local storage)。 一个进程可以有很多线程,每条线程并行执行不同的任务。

2、线程的创建

一个进程创建后,会首先生成一个缺省的线程,通常称这个线程为主线程(或称控制线程),C/C++程序中,主线程就是通过 main函数进入的线程,由主线程调用pthread_create()创建的线程称为子线程,子线程也可以有自己的入口函数,该函数由用户 在创建的时候指定。每个线程都有自己的线程ID,可以通过pthread_self()函数获取。最常见的线程模型中,除主线程较为特殊 之外,其他线程一旦被创建,相互之间就是对等关系,不存在隐含的层次关系。每个进程可创建的最大线程数由具体实现决定。

无论在windows中还是Posix中,主线程和子线程的默认关系是:无论子线程执行完毕与否,一旦主线程执行完毕退出,所有 子线程执行都会终止。这时整个进程结束或僵死,部分线程保持一种终止执行但还未销毁的状态,而进程必须在其所有线程销毁 后销毁,这时进程处于僵死状态。线程函数执行完毕退出,或以其他非常方式终止,线程进入终止态,但是为线程分配的系统资 源不一定释放,可能在系统重启之前,一直都不能释放,终止态的线程,仍旧作为一个线程实体存在于操作系统中,什么时候销 毁,取决于线程属性。在这种情况下,主线程和子线程通常定义以下两种关系:
1. 可会合(joinable):
这种关系下,主线程需要明确执行等待操作,在子线程结束后,主线程的等待操作执行完毕,子线 程和主线程会合,这时主线程继续执行等待操作之后的下一步操作。主线程必须会合可会合的子线程。在主线程的线程函 数内部调用子线程对象的wait函数实现,即使子线程能够在主线程之前执行完毕,进入终止态,也必须执行会合操作,否 则,系统永远不会主动销毁线程,分配给该线程的系统资源也永远不会释放。
2. 相分离(detached):表示子线程无需和主线程会合,也就是相分离的,这种情况下,子线程一旦进入终止状态,这种 方式常用在线程数较多的情况下,有时让主线程逐个等待子线程结束,或者让主线程安排每个子线程结束的等待顺序,是 很困难或不可能的,所以在并发子线程较多的情况下,这种方式也会经常使用。

线程的分离状态决定一个线程以什么样的方式来终止自己,在默认的情况下,线程是非分离状态的,这种情况下,原有的线程 等待创建的线程结束,只有当pthread_join函数返回时,创建的线程才算终止,释放自己占用的系统资源,而分离线程没有被其 他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。

3、多线程服务器编程代码:

#ifndef __SOCKET_SERVER_H //防止重重定义
#define __SOCKET_SERVER_H
#include <sys/types.h>      
#include <sys/socket.h>
#include <string.h>
#include<stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <getopt.h>
#include <pthread.h>
#include <ctype.h>
#define	MSG_STR  "Hello network word! I'm sever!"
#define	BUF_SIZE  1024
#endif

typedef void *(WORKWER_BODY) (void *thread_arg); //定义函数指针
void *thread_worker(void *ctx);
int thread_start(pthread_t * thraed_id, WORKWER_BODY* thread_workbody, void * thread_arg);

void print_usage(char *prograname)
{
    printf("%s usage : \n", prograname);
    printf("-p(--port): specify sever listen port.\n");
    printf("-h(--help): print this help information.\n");

    return  ;

}
int main (int argc, char **argv)
{

    int                      sockfd;
    int                      rv = -1;
    int                      clifd;
    struct sockaddr_in       servaddr;
    struct sockaddr_in       cliaddr;
    socklen_t                len = sizeof(cliaddr);
    int                      port    = 0;
    int                      ch;
    int                      on = 1;
    pthread_t                tid;

    struct option        opts[] = {
        {"port", required_argument, NULL, 'p'},            
        {"help", no_argument, NULL, 'h'},          
        {NULL, 0, NULL, 0}

    };

    while((ch=getopt_long(argc, argv, "p:h", opts, NULL)) != -1 )  //实现参数解析
    {
        switch(ch)
        {
            case 'p':
                port=atoi(optarg);
                break;
            case 'h':
                print_usage(argv[0]);
                return 0;
        }           
    }

    if( !port )        
    {
        print_usage(argv[0]);
        return 0;        
    }

    sockfd = socket(AF_INET,SOCK_STREAM,0); //服务器第一步,socket();
    if(sockfd < 0)
    {
        printf("Cearte socket failure :%s\a\n",strerror(errno));
        return -1;
    }
    printf("Create socket[%d] sucessfully.\n",sockfd);

    setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on,sizeof(on));//使得端口号在短时间内可以得到调用

    memset(&servaddr,0,sizeof(servaddr)); //清空servaddr结构体
    servaddr.sin_family = AF_INET;    //设置ipv4协议,如果要设置ipv6协议, 则需AF_INET6;
    servaddr.sin_port = htons(port);   //将本地字节序的端口转化为网络字节序
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //可以监听任何访问IP地址

    rv = bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr)); //服务器第二步,bind();
    if(rv < 0 )
    {
        printf(" socket[%d] bund port[%d] failure :%s\n\a",sockfd, port,strerror(errno));
        return -2;
    }

    listen(sockfd,13);  //服务器第三步,listen;
    printf("Statring to listen on port[%d].\n",port);

    while(1)
    {
        printf("Starting to accept new client incomming...\n");

        clifd = accept(sockfd, (struct sockaddr *)&cliaddr,&len); //服务器第四步,accept(),本处会从在阻塞
        if(clifd < 0)
        {
            printf("Accept new client failure :%s\n\a", strerror(errno));
        }
        printf("Accept new client[%s:%d]successfuly\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port));

        thread_start(&tid, thread_worker,(void *)(long)clifd ); //创建新的线程来解决accept阻塞问题
    }
    close(sockfd);

    return 0;
}

int thread_start(pthread_t * thread_id, WORKWER_BODY* thread_workbody, void * thread_arg)
{
    int                rv = -1;
    pthread_attr_t      thread_attr;
    if(pthread_attr_init(&thread_attr))  //初始化线程属性 thread_attr
    {
        printf("pthread_attr_init() failure:%s\n", strerror(errno));
        goto CleadUp;
    }

    if(pthread_attr_setstacksize(&thread_attr, 12*1024)) //设置栈大小
    {
        printf("pthread_attr_setstacksize() failure:%s\n", strerror(errno));
        goto CleadUp;   
    }

    if(pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED)) //设置可分离的状态
    {
        printf("pthread_attr_detachstate() failure:%s\n", strerror(errno));
        goto CleadUp;   
    }

    if(pthread_create(thread_id, &thread_attr, thread_workbody, thread_arg)) //创建子线程
    {
        printf("pthread_create() failure:%s\n", strerror(errno));
        goto CleadUp;   
    }

    rv = 0;
CleadUp:
    pthread_attr_destroy(&thread_attr); //摧毁线程属性,释放占用资源
    return rv;
}

void *thread_worker( void *ctx) //服务器第五步 read()会阻塞, write();
{ 
    int        clifd = 0;
    char       buf[1024];
    int        rv = 0;
    if(!ctx) 
    {
        printf("Invalid input arguements in %s()\n", __FUNCTION__);
        pthread_exit(NULL);   
    }
    clifd = (long)ctx;
    printf("clifd = %d\n",clifd );
    printf("Child thread start to commuicate with socket client ...\n");
    while (1) 
    {
        memset(buf,0,sizeof(buf));
        rv = read(clifd,buf,sizeof(buf));
        if(rv < 0)
        {
            printf("Read from client socket[%d] failure: %s\n",clifd,strerror(errno));
            close(clifd);
            pthread_exit(NULL);
        }
        else if(rv == 0)
        {

            printf("socket[%d] get disconnected\n",clifd);
            close(clifd);
            pthread_exit(NULL); //子线程退出必须调用pthread_exit(),调用exit()或return 都会使父子线程都结束
        }
        else if(rv >0)
        {
            printf("Read %d bytes data form client:\n%s\n",rv,buf);
        }
        rv = write(clifd,MSG_STR,strlen(MSG_STR));
        if(rv < 0)
        {
            printf("write to client by socket[%d] failure :%s.\a\n",clifd,strerror(errno));
            close(clifd);
            pthread_exit(NULL);
        }
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XiaoCheng'Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值