當(dāng)前位置:首頁 > 公眾號精選 > 嵌入式大雜燴
[導(dǎo)讀]點擊上方「嵌入式大雜燴」,選擇「置頂公眾號」第一時間查看嵌入式筆記! 前言 關(guān)于socket的筆記,之前已經(jīng)有分享過兩篇相關(guān)的文章: 【socket筆記】TCP、UDP通信總結(jié) 【socket應(yīng)用】基于C語言的天氣客戶端的實現(xiàn) 本篇筆記我們再來一起回顧一下socket相關(guān)的知

點擊上方「嵌入式大雜燴」,選擇「置頂公眾號」第一時間查看嵌入式筆記!

前言

關(guān)于socket的筆記,之前已經(jīng)有分享過兩篇相關(guān)的文章:

【socket筆記】TCP、UDP通信總結(jié)

【socket應(yīng)用】基于C語言的天氣客戶端的實現(xiàn)

本篇筆記我們再來一起回顧一下socket相關(guān)的知識:我們的開發(fā)板作為TCP客戶端,與TCP服務(wù)端程序進(jìn)行通信。

準(zhǔn)備相關(guān)工程

  • 硬件:小熊派開發(fā)板。
  • 軟件:STM32+RT-Thread
  • 開發(fā)工具:RT-Thread Studio V1.1.0。

實驗前提是我們的開發(fā)板與我們的PC所處的網(wǎng)絡(luò)環(huán)境在同一網(wǎng)段內(nèi)。

我們的開發(fā)板聯(lián)網(wǎng)模塊時ESP8266。這里需要使用RTT的at_device軟件包,這在之前的筆記中已經(jīng)有介紹:【RT-Thread筆記】onenet軟件包的使用。

RT-Thread的網(wǎng)絡(luò)框架

在編寫代碼之前有必要先了解一下RT-Thread的網(wǎng)絡(luò)框架結(jié)構(gòu)(圖片來源:RT-Thread官網(wǎng)):


從下往上看:

第 1 層:與硬件相關(guān)的一些網(wǎng)絡(luò)模塊,這里我們用的是ESP8266。

第 2~4 層:一些中間層。本次實驗中我們可以不用深究,我們把這幾層看做一個黑盒子,先不用管里面的實現(xiàn)。有精力的朋友可以去研究初學(xué)朋友暫時先別去碰,碰就是勸退。。。不過也可以稍微了解一些這幾層是什么。

第 2 層是協(xié)議棧層。這些是一些輕量型的、用于嵌入式中的TCP/IP 協(xié)議棧 。

第 3 層是網(wǎng)卡層。通過 netdev 網(wǎng)卡層用戶可以統(tǒng)一管理各個網(wǎng)卡信息和網(wǎng)絡(luò)連接狀態(tài),并且可以使用統(tǒng)一的網(wǎng)卡調(diào)試命令接口。

第 4 層是SAL 套接字抽象層。通過它 RT-Thread 系統(tǒng)能夠適配下層不同的網(wǎng)絡(luò)協(xié)議棧,并提供給上層統(tǒng)一的網(wǎng)絡(luò)編程接口,方便不同協(xié)議棧的接入。

第 5 層應(yīng)用層標(biāo)準(zhǔn)socket接口。其提供一套標(biāo)準(zhǔn) BSD Socket API。所謂標(biāo)準(zhǔn)就是我們在RT-Thread應(yīng)用編程中用的網(wǎng)絡(luò)接口與在PC上進(jìn)行網(wǎng)絡(luò)編程所用的接口函數(shù)是一樣的,如:


有了這樣的一套標(biāo)準(zhǔn) BSD Socket API,我們的程序就可以在 PC 上編寫、調(diào)試:


然后再移植相關(guān)代碼到 RT-Thread 操作系統(tǒng)上,這給我們提供了很大的便利。

其中,第4層和第5層在在代碼中是用宏來關(guān)聯(lián)起來的:


更多的關(guān)于RT-Thread的網(wǎng)絡(luò)框架介紹可以查看官網(wǎng)文檔:

https://www.rt-thread.org/document/site/programming-manual/sal/sal/#

下面開始編寫測試代碼,首先我們需要清楚一個TCP客戶端-服務(wù)端模型

編寫代碼

