zoukankan      html  css  js  c++  java
  • Liunx C 编程之多线程与Socket

    多线程

    pthread.h是linux特有的头文件,POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。Windows操作系统也有其移植版pthreads-win32。

    创建线程
    1.pthread_create 创建一个新线程并使之运行起来。该函数可以在程序的任何地方调用包括线程内,线程是没有依赖关系的。
    2.一个进程可以创建的线程最大数量取决于系统实现
    3. pthread_create参数:
            thread:返回一个不透明的,唯一的新线程标识符。
            attr:不透明的线程属性对象。可以指定一个线程属性对象,或者NULL为缺省值。
            start_routine:线程将会执行一次的C函数。
            arg: 传递给start_routine单个参数,传递时必须转换成指向void的指针类型。没有参数传递时,可设置为NULL。

    pthread_create (threadid,attr,start_routine,arg)

    结束线程
    1.结束线程的方法有一下几种:
           线程从主线程(main函数的初始线程)返回。
           线程调用了pthread_exit函数。
           其它线程使用 pthread_cancel函数结束线程。
           调用exec或者exit函数,整个进程结束。

    2.如果main()在其他线程创建前用pthread_exit()退出了,其他线程将会继续执行。否则,他们会随着main的结束而终止。

    pthread_exit (status)
    int pthread_cancel(pthread_t threadid);

    等待线程状态

    pthread_join (threadid,status)  

    例子:

     1 #include <stdio.h>
     2 #include <pthread.h> //liunx线程头文件
     3 #include <stdlib.h>
     4 //线程
     5 void *thread1_proc(void *arg)
     6 {
     7     int i=*(int *)arg;    //取出内容 
     8     free(arg);//释放空间 
     9     while(i<105)
    10     {
    11         printf("thread1:%-5d",i);
    12         sleep(2);//延时等待两秒 
    13         i++;
    14     }
    15     printf("Thread1 finished!
    ");
    16     pthread_exit(NULL);//终止当前线程
    17 }
    18 void main()
    19 {
    20     pthread_t thread1;
    21     int *ixi=(int *)malloc(sizeof(int));//在堆中申请一块内容 
    22     *ixi=100; //存在内容 
    23     if(pthread_create(&thread1,NULL,thread1_proc,(void *)ixi)!=0)//创建线程1并传递参数 
    24         perror("Create thread failed:");//创建错误时执行
    25     //终止当前线程,此时会子线程会执行完毕,相当于在此处join所有子线程一样 
    26     pthread_exit(NULL);//(1)结束主
    27     // pthread_join(thread1,NULL);//(2)可替换上一条
    28     printf("主线程已经退出,本条不执行"); //(1)不执行,(2)执行该条
    29 }

    多线程共享资源

    共享资源时可能会出现操作未完成而被另一个线程打破,造成资源存取异常

    定义变量

    #include <pthread.h> 
    pthread_mutex_t lockx;

    初始化

    pthread_mutex_init(&lockx,NULL);

    上锁与解锁

    pthread_mutex_lock(&lockx);//上锁 
    //独立资源
    //代码块 
    pthread_mutex_unlock(&lockx);//解锁

    信号量
    实现循序控制
    定义变量

    #include <semaphore.h>
    sem_t can_scanf;

    初始化

    sem_init(&can_scanf,0,1);

    PV操作

    sem_wait(&can_scanf);//等待信号量置位并进行减一操作
    sem_post(&can_scanf); //信号量加一 操作

    例子
    主线程负责从键盘获取两个整数,子线程1负责对这两个整数完成求和运算并把结果打印出来,子线程2负责对这两个整数完成乘法运算并打印出来。三个线程要求遵循如下同步顺序:
    1.主线程获取两个数;
    2.子线程1计算;
    3.子线程2计算;
    4.转(1)

     1 #include <stdio.h>
     2 #include <semaphore.h>
     3 #include <pthread.h>
     4 #include <stdlib.h>
     5 sem_t can_add;//能够进行加法计算的信号量
     6 sem_t can_mul;//能够进行输入的信号量 
     7 sem_t can_scanf;//能够进行乘法计算的信号量
     8 int x,y;
     9 void *thread_add(void *arg)//加法线程入口函数
    10 {
    11     while(1)
    12     {
    13         sem_wait(&can_add);
    14         printf("%d+%d=%d
    ",x,y,x+y);
    15         sem_post(&can_mul);
    16     }
    17 }
    18 void *thread_mul(void *arg)//乘法线程入口函数
    19 {
    20     while(1)
    21     {
    22         sem_wait(&can_mul);
    23         printf("%d*%d=%d
    ",x,y,x*y);
    24         sem_post(&can_scanf);
    25     }
    26 }
    27 int main()
    28 {
    29     pthread_t tid;
    30     int arg[2];
    31     //信号量初始化 
    32     sem_init(&can_scanf,0,1);
    33     sem_init(&can_add,0,0);
    34     sem_init(&can_mul,0,0);
    35     if(pthread_create(&tid,NULL,thread_add,NULL)<0)
    36     {
    37         printf("Create thread_add failed!
    ");
    38         exit(0);
    39     }
    40     if(pthread_create(&tid,NULL,thread_mul,NULL)<0)
    41     {
    42         printf("Create thread_mul failed!
    ");
    43         exit(0);
    44     }
    45     while(1)
    46     {
    47         sem_wait(&can_scanf);//等待信号量置位并进行减一操作 
    48         printf("Please input two integers:");
    49         scanf("%d%d",&x,&y);
    50         sem_post(&can_add);//信号量加一 操作 
    51     }        
    52 }

    Socket编程

    数据包的发送
    (1)TCP(write/send)
    基于流的,没有信息边界,所以发送的包的大小没有限制;但由于没有信息边界,就得要求要求应用程序自己能够把逻辑上的包分割出来。
    (2)UDP(sendto)
    基于包的,应用层的包在由下层包的传输过程中因尽量避免有分片——重组的发生,否则丢包的概率会很大,在以太网中,MTU的大小为46——1500字节,除掉IP层头、udp头,应用层的UDP包不要超过1472字节。鉴于Internet上的标准MTU值为576字节,所以我建议在进行Internet的UDP编程时。 最好将UDP的数据长度控制在548字节(576-8-20)以内.


    数据包的接收
    (1)TCP(read/recv)
    如果协议栈缓冲区实际收到的字节数大于所请求的字节数,则返回实际要读取的字节数,剩余未读取的字节数下次再读;
    如果协议栈缓冲区实际收到的字节数小于所请求的字节数,则返回所能提供的字节数;
    (2)UDP(recvfrom)
    如果协议栈缓冲区实际收到的字节数大于所请求的字节数,在linux下会对数据报进行截段,并丢弃剩下的数据;
    如果协议栈缓冲区实际收到的字节数小于所请求的字节数,则返回所能提供的字节数;


    注意点
    当发送函数返回时,并不表示数据包已经到达了目标计算机,仅仅说明待发送的数据包被协议栈给接收了;
    UDP的数据包要么被接收,要么丢失;TCP的数据报一定会无差错按序交付给对方

    TCP

    服务端
    1、连接WiFi或者开启AP,使模块接入网络
    2、socket 创建一个套接字
    socket可以认为是应用程序和网络之间信息传输通道,所以TCP编程服务端、客户端的第一步就是要建立这个信息传输的通道,主要通过socket函数完成。

    3、 Bind socket信息
    给在第一步中所创建的socket显式指定其ip地址和端口号(bind)
    其中结构体为:

    //设置server的详情信息
    struct sockaddr_in server_addr,client_addr;
    u32_t sock_size=sizeof(struct sockaddr_in);
    server_addr.sin_family = AF_INET; //IPV4
    server_addr.sin_port = htons(2351); //端口
    //绑定本机的所有IP地址htonl(INADDR_ANY),确定某个inet_addr(“172.16.4.1”)
    server_addr.sin_addr.s_addr =htonl(INADDR_ANY); 
    bind(connect_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));

    4、 listen确定请求队列的最大值

    5、 accept等待接入
    此函数为所有网络函数中最难理解的一个函数,它的调用将意味着服务端开始处理外来请求,如果没有外来请求(也就是没有listen到请求进来)默认情况下则阻塞;当有外来请求时会新产生一个soket,并返回其描述符,应用程序将在这个新的socket上和请求者进行会话(读、写该socket),原套接字sockfd则继续侦听

    6、 send
    当send返回时,并不是表示数据已经发送到了对方,而仅仅表示数据已经到了协议栈的缓冲区中。最后一个值在ESP32中不可用

     

    7、 recv
    默认情况下,当没有可接收的数据时则阻塞,参数len表示最多接收多少个字节数, 成功的接受的字节数完全可以小于len。最后一个值在ESP32中不可用

    客户端
    1、连接WiFi或者开启AP,使模块接入网络
    2、socket 创建一个套接字,参考服务器
    3、是指向服务端发起连接请求(请求成功的前提是服务端已经进入了accept状态)
    结构体参数

    //设置server的详情信息
    struct sockaddr_in server_addr,client_addr;
    u32_t sock_size=sizeof(struct sockaddr_in);
    server_addr.sin_family = AF_INET; //IPV4
    server_addr.sin_port = htons(2351); //端口
    //绑定本机的所有IP地址htonl(INADDR_ANY),确定某个inet_addr(“172.16.4.1”)
    server_addr.sin_addr.s_addr = inet_addr("192.168.43.21"); 
    int ret=connect(client_fd,(struct sockaddr*)&server_addr,sock_size);//连接服务器

    4、recv 和 send

     服务器示例

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <fcntl.h>
     4 #include <sys/socket.h>
     5 #include <arpa/inet.h>
     6 #include <netinet/in.h>
     7 #include <string.h>
     8 #define MAXCONN 8
     9 int main()
    10 {
    11     int listen_fd,comm_fd;
    12     int ret;
    13     int i=1;
    14     struct sockaddr_in server_addr,client_addr;
    15     int sock_size=sizeof(struct sockaddr_in);
    16     listen_fd=socket(AF_INET,SOCK_STREAM,0);//创建一个socket,参数(IPV4,TCP,0)
    17     if(listen_fd<0)
    18     {
    19         perror("Failed to create socket:");
    20         return -1;
    21     }
    22     bzero(&server_addr,sock_size);//清零server_addr
    23     server_addr.sin_family=AF_INET;//IPV4
    24     server_addr.sin_port=htons(8000);//端口
    25     server_addr.sin_addr.s_addr=INADDR_ANY;//绑定主机全部网络地址
    26     setsockopt(listen_fd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));//设置套接字关联的选 项
    27     ret=bind(listen_fd,(struct sockaddr*)&server_addr,sock_size);//网络主机绑定
    28     if(ret==0)
    29     {
    30         printf("Bind Successfully!
    ");
    31     }
    32     ret=listen(listen_fd,MAXCONN);//确定最大监听数
    33     if(ret==0)
    34     {
    35         printf("Listen Successfully!
    ");
    36     }
    37     while((comm_fd=accept(listen_fd,(struct sockaddr*)&client_addr,&sock_size))>=0)//阻塞并等待接入
    38     {
    39         char ipaddr[16];
    40         inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ipaddr,16);//网络地址符转换
    41         printf("连接进入:%s
    ",ipaddr);
    42         while(1)
    43         {
    44             char buff[512];
    45             int count;
    46             count=read(comm_fd,buff,511);//读数据,接收
    47             if(count>0)//判断接收的字节数是否大于零
    48             {
    49                 buff[count]=0;//截断字符串
    50                 printf("收到来自 %s 的数据:%s
    ",ipaddr,buff);
    51                 if(strncmp(buff,"quit",4)==0)//判断退出条件
    52                 {
    53                     printf("%s已经退出退出,等待下一个连接
    ",ipaddr);
    54                     break;//退出此个连接,进行下一个连接接入
    55                 }
    56                 write(comm_fd,buff,count);//写数据,发送
    57             }
    58             else
    59             {
    60                 printf("A talking is over!
    ");
    61                 break;  //客户端断开                  
    62             }
    63         }
    64     }
    65     close(listen_fd);//关闭连接
    66     return 0;
    67     
    68 }

    客户端示例

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <fcntl.h>
     4 #include <sys/socket.h>
     5 #include <arpa/inet.h>
     6 #include <netinet/in.h>
     7 #include <string.h>
     8 #include <string.h>
     9 int main(int argc,char **argv)
    10 {
    11     int client_fd;
    12     int ret;
    13     int count;
    14     struct sockaddr_in server_addr;
    15     char buf[512];
    16     char recv_buf[512];
    17     int sock_size=sizeof(struct sockaddr_in);
    18     if(argc<2)
    19     {
    20         printf("Usage:./client serverip
    ");
    21         return 0;
    22     }
    23     bzero(&server_addr,sock_size);//清零server_addr
    24     client_fd=socket(AF_INET,SOCK_STREAM,0);//创建一个socket,参数(IPV4,TCP,0)
    25     server_addr.sin_family=AF_INET;
    26     server_addr.sin_port=htons(8000);
    27     server_addr.sin_addr.s_addr=inet_addr(argv[1]);
    28     ret=connect(client_fd,(struct sockaddr*)&server_addr,sock_size);//连接服务器
    29     if(ret<0)
    30     {
    31         perror("Failed to connect:");
    32         return -1;
    33     }
    34     printf("Connect successfully!
    ");
    35     while(1)
    36     {    printf("请输入要发送的内容:");
    37         fgets(buf,512,stdin);//从键盘获取字符串
    38         ret=write(client_fd,buf,strlen(buf));//写数据,发送
    39         if(ret<=0)
    40             break;
    41         if(strncmp(buf,"quit",4)==0){
    42             printf("程序退出
    ");
    43             break;    
    44         }
    45         count=read(client_fd,recv_buf,511);//读数据,接收
    46         if(count>0)
    47         {
    48             recv_buf[count]=0;//截断接收的字符串
    49             printf("Echo:%s
    ",recv_buf);
    50         }    
    51         else
    52         {
    53             break;//服务器断开
    54         }
    55     }
    56     close(client_fd);//关闭连接
    57     return 0;
    58     
    59 }

    UDP

    服务器
    1、 创建socket
    2、 调用函数设置udp播

    int  setsockopt(int  s,  int level,  int  optname, const void *optval, socklen_t  optlen);
    头文件:<sys/socket.h>
    level : 选项级别(例如SOL_SOCKET)
    optname : 选项名(例如SO_BROADCAST)
    optval : 存放选项值的缓冲区的地址
    optlen : 缓冲区长度
    返回值:成功返回0   失败返回-1并设置errno

    3、 绑定服务器信息bind
    4、 数据收发
    数据发送

    int  sendto(int sockfd, const void *msg, size_t len, int flags, const struct sockaddr *to, int tolen);
    返回:大于0-成功发送数据长度;-1-出错;
    UDP套接字使用无连接协议,因此必须使用sendto函数,指明目的地址;
    msg:发送数据缓冲区的首地址;
    len:缓冲区的长度;
    flags:传输控制标志,通常为0;
    to:发送目标;
    tolen: 地址结构长度——sizeof(struct sockaddr)

    数据接收

    int  recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *from, int *fromlen);
    返回:大于0——成功接收数据长度;-1——出错;
    buf:接收数据的保存地址;
    len:接收的数据长度
    flags:是传输控制标志,通常为0;
    from:保存发送方的地址
    fromlen: 地址结构长度。

    服务器示例

     1 #include <sys/socket.h>
     2 #include <netinet/in.h>
     3 #include <arpa/inet.h>
     4 #include <string.h>
     5 #include <stdio.h>
     6 int main()
     7 {
     8     int sockfd;
     9     int ret;
    10     char buff[512];
    11     char ipaddr[16];
    12     struct sockaddr_in server_addr,client_addr;
    13     int i=1;
    14     int sock_size=sizeof(struct sockaddr_in);
    15     sockfd=socket(AF_INET,SOCK_DGRAM,0);
    16     if(sockfd<0)
    17     {
    18         perror("Failed to socket:");
    19         return -1;
    20     }
    21     bzero(&server_addr,sock_size);
    22     server_addr.sin_family=AF_INET;//服务器相关参数设置
    23     server_addr.sin_port=htons(6000);
    24     server_addr.sin_addr.s_addr=INADDR_ANY;
    25     setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(int));
    26     if(bind(sockfd,(struct sockaddr*)&server_addr,sock_size)<0)//等待客户端接入,阻塞
    27     {
    28         perror("Failed to bind:");
    29         return -1;
    30     }
    31     while(1)
    32     {
    33         ret=recvfrom(sockfd,buff,512,0,(struct sockaddr*)&client_addr,&sock_size);//收到数据包
    34         if(ret>0)
    35         {
    36             buff[ret]=0;
    37             inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,ipaddr,16);//网络地址符转换
    38             printf("Receive a string from %s:%d,data:%s
    ",ipaddr,client_addr.sin_port,buff);
    39             if(strncmp(buff,"exit",4)==0){//退出
    40               printf("Socket server exit ");
    41               close(sockfd);//关闭socket    
    42               break;    
    43             }
    44             sendto(sockfd,buff,ret,0,(struct sockaddr*)&client_addr,sock_size);
    45         }
    46     }
    47     close(sockfd);    
    48 }

    客户端示例

     1 #include <stdio.h>
     2 #include <unistd.h>
     3 #include <fcntl.h>
     4 #include <sys/socket.h>
     5 #include <arpa/inet.h>
     6 #include <netinet/in.h>
     7 #include <string.h>
     8 #include <strings.h>
     9 int main(int argc,char **argv)
    10 {
    11     int client_fd;
    12     int ret;
    13     int count;
    14     struct sockaddr_in server_addr,sock_addr;
    15     char buf[512];
    16     char recv_buf[512];
    17     int sock_size=sizeof(struct sockaddr_in);
    18     if(argc<2)
    19     {
    20         printf("Usage:./udpclient serverip
    ");
    21         return 0;
    22     }
    23     client_fd=socket(AF_INET,SOCK_DGRAM,0);
    24     bzero(&server_addr,sock_size);
    25     server_addr.sin_family=AF_INET;
    26     server_addr.sin_port=htons(6000);
    27     server_addr.sin_addr.s_addr=inet_addr(argv[1]);
    28     while(1)
    29     {   
    30         printf("In:");
    31         fgets(buf,512,stdin);
    32         ret=sendto(client_fd,buf,strlen(buf),0,(struct sockaddr*)&server_addr,sock_size);
    33         if(ret<0)
    34         {
    35             perror("Failed to sendto:");
    36             break;
    37         }
    38         if(strncmp(buf,"exit",4)==0)
    39             break;    
    40         count=recvfrom(client_fd,recv_buf,512,0,(struct sockaddr*)&sock_addr,&sock_size);
    41         if(count>0)
    42         {
    43             recv_buf[count]=0;
    44             printf("Echo:%s
    ",recv_buf);
    45         }    
    46         else
    47         {
    48             perror("Failed to recvfrom:");
    49             break;
    50         }
    51     }
    52     close(client_fd);
    53     return 0;
    54     
    55 }

    参考:

    https://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html

    物联网网关开发技术(罗老师)

  • 相关阅读:
    笔试复习题《三》
    笔试复习题《二》
    《数据结构(C#语言描述)》
    设计模式《一》
    笔试复习题《一》之常用的排序算法
    序列模型(1)----简单介绍
    梯度消失、爆炸原因及其解决方法
    算法66------计算各个位数不同的数字个数【动态规划】
    TensorFlow实战学习笔记(14)------VGGNet
    TensorFlow技术解析与实战学习笔记(13)------Mnist识别和卷积神经网络AlexNet
  • 原文地址:https://www.cnblogs.com/dongxiaodong/p/11309140.html
Copyright © 2011-2022 走看看