zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
    
  • 相关阅读:
    java+opencv实现图像灰度化
    java实现高斯平滑
    hdu 3415 单调队列
    POJ 3368 Frequent values 线段树区间合并
    UVA 11795 Mega Man's Mission 状态DP
    UVA 11552 Fewest Flops DP
    UVA 10534 Wavio Sequence DP LIS
    UVA 1424 uvalive 4256 Salesmen 简单DP
    UVA 1099 uvalive 4794 Sharing Chocolate 状态DP
    UVA 1169uvalive 3983 Robotruck 单调队列优化DP
  • 原文地址:https://www.cnblogs.com/kebibuluan/p/7091374.html
Copyright © 2011-2022 走看看