(1)編寫TCP客戶端代碼(開發(fā)板代碼)

我們這里編寫的客戶端測試代碼就是按照上面那個圖來一步一步的編寫的:

1、創(chuàng)建一個socket

2、連接服務(wù)端

3、發(fā)送數(shù)據(jù)

4、阻塞等待接收數(shù)據(jù)

5、關(guān)閉連接

①創(chuàng)建一個socket

用到的接口:

int socket(int domain, int type, int protocol);

我們創(chuàng)建socket相關(guān)的代碼如下:

/* 創(chuàng)建一個socket,類型是SOCKET_STREAM, TCP類型 */
if ((sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1)
{
    /* 創(chuàng)建socket失敗 */
    rt_kprintf("Socket error\n");
    return -1;
}

domain / 協(xié)議族類型:

  • AF_INET:IPv4
  • AF_INET6:IPv6

type / 協(xié)議類型:

  • SOCK_STREAM:流套接字
  • SOCK_DGRAM:數(shù)據(jù)報套接字
  • SOCK_RAW:原始套接字

protocol / 傳輸協(xié)議

  • IPPROTO_TCP
  • IPPROTO_UDP
  • ......

②連接服務(wù)端

用到的接口:

int connect(int s, const struct sockaddr *name, socklen_t namelen);

我們連接服務(wù)端相關(guān)的代碼如下:

左右滑動查看全部代碼>>>

/* 從終端獲取URL */
url = argv[1];

/* 從終端獲取端口并轉(zhuǎn)為無符號數(shù)據(jù) */
port = strtoul(argv[2], 010);

/* 通過函數(shù)入口參數(shù)url獲得host地址(如果是域名,會做域名解析) */
host = gethostbyname(url);

/* 初始化預(yù)連接的服務(wù)端地址 */
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr = *((struct in_addr *)host->h_addr);
rt_memset(&(server_addr.sin_zero), 0sizeof(server_addr.sin_zero));

/* 連接到服務(wù)端 */
if (connect(sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
    /* 連接失敗 */
    rt_kprintf("Connect fail!\n");
    closesocket(sock_fd);
    return -1;
}
else
{
    /* 連接成功 */
    rt_kprintf(">>>>>>>>>>>>Connect server(%s %d) success!\n", url, port);
}

③發(fā)送數(shù)據(jù)

用到的接口:

int send(int s, const void *dataptr, size_t size, int flags);

我們發(fā)送數(shù)據(jù)相關(guān)的代碼如下:

 /* 發(fā)送數(shù)據(jù) */
 if (send(sock_fd, argv[3], strlen(argv[3]), 0) < 0)
 {
     /* 發(fā)送失敗,關(guān)閉這個連接 */
     closesocket(sock_fd);
     rt_kprintf("\nsend error,close the socket.\r\n");
 }
 else
 {
     /* 發(fā)送成功 */
     rt_kprintf(">>>>>>>>>>>>Send data(%s) to server success!\n", argv[3]);
 }

④接收數(shù)據(jù)

用到的接口:

int recv(int s, void *mem, size_t len, int flags);

我們接收數(shù)據(jù)的相關(guān)代碼如下:

/* 等待服務(wù)端發(fā)送過來的數(shù)據(jù) */
if (recv(sock_fd, recv_buf, 1000) < 0)
{
    /* 接收失敗,關(guān)閉這個連接 */
    closesocket(sock_fd);
    rt_kprintf("\nreceived error,close the socket.\r\n");
}
else
{
    /* 接收成功,打印收到的數(shù)據(jù) */
    rt_kprintf(">>>>>>>>>>>>Recv data from server: %s\n",recv_buf);
}

⑤關(guān)閉連接

用到的接口:

int closesocket(int s);

(2)編寫TCP服務(wù)端代碼(PC機(jī))

這里提供的是Windows環(huán)境下的TCP服務(wù)端程序代碼,編寫思路也是按照上面的TCP客戶端-服務(wù)端模型來的,相關(guān)接口就不詳細(xì)列舉了,直接貼代碼吧:

左右滑動查看全部代碼>>>

/* 程序:Windows環(huán)境下的TCP服務(wù)端程序
 gcc編譯命令:gcc tcp_server.c -lwsock32 -o tcp_server.exe
 
 微信公眾號:嵌入式大雜燴
 作者:ZhengN
*/


#include <stdio.h>
#include <winsock2.h>

#define BUF_LEN  100

int main(void)
{
 WSADATA wd;
 SOCKET ServerSock, ClientSock;
 char Buf[BUF_LEN] = {0};
 SOCKADDR ClientAddr;
 SOCKADDR_IN ServerSockAddr;
 int addr_size = 0, recv_len = 0;
 
 /* sock需要 */
 WSAStartup(MAKEWORD(2,2),&wd);  
 
 printf("===============這是一個TCP服務(wù)端程序==============\n");
 
 /* 創(chuàng)建服務(wù)端socket */
 if (-1 == (ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))
 {
  printf("socket error!\n");
  exit(1);
 }
 
 /* 設(shè)置服務(wù)端信息 */
    memset(&ServerSockAddr, 0sizeof(ServerSockAddr));   // 給結(jié)構(gòu)體ServerSockAddr清零
    ServerSockAddr.sin_family = AF_INET;        // 使用IPv4地址
    ServerSockAddr.sin_addr.s_addr = inet_addr("192.168.1.101");// 本機(jī)IP地址
    ServerSockAddr.sin_port = htons(1314);       // 端口
 
 /* 綁定套接字 */
    if (-1 == bind(ServerSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR)))
 {
  printf("bind error!\n");
  exit(1);
 }
  
 /* 進(jìn)入監(jiān)聽狀態(tài) */
 if (-1 == listen(ServerSock, 10))
 {
  printf("listen error!\n");
  exit(1);
 }
 
 addr_size = sizeof(SOCKADDR);

 while (1)
 {
  /* 監(jiān)聽客戶端請求,accept函數(shù)返回一個新的套接字,發(fā)送和接收都是用這個套接字 */
  if (-1 == (ClientSock = accept(ServerSock, (SOCKADDR*)&ClientAddr, &addr_size)))
  {
   printf("socket error!\n");
   exit(1);
  }

  /* 接受客戶端的返回數(shù)據(jù) */
  int recv_len = recv(ClientSock, Buf, BUF_LEN, 0);
  printf("客戶端發(fā)送過來的數(shù)據(jù)為:%s\n", Buf);
  
  /* 發(fā)送數(shù)據(jù)到客戶端 */
  send(ClientSock, Buf, recv_len, 0);
  
  /* 關(guān)閉客戶端套接字 */
  closesocket(ClientSock);
  
  /* 清空緩沖區(qū) */
  memset(Buf, 0, BUF_LEN);  
 }

 /*如果有退出循環(huán)的條件,這里還需要清除對socket庫的使用*/
 /* 關(guān)閉服務(wù)端套接字 */
 //closesocket(ServerSock);
    /* WSACleanup();*/

 return 0;
}

驗證、分析

1、PC端自驗證

我們使用我們自己用C語言編寫的客戶端、服務(wù)端程序進(jìn)行驗證:

2、STM32<-->PC

(1)STM32作為客戶端,與PC端我們自己編寫的服務(wù)端程序進(jìn)行通信。


tcp_client命令是我們使用MSH_CMD_EXPORT宏導(dǎo)出的命令,如:

MSH_CMD_EXPORT(tcp_client, tcp_client sample);

我們可在終端按下TAB鍵或者輸入help來查看有沒有導(dǎo)出成功:


我們的測試命令格式為:

tcp_client URL PORT DATA

其中,URL 參數(shù)代表網(wǎng)址或IP地址,這里是局域網(wǎng)內(nèi)的TCP通信測試,所以這個參數(shù)其實就是我們電腦的IP地址,可以在cmd下輸入ipconfig命令進(jìn)行查看:


PORT 參數(shù)代表端口。這里要輸入的是服務(wù)端程序綁定的端口號。端口使用16bit進(jìn)行編號,即其范圍為:0~65536。

0~1023 的端口一般由系統(tǒng)分配給特定的服務(wù)程序,例如 Web 服務(wù)的端口號為 80,F(xiàn)TP 服務(wù)的端口號為 21等。

我們這里的服務(wù)端程序端口號可以設(shè)置為1024~65535范圍內(nèi)的隨意一個數(shù)。但要注意的是我們輸入的測試命令中的PORT參數(shù)要與服務(wù)端程序綁定的端口一樣,否則客戶端就連接不上服務(wù)端:


DATA參數(shù)代表我們要發(fā)送給服務(wù)端的數(shù)據(jù)。

需要注意的是,我們在進(jìn)行測試時需要先啟動服務(wù)端程序。如果服務(wù)端程序還未啟動就運行我們的客戶端程序,就會出現(xiàn)連接失敗:


(2)STM32作為客戶端,PC端網(wǎng)絡(luò)調(diào)試助手作為服務(wù)端。


從這個網(wǎng)絡(luò)助手中可以看到在收到數(shù)據(jù)的同時可以顯示出客戶端的IP及端口號。客戶端的端口號是系統(tǒng)隨機(jī)分配的(范圍為:1024~65535):


所以我們不關(guān)心端口號,但是我們可以查看客戶端的IP地址。如:


除了這個串口調(diào)試助手之外,之前也有分享過一個很好用的socket編程調(diào)試工具,有興趣的朋友可移步至:《網(wǎng)絡(luò)調(diào)試助手的簡單使用》進(jìn)行查看。

(3)Python實現(xiàn)服務(wù)端

服務(wù)端程序可以用C、C++、Python等語言來編寫,上面我們用的是C語言。這里我們也來過一把Python癮:

Python代碼:

以下Python代碼來自CSDN博客。博客鏈接:

https://blog.csdn.net/liao392781/article/details/80116600?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase
#coding=utf-8
#創(chuàng)建TCP服務(wù)器
from socket import *
from time import ctime
 
HOST='192.168.1.101' #這個是我的服務(wù)器ip,根據(jù)情況改動
PORT=1314 #我的端口號
BUFSIZ=1024
ADDR=(HOST,PORT)
 
tcpSerSock=socket(AF_INET,SOCK_STREAM) #創(chuàng)服務(wù)器套接字
tcpSerSock.bind(ADDR) #套接字與地址綁定
tcpSerSock.listen(5)  #監(jiān)聽連接,傳入連接請求的最大數(shù),一般為5就可以了
 
while True:
    print('waiting for connection...')
    tcpCliSock,addr =tcpSerSock.accept()
    print('...connected from:',addr)
 
    while True:
        stock_codes = tcpCliSock.recv(BUFSIZ).decode() #收到的客戶端的數(shù)據(jù)需要解碼(python3特性)
        print('stock_codes = ',stock_codes)    #傳入?yún)?shù)stock_codes
        if not stock_codes:
            break
        tcpCliSock.send(('[%s] %s' %(ctime(),stock_codes)).encode())  #發(fā)送給客戶端的數(shù)據(jù)需要編碼(python3特性)
        after_close_simulation = tcpCliSock.recv(BUFSIZ).decode() #收到的客戶端的數(shù)據(jù)需要解碼(python3特性)
        print('after_close_simulation = ',after_close_simulation)    #傳入?yún)?shù)after_close_simulation
        if not after_close_simulation:
            break
        tcpCliSock.send(('[%s] %s' %(ctime(),after_close_simulation)).encode())  #發(fā)送給客戶端的數(shù)據(jù)需要編碼(python3特性) 
 
    tcpCliSock.close()
tcpSerSock.close()

以上就是本次的分享,由于篇幅過長,因此部分代碼沒有全部貼出。若本篇文章相關(guān)代碼,可通過微信 聯(lián)系我進(jìn)行獲取 。
另外,本篇筆記將會同步至我的個人博客: www.lizhengnian.cn ,歡迎來訪。
據(jù)說現(xiàn)在微信公號文章推送規(guī)則有點變化,如果覺得本公眾號文章有用,不妨星標(biāo)置頂本公眾號。

猜你喜歡

基于RT-Thread的智慧路燈案例實驗分享

十年經(jīng)驗工程師為何被裁?

串口打印知多少?

嵌入式百寶箱:第2期
基于Linux、C、JSON、Socket的編程實例(附代碼)

最后

若覺得文章不錯,轉(zhuǎn)發(fā)分享、在看,也是我們繼續(xù)更新的動力。

在公眾號內(nèi)回復(fù)更多資源,可免費獲取嵌入式資料。期待你的關(guān)注~


加好友,回暗號【嵌入式大雜燴】,進(jìn)微信群

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團(tuán))股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