
一、概述
要編寫通過計算機網(wǎng)絡通信的程序,首先要確定這些程序相互通信使用的協(xié)議,通常使用TCP或UDP協(xié)議族。TCP是面向連接的傳輸協(xié)議,建立連接時需要經(jīng)過三次握手,斷開連接時需要經(jīng)過四次握手,中間傳輸數(shù)據(jù)也要回復ACK包進行確認。而UDP是非連接的傳輸協(xié)議,沒有建立連接和斷開連接的過程,它只是簡單的把數(shù)據(jù)丟到網(wǎng)絡中,也不明確區(qū)分服務器和客戶端。因此TCP比UDP協(xié)議更加可靠,且TCP和UDP編程大致相同,所以本文就以TCP協(xié)議為例,建立圖 1所示的基本客戶/服務器網(wǎng)絡模型,進行通信。
圖 1 基本服務器-客戶端模型
二、基本套接字編程
圖 2給出了一對客戶與服務器進程之間發(fā)生的典型事件的時間表。服務器首先啟動,稍后客戶端啟動連接到服務器。所有的客戶和服務器都從調(diào)用socket開始,它返回套接字描述符;客戶隨后調(diào)用connect,服務器則調(diào)用bind、listen和accept;建立連接之后調(diào)用send、recv函數(shù)進行數(shù)據(jù)傳輸。數(shù)據(jù)傳輸完成后,套接字使用標準的closesocket函數(shù)關(guān)閉。
圖 2 基本客戶/服務器程序的套接字函數(shù)
● socket()函數(shù):指定期望的通信協(xié)議類型(使用IPv4的TCP、使用IPv6的UDP等)創(chuàng)建套接字。
● blind()函數(shù):將套接字與本地的IP地址和端口綁定。
● connect()函數(shù):客戶端向服務器發(fā)出連接請求。
● listen()函數(shù):僅服務器調(diào)用,使套接字進入被動監(jiān)聽狀態(tài)。所謂被動監(jiān)聽是指當沒有客戶端請求時,套接字處于“睡眠”狀態(tài),只有當接收到客戶端請求時,套接字才會被喚醒來響應請求。
● accept()函數(shù):當套接字處于監(jiān)聽狀態(tài)時,可以通過aceept函數(shù)來接收客戶端的請求。
● send/recv()函數(shù):發(fā)送和接收函數(shù)。
● closesocket()函數(shù):關(guān)閉套接字,回收資源。
三、網(wǎng)絡連接與配置
socket通信程序基于網(wǎng)絡之上,常規(guī)的開發(fā)板一般都攜帶以太網(wǎng)外設(shè),所以本文以以太網(wǎng)為例,配置開發(fā)板的網(wǎng)絡連接。
首先需要用網(wǎng)線連接開發(fā)板的網(wǎng)口和電腦,來建立網(wǎng)絡的物理連接。其次在aworks sdk包中的aw_prj_params.h文件中打開以太網(wǎng)設(shè)備宏,如程序清單 1。
#define AW_DEV_IMX1050_ENET /**< \brief iMX1050 ENET (有線網(wǎng)卡) */
程序清單 1 打開以太網(wǎng)配置宏
再次在awbl_hwconf_imx1050_enet.h文件中配置以太網(wǎng)的IP地址、子網(wǎng)掩碼和網(wǎng)關(guān),并關(guān)閉dhcp,使用靜態(tài)的IP地址,如程序清單 2。
aw_local char *__get_ipaddr (void)
{
return "192.168.1.10";
}
aw_local char *__get_netmsk (void)
{
return "255.255.255.0";
}
aw_local char *__get_gateway (void)
{
return "192.168.1.1";
}
. . . .
aw_local bool_t __get_dhcp_en (void)
{
return FALSE;
}
程序清單 2 IP地址設(shè)置
最后修改電腦為靜態(tài)IP地址并與開發(fā)板IP地址位于同一網(wǎng)段。
四、TCP客戶端實例
按照基本的套接字編程流程,建立一個客戶端,我們只需要調(diào)用socket、connect、send、recv、closesocket函數(shù)即可,如程序清單 3,首先使用socket創(chuàng)建一個TCP類型的套接字,再調(diào)用connect連接到已指定的服務器(IP地址為192.168.1.34、端口號為4000),當服務器端接收客戶端的連接請求后,connect函數(shù)退出阻塞狀態(tài),進入循環(huán),再在循環(huán)中調(diào)用send函數(shù)向服務器發(fā)送數(shù)據(jù),調(diào)用recv函數(shù)(阻塞)接收數(shù)據(jù)。當數(shù)據(jù)傳輸完成后,使用closesocket關(guān)閉連接,回收資源。
程序清單 3 回聲客戶端程序
#include "aworks.h"
#include "aw_delay.h"
#include "aw_task.h"
#include "net/aw_net.h"
#include "net/aw_sockets.h"
#include
/* 客戶端訪問的服務器IP地址 */
#define REMOTE_SERVER_ADDR "192.168.1.34" /* 對應服務器的 IP 地址,用戶需要根據(jù)具體創(chuàng)建的服務端的IP地址修改 */
#define REMOTE_SERVER_PORT 4000 /* 客戶端訪問服務器端口 */
/**
* \brief net 示例程序入口
* \return 無
*/
void demo_tcp_client_entry (void)
{
struct sockaddr_in server_addr;
int rcv_len;
int sock;
char net_buf[1500];
memset(net_buf,'\0',1500);
/* 設(shè)置客戶端訪問的服務器IP地址、端口號 */
inet_aton(REMOTE_SERVER_ADDR, &server_addr.sin_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(REMOTE_SERVER_PORT);
server_addr.sin_len = sizeof(server_addr);
aw_kprintf("TCP client: connecting...\r\n");
while(1) {
sock = socket(AF_INET, SOCK_STREAM, 0);/* 創(chuàng)建socket套接字 */
if (sock < 0) {
aw_kprintf("TCP server socket failed!\r\n");
return;
}
/* 連接服務器 */
if (0 == connect(sock, (struct sockaddr *) &server_addr, sizeof(server_addr))) {
aw_kprintf("TCP client: connected.\r\n");
while(1) {
send(sock, "hello,i'm tcp client.", 21, 0); /* 向服務器發(fā)送數(shù)據(jù) */
rcv_len = recv(sock, net_buf, sizeof(net_buf), 0);/* 接收服務器發(fā)送的數(shù)據(jù) */
if (rcv_len <= 0) {
aw_kprintf("TCP client: disconnect. ret=%d, err=%d\r\n", rcv_len, errno);
break;
}
aw_kprintf("recv:%s\r\n",net_buf);/*打印數(shù)據(jù)*/
memset(net_buf,'\0',1500);/*清空緩存區(qū)*/
}
}
closesocket(sock); /* 關(guān)閉此連接 */
aw_mdelay(1000);
}
}
程序編寫完成后,我們使用TCP上位機軟件測試。打開TCP調(diào)試軟件,如圖 3。創(chuàng)建服務器,如圖 4。最后啟動服務器,如圖 5。
圖 3 TCP調(diào)試工具
圖 4 創(chuàng)建服務器
圖 5 啟動服務器
以上步驟就緒后,在主程序中調(diào)用demo_tcp_client_entry()入口函數(shù),編譯、下載程序到開發(fā)板,待程序運行之后,可以在shell界面看到TCP客戶端連接成功,如圖 6,此時在上位機軟件上可以看到建立的TCP連接,在發(fā)送區(qū)域向客戶端發(fā)送數(shù)據(jù),在接收區(qū)將看到客戶端回發(fā)的數(shù)據(jù),如圖 7。shell界面打印客戶端收到的數(shù)據(jù),如圖 8。
圖 6 TCP客戶端連接成功
圖 7 服務器數(shù)據(jù)顯示
圖 8 客戶端數(shù)據(jù)打印
五、TCP服務器實例
按照基本的套接字編程流程,建立服務器,我們只需要調(diào)用socket、bind、listen、accept、send、recv、closesocket函數(shù)即可,如程序清單 4,首先使用socket函數(shù)創(chuàng)建TCP類型的套接字,然后調(diào)用bind函數(shù)綁定本地網(wǎng)卡的IP地址和端口號,使用listen監(jiān)聽客戶端的請求,然后accept函數(shù)將阻塞等待客戶端的請求連接,當服務器監(jiān)聽到有客戶端請求連接時,accept退出阻塞狀態(tài),建立連接,進入循環(huán),使用send、recv收發(fā)數(shù)據(jù)。當數(shù)據(jù)傳輸完成后,使用closesocket關(guān)閉連接,回收資源。
程序清單 4 非阻塞服務器
#include "aworks.h"
#include "aw_delay.h"
#include "aw_task.h"
#include "net/aw_net.h"
#include "net/aw_sockets.h"
#include
#define LOCAL_SERVER_PORT 4000 /* 本地服務器端口 */
/* 客戶端訪問的服務器IP地址 */
/**
* \brief net 示例程序入口
* \return 無
*/
void demo_tcp_server_block_entry (void)
{
struct sockaddr_in server_addr, client_addr;
int server_sock, client_sock;
int rcv_len;
char net_buf[1500];
socklen_t len;
memset(net_buf,'\0',1500);
memset(&server_addr, 0, sizeof(server_addr));
memset(&client_addr, 0, sizeof(client_addr));
/*創(chuàng)建socket套接字,使用TCP連接 */
server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (0 > server_sock) {
AW_INFOF(("TCP server socket failed!" ENDL));
return;
}
/*設(shè)置服務器的端口等屬性*/
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(LOCAL_SERVER_PORT); /* 設(shè)置服務器的端口 */
server_addr.sin_addr.s_addr = 0; /* 在所有接口上監(jiān)聽 */
server_addr.sin_len = sizeof(server_addr);
memset(&(server_addr.sin_zero), 8, sizeof(server_addr.sin_zero));
/* 綁定本地接口 */
if (bind(server_sock, (struct sockaddr *) &server_addr, sizeof(server_addr)) < 0) {
AW_INFOF(("TCP server bind failed!" ENDL));
closesocket(server_sock);
return;
}
/* 進入監(jiān)聽模式 */
listen(server_sock, 1);
AW_INFOF(("TCP server: listen on %s:%d" ENDL,
inet_ntoa(server_addr.sin_addr), htons(server_addr.sin_port)));
len = sizeof(struct sockaddr);
for (;;) {
/* 阻塞等待客戶端連接 */
client_sock = accept(server_sock, (struct sockaddr *) &client_addr, &len);
if (client_sock > 0) {
AW_INFOF(("TCP server: client %s:%d connected." ENDL,
inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port)));
for (;;) {
send(client_sock, "hello,i'm tcp server.", 21, 0);/*發(fā)送數(shù)據(jù)*/
rcv_len = recv(client_sock, &net_buf, sizeof(net_buf), 0);/* 接收數(shù)據(jù),阻塞,直到收到數(shù)據(jù) */
if (rcv_len <= 0) {
break;
}
aw_kprintf("recv:%s\r\n",net_buf);/*打印接收得到的數(shù)據(jù)*/
memset(net_buf,'\0',1500);/*清空緩存區(qū)*/
}
closesocket(client_sock); /* 關(guān)閉此連接 */
AW_INFOF(("TCP server: client %s:%d leave." ENDL,
inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port)));
aw_mdelay(5);
}
}
}
程序編寫完成后,將服務器例程入口函數(shù)demo_tcp_server_block_entry()放入主函數(shù)中編譯、下載到開發(fā)板,程序運行后在shell界面可看到服務器已經(jīng)啟動,如圖 9。
圖 9 服務器啟動
服務器啟動后,同樣使用TCP上位機軟件建立客戶端進行測試,指定服務器的IP和端口號,如圖 10。
圖 10 創(chuàng)建客戶端
客戶端創(chuàng)建后,點擊連接,如圖 11。
圖 11 連接到服務器
連接成功后,在發(fā)送區(qū)發(fā)送數(shù)據(jù),接收區(qū)可以看到服務器回發(fā)的數(shù)據(jù),如圖 12所示。此時在串口界面也可以看到服務器收到的數(shù)據(jù),如圖 13。
圖 12 客戶端收發(fā)數(shù)據(jù)
圖 13 服務器收到的數(shù)據(jù)
六、出錯調(diào)試
如果程序運行后,沒有成功建立連接,可按照以下步驟查看網(wǎng)絡狀態(tài):
首先在串口界面調(diào)用 AWorks的Shell命令ip addr,查看以太網(wǎng)IP地址是否配置成功,如圖 14。然后使用ping命令測試網(wǎng)絡通信是否正常,如圖 15。
圖 14 查看ip地址
圖 15 測試網(wǎng)絡是否正常
聲明:本內(nèi)容為作者獨立觀點,不代表電源網(wǎng)。本網(wǎng)站原創(chuàng)內(nèi)容,如需轉(zhuǎn)載,請注明出處;本網(wǎng)站轉(zhuǎn)載的內(nèi)容(文章、圖片、視頻)等資料版權(quán)歸原作者所有。如我們采用了您不宜公開的文章或圖片,未能及時和您確認,避免給雙方造成不必要的經(jīng)濟損失,請電郵聯(lián)系我們,以便迅速采取適當處理措施;歡迎投稿,郵箱∶editor@netbroad.com。
電機制氧-剖析便攜制氧機的工作原理 | 22-11-15 14:53 |
---|---|
嵌入式軟件中的“樂高”— | 22-11-15 14:47 |
亥姆霍茲線圈新一代供電電源解決方案 | 21-01-21 16:00 |
基于S32K的EDR解決方案 | 20-12-07 10:48 |
消除電摩充電隱患,1分鐘get妙招 | 20-10-12 17:07 |
微信關(guān)注 | ||
![]() |
技術(shù)專題 | 更多>> | |
![]() |
技術(shù)專題之EMC |
![]() |
技術(shù)專題之PCB |