ART PI之UDP非阻塞客戶端

2022-09-23 17:48:12 字數 4106 閱讀 2123

服務端控制客戶端的小電機(pwm 方式)

客戶端: art-pi,向服務端傳送天氣資訊和客戶端狀態,訊息格式s:%d;v:%d;n:%d;l:%s

服務端:自製python服務端,埠繫結8887,傳送電機控制命令  60/61/62/63/64  (hex 0x36 0x30...)

遇到的問題:雖然使用的是udp 連線, 預設狀態下recvfrom是阻塞的, 如果服務端沒有傳送指令,socket並沒有收到資料,客戶端任務阻塞等待在這個接收函式。

如果這個任務還有其他任務要求,比如傳送狀態,那就成了問題。那麼,是否有一種方法為udp的recv設定超時。

方法一:設定socket超時時間, timeout後, 讀取recvfrom的返回值,判斷狀態並繼續loop

struct

timeval tv_out;

tv_out.tv_sec = 3

; tv_out.tv_usec = 0

; setsockopt(sock, sol_socket, so_rcvtimeo, &tv_out,izeof(tv_out));

方法二:使用select實現非阻塞通訊

/*

int select(int maxfdp1, fd_set* readset, fd_set* writeset, fd_set* exceptset, const struct timeval* timeout)

返回值:就緒描述符的數目,超時返回0,出錯返回 - 1

*//*

引數列表:

int maxfdp是乙個整數值,是指集合中所有檔案描述符的範圍,即所有檔案描述符的最大值加1,不能錯!

fd_set* readfds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的讀變化的,即我們關心是否可以從這些檔案中讀取資料了,如果這個集合中有乙個檔案可讀,select就會返回乙個大於0的值,表示有檔案可讀,如果沒有可讀的檔案,則根據timeout引數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入null值,表示不關心任何檔案的讀變化。

d_set* writefds是指向fd_set結構的指標,這個集合中應該包括檔案描述符,我們是要監視這些檔案描述符的寫變化的,即我們關心是否可以向這些檔案中寫入資料了,如果這個集合中有乙個檔案可寫,select就會返回乙個大於0的值,表示有檔案可寫,如果沒有可寫的檔案,則根據timeout引數再判斷是否超時,若超出timeout的時間,select返回0,若發生錯誤返回負值。可以傳入null值,表示不關心任何檔案的寫變化。

fd_set* errorfds同上面兩個引數的意圖,用來監視檔案錯誤異常。

struct timeval* timeout是select的超時時間,這個引數至關重要,它可以使select處於三種狀態:

第一,若將null以形參傳入,即不傳入時間結構,就是將select置於阻塞狀態,一定等到監視檔案描述符集合中某個檔案描述符發生變化為止;

第二,若將時間值設為0秒0毫秒,就變成乙個純粹的非阻塞函式,不管檔案描述符是否有變化,都立刻返回繼續執行,檔案無變化返回0,有變化返回乙個正值;

第三,timeout的值大於0,這就是等待的超時時間,即 select在timeout時間內阻塞,超時時間之內有事件到來就返回了,否則在超時後不管怎樣一定返回,返回值同上述。

* //* 說明兩個結構體:

第一,struct fd_set可以理解為乙個集合,這個集合中存放的是檔案描述符(file descriptor),即檔案控制代碼,這可以是我們所說的普通意義的檔案,當然unix下任何裝置、管道、fifo等都是檔案形式,全部包括在內,所以毫無疑問乙個socket就是乙個檔案,socket控制代碼就是乙個檔案描述符。fd_set集合可以通過一些巨集由人為來操作。比如清空集合 fd_zero(fd_set*),將乙個給定的檔案描述符加入集合之中fd_set(int, fd_set*),將乙個給定的檔案描述符從集合中刪除fd_clr(int, fd_set*),檢查集合中指定的檔案描述符是否可以讀寫fd_isset(int, fd_set*)。

第二,struct timeval是乙個大家常用的結構,用來代表時間值,有兩個成員,乙個是秒數,另乙個是毫秒數。

*/

void udp_demo(void);

int vol = 0

;

char buff[128] = ;

char ip_addr_buf[64

]; recv_data = rt_calloc(1

, test_bufsz);

if (recv_data ==rt_null)

//rt_thread_mdelay(5000);

//udp 收發的一些心得

///*建立乙個socket,型別是sock_dgram,udp型別

*/if ((sock = socket(af_inet, sock_dgram, 0)) == -1

)

server_addr.sin_family =af_inet;

server_addr.sin_port =htons(udp_remote_port);

server_addr.sin_addr.s_addr =inet_addr(udp_remote_ip);

rt_kprintf(

"ip: %s, port: %d \n

",udp_remote_ip, udp_remote_port);

if ((ret = connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr))) < 0

)

log_i(

"connect success\n");

////udp receive timeout

struct

timeval tv_out;

tv_out.tv_sec = 3

; tv_out.tv_usec = 0

;

//setsockopt(sock, sol_socket, so_rcvtimeo, &tv_out, sizeof(tv_out));

struct timeval tv; //

設定select 超時時間

發現ulog用起來蠻舒服的,幾個msh常用命令來設定日誌級別、標籤、輸出關鍵字、過濾器。相信以後除錯一定還要用。.

ulog_lvl 0 

ulog_lvl 7

ulog_lvl 4

ulog_tag udp

ulog_kw wifi

ulog_filter 

ulog_flush

總結:對於udp而言,伺服器也不知道客戶端的狀態(面向非連線,沒有連線這個東西,因此只剩下判斷那個客戶端控制代碼需要收資料)。

「大多數情況下,tcp伺服器是併發的,udp的伺服器是迭代的「。人說,udp沒有必要使用多路復用。

這個例子也只是起到了超時控制的作用,就當做以後tcp服務端程式設計做個練習實驗吧。

UDP通訊(客戶端)

根據網上的資源修改的 include include include pragma comment lib,ws2 32.lib int client void int main int argc,char argv int client void local.sin family af inet ...

實現UDP客戶端

實現udp客戶端 1 基於連線和無連線 2 對系統資源的要求 tcp多,udp少 3 udp程式結構較簡單 4 流模式與資料報模式 5 tcp保證資料準確性,udp可能丟包,tcp保證資料順序,udp不保證 import socket if name main 1.建立udp客戶端,建立套接字 ud...

udp服務端 客戶端

個數 2的16次方 埠是資料發出或接收的入口 埠的目的 通過埠號找到對應的程序,完成資料的通訊 著名埠0 1023 這是建立了乙個基於udp協議的服務端 import socket todo 1.0 建立了乙個套接字,用來連線客戶端,傳送與接收資料 udp server socket.socket ...