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!\n);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)">1);
PV *** 作
sem_wait(&can_scanf);等待信号量置位并进行减一 *** 作sem_post(&can_scanf); 信号量加一 *** 作
例子
主线程负责从键盘获取两个整数,子线程1负责对这两个整数完成求和运算并把结果打印出来,子线程2负责对这两个整数完成乘法运算并打印出来。三个线程要求遵循如下同步顺序:
1.主线程获取两个数;
2.子线程1计算;
3.子线程2计算;
4.转(1)
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)加法线程入口函数11 while(112 13 sem_wait(&can_add);14 printf(%d+%d=%d\n",x,y,x+y);15 sem_post(&can_mul);16 voID *thread_mul(乘法线程入口函数20 21 22 sem_wait(&23 printf(%d*%d=%d\n24 sem_post(&can_scanf);25 26 27 28 29 pthread_t tID;30 int arg[2];31 信号量初始化 32 sem_init(&can_scanf,1)">33 sem_init(&can_add,1)">034 sem_init(&can_mul,1)">35 if(pthread_create(&tID,thread_add,NulL)<36 37 printf(Create thread_add Failed!\n38 exit(39 40 41 42 printf(Create thread_mul Failed!\n43 exit(44 45 46 47 sem_wait(&can_scanf);等待信号量置位并进行减一 *** 作 48 printf(Please input two integers:49 scanf(%d%d50 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的数据报一定会无差错按序交付给对方
服务端
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= sockaddr_in);server_addr.sin_family = AF_INET; IPV4server_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状态)
结构体参数
绑定本机的所有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,sock_size);连接服务器
4、recv 和 send
服务器示例
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>#define MAXCONN 8 Listen_fd,comm_fd;12 ret;13 int i=14 15 int sock_size= sockaddr_in);16 Listen_fd=socket(AF_INET,SOCK_STREAM,1)">0);创建一个socket,参数(IPV4,TCP,0)17 if(Listen_fd<19 perror(Failed to create socket:20 return -22 bzero(&server_addr,1)">清零server_addr23 server_addr.sin_family=AF_INET;IPV424 server_addr.sin_port=htons(8000);端口25 server_addr.sin_addr.s_addr=INADDR_ANY;绑定主机全部网络地址26 setsockopt(Listen_fd,Sol_SOCKET,SO_REUSEADDR,&i,1)">设置套接字关联的选 项27 ret=bind(Listen_fd,1)">网络主机绑定28 if(ret==30 printf(Bind Successfully!\n31 32 ret=Listen(Listen_fd,MAXCONN);确定最大监听数33 34 35 printf(Listen Successfully!\n37 while((comm_fd=accept(Listen_fd,1)">struct sockaddr*)&clIEnt_addr,&sock_size))>=阻塞并等待接入38 39 char ipaddr[1640 inet_ntop(AF_INET,&clIEnt_addr.sin_addr.s_addr,ipaddr,1)">16);网络地址符转换41 printf(连接进入:%s\n42 43 {44 char buff[51245 count;46 count=read(comm_fd,buff,1)">511);读数据,接收47 if(count>判断接收的字节数是否大于零48 {49 buff[count]=0;截断字符串50 printf(收到来自 %s 的数据:%s\n51 if(strncmp(buff,1)">quit4)==判断退出条件52 {53 printf(%s已经退出退出,等待下一个连接\n54 break;退出此个连接,进行下一个连接接入55 }56 write(comm_fd,count);写数据,发送57 }58 else59 60 printf(A talking is over!\n61 break; //客户端断开 62 63 }64 65 close(Listen_fd);关闭连接66 return 67 68 }
客户端示例
8 #include <int main(int argc,1)">char **argv) clIEnt_fd; sockaddr_in server_addr;char buf[16 char recv_buf[18 if(argc<20 printf(Usage:./clIEnt serverip\n21 22 23 bzero(&server_addr,1)">24 clIEnt_fd=socket(AF_INET,1)">25 server_addr.sin_family=AF_INET;26 server_addr.sin_port=htons(800027 server_addr.sin_addr.s_addr=inet_addr(argv[]);28 ret=connect(clIEnt_fd,1)">连接服务器29 if(ret<30 31 perror(Failed to connect:32 33 34 printf(Connect successfully!\n36 { printf(请输入要发送的内容:37 fgets(buf,1)">512,stdin);从键盘获取字符串38 ret=write(clIEnt_fd,buf,strlen(buf));if(ret<=40 41 if(strncmp(buf,1)">){42 printf(程序退出\n43 ; 45 count=read(clIEnt_fd,recv_buf,1)">46 47 48 recv_buf[count]=截断接收的字符串49 printf(Echo:%s\n50 } 51 53 ;//服务器断开54 56 close(clIEnt_fd);57 58 59 }UDP
服务器
1、 创建socket
2、 调用函数设置udp播
int setsockopt(int s,1)">int level,1)">int optname,1)">const 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,1)">voID *msg,size_t len,1)">int flags,1)">struct sockaddr *to,1)"> tolen);返回:大于0-成功发送数据长度;--出错;UDP套接字使用无连接协议,因此必须使用sendto函数,指明目的地址;msg:发送数据缓冲区的首地址;len:缓冲区的长度;flags:传输控制标志,通常为0;to:发送目标;tolen: 地址结构长度——struct sockaddr)
数据接收
int recvfrom(voID *buf,1)">struct sockaddr *from,1)">int *fromlen);返回:大于0——成功接收数据长度;-——出错;buf:接收数据的保存地址;len:接收的数据长度flags:是传输控制标志,通常为0;from:保存发送方的地址fromlen: 地址结构长度。
服务器示例
1 #include <sys/socket.h> 2 #include <netinet/ 3 #include <arpa/inet.h> 4 #include < 5 #include <stdio.h> 7 sockfd;10 15 sockfd=socket(AF_INET,SOCK_DGRAM,1)">if(sockfd<18 perror(Failed to socket:19 21 bzero(&server_addr,sock_size);22 server_addr.sin_family=AF_INET;服务器相关参数设置23 server_addr.sin_port=htons(600024 server_addr.sin_addr.s_addr=INADDR_ANY;25 setsockopt(sockfd,1)">));26 if(bind(sockfd,sock_size)<等待客户端接入,阻塞28 perror(Failed to bind:29 32 33 ret=recvfrom(sockfd,&sock_size);收到数据包34 if(ret>35 36 buff[ret]=37 inet_ntop(AF_INET,1)">38 printf(Receive a string from %s:%d,data:%s\n39 exit0){退出40 printf(Socket server exit 41 close(sockfd);关闭socket 42 44 sendto(sockfd,ret,1)">struct sockaddr*)&clIEnt_addr,1)">45 close(sockfd); 48 }
客户端示例
8 #include <strings.h>Usage:./udpclIEnt serverip\n23 clIEnt_fd=socket(AF_INET,1)">24 bzero(& { In:31 fgets(buf,stdin);32 ret=sendto(clIEnt_fd,strlen(buf),1)">33 35 perror(Failed to sendto:36 37 38 40 count=recvfrom(clIEnt_fd,1)">struct sockaddr*)&sock_addr,1)">sock_size);42 43 recv_buf[count]=44 printf(48 perror(Failed to recvfrom:49 close(clIEnt_fd);53 54 55 }
参考:
https://www.cnblogs.com/mywolrd/archive/2009/02/05/1930707.html
物联网网关开发技术(罗老师)
总结以上是内存溢出为你收集整理的Liunx C 编程之多线程与Socket全部内容,希望文章能够帮你解决Liunx C 编程之多线程与Socket所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)