Liunx C 编程之多线程与Socket

前端之家收集整理的这篇文章主要介绍了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的结束而终止。

@H_502_32@pthread_exit (status)
int pthread_cancel(pthread_t threadid);

等待线程状态

pthread_join (threadid,status)  

例子:

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

多线程共享资源

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

定义变量

#include <pthread.h>@H_502_32@ 
pthread_mutex_t lockx;

初始化

pthread_mutex_init(&lockx,NULL);

上锁与解锁

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

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

#include <semaphore.h>@H_502_32@
sem_t can_scanf;

初始化

sem_init(&can_scanf,0,1)">1);

PV操作

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

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

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

@H_502_32@设置server的详情信息
@H_502_32@struct@H_502_32@ sockaddr_in server_addr,client_addr;
u32_t sock_size=@H_502_32@ sockaddr_in);
server_addr.sin_family = AF_INET; @H_502_32@IPV4
server_addr.sin_port = htons(2351); @H_502_32@端口
@H_502_32@绑定本机的所有IP地址htonl(INADDR_ANY),确定某个inet_addr(“172.16.4.1”)
server_addr.sin_addr.s_addr =@H_502_32@htonl(INADDR_ANY); 
bind(connect_socket,(@H_502_32@struct sockaddr*)&server_addr,@H_502_32@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状态)
结构体参数

@H_502_32@绑定本机的所有IP地址htonl(INADDR_ANY),确定某个inet_addr(“172.16.4.1”)
server_addr.sin_addr.s_addr = inet_addr(192.168.43.21); 
@H_502_32@int ret=connect(client_fd,sock_size);@H_502_32@连接服务器

4、recv 和 send

 服务器示例

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

客户端示例

@H_502_32@ 8 #include <@H_502_32@int main(@H_502_32@int argc,1)">char **@H_502_32@argv)
@H_502_32@ client_fd;
@H_502_32@ sockaddr_in server_addr;
@H_502_32@char buf[16     @H_502_32@char recv_buf[18     @H_502_32@if(argc<20         printf(Usage:./client serverip\n21         @H_502_32@22 @H_502_32@23     bzero(&server_addr,1)">24     client_fd=socket(AF_INET,1)">25     server_addr.sin_family=@H_502_32@AF_INET;
@H_502_32@26     server_addr.sin_port=htons(8000@H_502_32@27     server_addr.sin_addr.s_addr=inet_addr(argv[]);
@H_502_32@28     ret=connect(client_fd,1)">连接服务器
@H_502_32@29     @H_502_32@if(ret<30 @H_502_32@31         perror(Failed to connect:32         @H_502_32@33 @H_502_32@34     printf(Connect successfully!\n36     {    printf(请输入要发送的内容:37         fgets(buf,1)">512,stdin);@H_502_32@从键盘获取字符串
@H_502_32@38         ret=write(client_fd,buf,strlen(buf));@H_502_32@if(ret<=40             @H_502_32@41         @H_502_32@if(strncmp(buf,1)">){
@H_502_32@42             printf(程序退出\n43             @H_502_32@;    
@H_502_32@45         count=read(client_fd,recv_buf,1)">46         @H_502_32@47 @H_502_32@48             recv_buf[count]=截断接收的字符串
@H_502_32@49             printf(Echo:%s\n502_32@50 @H_502_32@        }    
@H_502_32@51         @H_502_32@53             @H_502_32@;//服务器断开
@H_502_32@54 @H_502_32@56     close(client_fd);@H_502_32@57     @H_502_32@58     
@H_502_32@59 }

UDP

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

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

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

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

数据接收

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

服务器示例

@H_502_32@ 1 #include <sys/socket.h>
@H_502_32@ 2 #include <netinet/@H_502_32@ 3 #include <arpa/inet.h>
@H_502_32@ 4 #include <@H_502_32@ 5 #include <stdio.h>
@H_502_32@ 7 @H_502_32@ sockfd;
@H_502_32@10     @H_502_32@15     sockfd=socket(AF_INET,SOCK_DGRAM,1)">if(sockfd<18         perror(Failed to socket:19         @H_502_32@21     bzero(&@H_502_32@server_addr,sock_size);
@H_502_32@22     server_addr.sin_family=AF_INET;@H_502_32@服务器相关参数设置
@H_502_32@23     server_addr.sin_port=htons(6000@H_502_32@24     server_addr.sin_addr.s_addr=@H_502_32@INADDR_ANY;
@H_502_32@25     setsockopt(sockfd,1)">));
@H_502_32@26     @H_502_32@if(bind(sockfd,sock_size)<等待客户端接入,阻塞
@H_502_32@28         perror(Failed to bind:29         @H_502_32@32 @H_502_32@33         ret=recvfrom(sockfd,&sock_size);@H_502_32@收到数据包
@H_502_32@34         @H_502_32@if(ret>35 @H_502_32@36             buff[ret]=37             inet_ntop(AF_INET,1)">38             printf(Receive a string from %s:%d,data:%s\n39             @H_502_32@exit0){@H_502_32@退出
@H_502_32@40               printf(Socket server exit 41               close(sockfd);@H_502_32@关闭socket    
@H_502_32@42               @H_502_32@44             sendto(sockfd,ret,1)">struct sockaddr*)&@H_502_32@client_addr,1)">45 @H_502_32@    close(sockfd);    
@H_502_32@48 }

客户端示例

@H_502_32@ 8 #include <strings.h>
502_32@Usage:./udpclient serverip\n23     client_fd=socket(AF_INET,1)">24     bzero(&@H_502_32@    {   
@H_502_32@In:31         fgets(buf,stdin);
@H_502_32@32         ret=sendto(client_fd,strlen(buf),1)">33         @H_502_32@35             perror(Failed to sendto:36             @H_502_32@37 @H_502_32@38         @H_502_32@40         count=recvfrom(client_fd,1)">struct sockaddr*)&sock_addr,1)">sock_size);
@H_502_32@42 @H_502_32@43             recv_buf[count]=44             printf(48             perror(Failed to recvfrom:49             @H_502_32@    close(client_fd);
@H_502_32@53     @H_502_32@54     
@H_502_32@55 }

参考:

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

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

猜你在找的C&C++相关文章