多線程
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);
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\n",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\n",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!\n"); 38 exit(0); 39 } 40 if(pthread_create(&tid,NULL,thread_mul,NULL)<0) 41 { 42 printf("Create thread_mul failed!\n"); 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!\n"); 31 } 32 ret=listen(listen_fd,MAXCONN);//確定最大監聽數 33 if(ret==0) 34 { 35 printf("Listen Successfully!\n"); 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\n",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\n",ipaddr,buff); 51 if(strncmp(buff,"quit",4)==0)//判斷退出條件 52 { 53 printf("%s已經退出退出,等待下一個連接\n",ipaddr); 54 break;//退出此個連接,進行下一個連接接入 55 } 56 write(comm_fd,buff,count);//寫數據,發送 57 } 58 else 59 { 60 printf("A talking is over!\n"); 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\n"); 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!\n"); 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("程序退出\n"); 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\n",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\n",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\n"); 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\n",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
物聯網網關開發技術(羅老師)