• linux网络编程-socket(37)


    在编程的时候需要加上对应pthread开头的头文件,gcc编译的时候需要加了-lpthread选项

    第三个参数是线程的入口参数,函数的参数是void*,返回值是void*,第四个参数传递给线程函数的参数

    如果创建线程失败,返回值是一个错误码,错误码通过返回值返回,我们要进行错误检查就检查函数的返回值

    函数简介

    pthread_create是UNIX环境创建线程函数

    头文件

    #include<pthread.h>

    函数声明

    int pthread_create(pthread_t *restrict tidp,const pthread_attr_t *restrict_attr,void*(*start_rtn)(void*),void *restrict arg);

    返回值

    若成功则返回0,否则返回出错编号

    参数

    第一个参数为指向线程标识符的指针。

    第二个参数用来设置线程属性。

    第三个参数是线程运行函数的起始地址。

    最后一个参数是运行函数的参数。

    另外

    在编译时注意加上-lpthread参数,以调用静态链接库。因为pthread并非Linux系统的默认库

    pthread_join函数及Linux线程

    pthread_join使一个线程等待另一个线程结束。

    如果你的主线程,也就是main函数执行的那个线程,在你其他线程退出之前就已经退出,那么带来的bug则不可估量。通过pthread_join函数会让主线程阻塞,直到所有线程都已经退出。

      int pthread_join(pthread_t thread, void **value_ptr);
        thread:等待退出线程的线程号。
        value_ptr:退出线程的返回值。

    代码中如果没有pthread_join主线程会很快结束从而使整个进程结束,从而使创建的线程没有机会开始执行就结束了。加入pthread_join后,主线程会一直等待直到等待的线程结束自己才结束,使创建的线程有机会执行。

    我们来看一个简单的例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <pthread.h> 
    
    /*
    *定义一个宏,输出错误信息并且退出 
    */
    #define ERR_EXIT(m) 
    do 
    {
    perror(m);
    exit(EXIT_FAILURE);
    }while(0)
    
    void*thread_exc(void*arg){
        int j = 0;
        for(j = 0 ;j < 20 ;j++){
            printf("sub Thread run%d
    ",j);
        }
        return 0;
    }
    int main(){
        
        
        //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型 
        pthread_t thread_id ;
        int ret;
        if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != 0)){
            ERR_EXIT("线程创建失败");
        }
        
    
        int i = 0;
        for(i = 0 ;i < 20 ;i++){
            printf("main Thread run%d
    ",i);
        }
       printf(" thread_id =%ud
    ",thread_id);
       if((ret = pthread_join(thread_id,NULL))!= 0){
           ERR_EXIT("现在加入失败");
       }
        return 0;
    }

    上面代码有一个相当重要的地方,在c语言中的类型定义

    pthread_create函数的第一个参数是输入一个pthread_t类型
    typedef unsigned long int pthread_t;
    //come from /usr/include/bits/pthreadtypes.h
    用途:pthread_t用于声明线程ID。
    sizeof(pthread_t) =8
    pthread_t,在使用printf打印时,应转换为u类型。
     
    我在写代码的时候不小心写成了int 类型,结果一直提示egmentation fault (core dumped)
    这里一定要小心呀

     可以调用pthread_exit来退出线程

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <pthread.h> 
    
    /*
    *定义一个宏,输出错误信息并且退出 
    */
    #define ERR_EXIT(m) 
    do 
    {
    perror(m);
    exit(EXIT_FAILURE);
    }while(0)
    
    void*thread_exc(void*arg){
        int j = 0;
        for(j = 0 ;j < 20 ;j++){
            printf("sub Thread run%d
    ",j);
            if(j == 3){
                //线程退出的时候会将返回值传递给pthread_join的第二个参数中 
                pthread_exit("abc");
            }
        }
        return 0;
    }
    int main(){
        
        
        //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型 
        pthread_t thread_id ;
        int ret;
        if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != 0)){
            ERR_EXIT("线程创建失败");
        }
        
    
        int i = 0;
        for(i = 0 ;i < 20 ;i++){
            printf("main Thread run%d
    ",i);
        }
       printf(" thread_id =%ud
    ",thread_id);
       void*value;
       if((ret = pthread_join(thread_id,&value))!= 0){
           ERR_EXIT("现在加入失败");
       }
       printf("pthread_exit return msg is %s
    ",((char*)value));
        return 0;
    }

    例如当线程中i=3的时候,退出线程 

    pthread_exit退出的时候可以携带返回值,返回值存储在
    pthread_join的第二个参数中,我们来看程序运行的代码
    代码名称是thread.c我们使用gcc进行编译 gcc thread.c -g -o thread -lpthread
    我们看程序运行的结果

    value中的值就是线程退出的时候的返回值。

     如果不调用pthred_exit退出线程,当线程执行完成之后,也可以返回值,返回值也存储在value中我们来看下面的代码

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <pthread.h> 
    
    /*
    *定义一个宏,输出错误信息并且退出 
    */
    #define ERR_EXIT(m) 
    do 
    {
    perror(m);
    exit(EXIT_FAILURE);
    }while(0)
    
    void*thread_exc(void*arg){
        int j = 0;
        for(j = 0 ;j < 20 ;j++){
            printf("sub Thread run%d
    ",j);
        }
        return "edf";
    }
    int main(){
        
        
        //这里thread_id必须定义成 pthread_t类型 unsigned long int ,无符号的long类型,不能定义成int类型 
        pthread_t thread_id ;
        int ret;
        if((ret = pthread_create(&thread_id,NULL,thread_exc,NULL) != 0)){
            ERR_EXIT("线程创建失败");
        }
        
    
        int i = 0;
        for(i = 0 ;i < 20 ;i++){
            printf("main Thread run%d
    ",i);
        }
       printf(" thread_id =%ud
    ",thread_id);
       void*value;
       if((ret = pthread_join(thread_id,&value))!= 0){
           ERR_EXIT("现在加入失败");
       }
       printf("pthread_exit return msg is %s
    ",((char*)value));
        return 0;
    }
    程序运行的结果是:

     僵尸线程:

    如果是新创建的线程运行结束了,而主线程一直阻塞没有调用pthread_join函数,导致子线程的资源无法释放,那么子线程就会一直处于僵尸状态

    我们可以设置线程的属性来必须线程处于僵尸状态,因为有的时候主线程可能不会主动去调用pthread_join函数

    可以使用pthread_detach函数来避免僵尸线程,将线程的属性设置成脱离的状态,脱离的线程不会产生僵尸线程

    pthread_detach():设置线程为可分离状态,pthread有两种状态joinable状态和unjoinable状态

    pthread_self():获得自身的线程号

    pthread_join():使一个线程等待另一个线程结束

    pthread_self():获得自身的线程

    pthread_create():创建一个新的线程

    pthread_detach():设置线程为可分离状态,pthread有两种状态joinable状态和unjoinable状态

    pthread_exit():通过自身来结束线程,并释放资源

    pthread_cancel():通过其他线程调用释放资源

    pthread_cancel()可以杀死其他的线程

    例如主线程要取消子线程,可以调用pthread_cancel

    现在我们使用多线程的方式,把linux网络编程-socket(2)对应的代码,改成服务器能够支持多个客户端连接请求相应的功能

    首先我们应该把服务器端accept等待客户端请求的代码放在while循环中,让服务器一直运行来监听客户端的请求

    第二:监听到客户端的请求之后,每一个客户端对应一个线程来处理客户端的请求

    我们来修改下服务器端的代码:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <pthread.h> 
    
    /*
    *定义一个宏,输出错误信息并且退出 
    */
    #define ERR_EXIT(m) 
    do 
    {
    perror(m);
    exit(EXIT_FAILURE);
    }while(0)
    
    void*thread_exc(void* arg){
        pthread_detach(pthread_self()); //将线程设置成分离状态,避免僵尸线程 
       int     clnt_sock = *((int*)arg);
       //记得关闭指针
       free(arg); 
       char revbuf[1024];
        while(1){
        memset(revbuf,0,sizeof(revbuf));
        int len = read(clnt_sock,revbuf,sizeof(revbuf)); //len读到数据的字节长度 
        if(len == 0){ //说明客户端终止了数据的发送 
            break;
        }
        fputs(revbuf,stdout);
        //读到多少数据就给客户端返回多少字节的数据 
        write(clnt_sock,revbuf,len);
        }
        close(clnt_sock); //记得关闭线程 
    }
    
    int main(int argc, char *argv[])
    {
        int serv_sock;
        int clnt_sock;
        
        struct sockaddr_in serv_addr;
        struct sockaddr_in clnt_addr;
        socklen_t clnt_addr_size;
        
         
        
        serv_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (serv_sock == -1)
        {
        ERR_EXIT("socket创建失败");
        }
        
        
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_addr.sin_port = htons(9999);
        
        if (bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1){
        ERR_EXIT("bind失败");
        }
        //SOMAXCON系统默认的最大的客户端的连接数据 , (listen(serv_sock, 5)表示最大允许5个客户端的连接 
        if (listen(serv_sock, SOMAXCONN) == -1){
        ERR_EXIT("listen失败");    
        }
        
        while(1){ //在while循环中一直等待客户端的监听
         
          clnt_addr_size = sizeof(clnt_addr);
           clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);
           if (clnt_sock == -1){
              ERR_EXIT("accept失败");    
           }
           //每一个客户端的请求都开启一个线程进行处理
              pthread_t thread_id ;
              int ret;
          //将clnt_sock通过第三个参数传递到线程函数中 
          int * p = (int*)malloc(sizeof(int));
          *p = clnt_sock;
        if((ret = pthread_create(&thread_id,NULL,thread_exc, p ))!= 0){
            ERR_EXIT("线程创建失败");
        }
            
        }
        
        
        close(serv_sock);
    
    return 0;
    }
    
    void error_handling(char *message)
    {
    fputs(message, stderr);
    fputc('
    ', stderr);
    exit(1);
    }

    客户端的代码如下:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    
    /*
    *定义一个宏,输出错误信息并且退出 
    */
    #define ERR_EXIT(m) 
         do 
         {
            perror(m);
            exit(EXIT_FAILURE);
         }while(0)
    
    int main(int argc, char *argv[])
    {
        int serv_sock;
        struct sockaddr_in serv_addr;
    
    
    
        serv_sock = socket(AF_INET, SOCK_STREAM, 0);
        if (serv_sock == -1)
            {
                ERR_EXIT("socket创建失败");
            }
    
    
        memset(&serv_addr, 0, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
        serv_addr.sin_port = htons(9999);
    
    
    
        if((connect(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)))<0){
            ERR_EXIT("客户端connect失败");
        }
        
           char revbuf[1024];
            char sendbuf[1024];
          memset(revbuf,0,sizeof(revbuf));
          memset(sendbuf,0,sizeof(revbuf));
         while((fgets(sendbuf,sizeof(sendbuf),stdin)!= NULL)){
    
            write(serv_sock,sendbuf,strlen(sendbuf));
            read(serv_sock,revbuf,sizeof(revbuf));
             fputs(revbuf,stdout);
             //读到多少数据就给客户端返回多少字节的数据 
             memset(sendbuf,0,sizeof(revbuf));
             memset(revbuf,0,sizeof(revbuf));
         }
        close(serv_sock);
    
        return 0;
    }
    
    
    
  • 相关阅读:
    shell eval命令
    嘟嘟嘟
    07 linkextractor的基本用法
    rabbitmq消息队列
    5. 哨兵集群
    4.主从同步
    3. redis持久化存储
    2. redis 安全
    1.redis基础
    06. scrapy的Request对象
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/7091374.html
Copyright © 2020-2023  润新知