微软公司宣布不再支持你正在使用的 IE浏览器,这会严重影响浏览网页,请使用微软最新的Edge浏览器
厂商专区
产品/技术
应用分类

AWorks编程——socket通信

2019-07-25 14:37 来源:致远电子 编辑:电源网

一、概述

要编写通过计算机网络通信的程序,首先要确定这些程序相互通信使用的协议,通常使用TCP或UDP协议族。TCP是面向连接的传输协议,建立连接时需要经过三次握手,断开连接时需要经过四次握手,中间传输数据也要回复ACK包进行确认。而UDP是非连接的传输协议,没有建立连接和断开连接的过程,它只是简单的把数据丢到网络中,也不明确区分服务器和客户端。因此TCP比UDP协议更加可靠,且TCP和UDP编程大致相同,所以本文就以TCP协议为例,建立图 1所示的基本客户/服务器网络模型,进行通信。

AWorks编程——socket通信

图 1 基本服务器-客户端模型

二、基本套接字编程

图 2给出了一对客户与服务器进程之间发生的典型事件的时间表。服务器首先启动,稍后客户端启动连接到服务器。所有的客户和服务器都从调用socket开始,它返回套接字描述符;客户随后调用connect,服务器则调用bind、listen和accept;建立连接之后调用send、recv函数进行数据传输。数据传输完成后,套接字使用标准的closesocket函数关闭。

AWorks编程——socket通信

图 2 基本客户/服务器程序的套接字函数

● socket()函数:指定期望的通信协议类型(使用IPv4的TCP、使用IPv6的UDP等)创建套接字。

● blind()函数:将套接字与本地的IP地址和端口绑定。

● connect()函数:客户端向服务器发出连接请求。

● listen()函数:仅服务器调用,使套接字进入被动监听状态。所谓被动监听是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被唤醒来响应请求。

● accept()函数:当套接字处于监听状态时,可以通过aceept函数来接收客户端的请求。

● send/recv()函数:发送和接收函数。

● closesocket()函数:关闭套接字,回收资源。

三、网络连接与配置

socket通信程序基于网络之上,常规的开发板一般都携带以太网外设,所以本文以以太网为例,配置开发板的网络连接。

首先需要用网线连接开发板的网口和电脑,来建立网络的物理连接。其次在aworks sdk包中的aw_prj_params.h文件中打开以太网设备宏,如程序清单 1。

#define AW_DEV_IMX1050_ENET             /**< \brief iMX1050 ENET (有线网卡) */

程序清单 1 打开以太网配置宏

再次在awbl_hwconf_imx1050_enet.h文件中配置以太网的IP地址、子网掩码和网关,并关闭dhcp,使用静态的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地址设置

最后修改电脑为静态IP地址并与开发板IP地址位于同一网段。

四、TCP客户端实例

按照基本的套接字编程流程,建立一个客户端,我们只需要调用socket、connect、send、recv、closesocket函数即可,如程序清单 3,首先使用socket创建一个TCP类型的套接字,再调用connect连接到已指定的服务器(IP地址为192.168.1.34、端口号为4000),当服务器端接收客户端的连接请求后,connect函数退出阻塞状态,进入循环,再在循环中调用send函数向服务器发送数据,调用recv函数(阻塞)接收数据。当数据传输完成后,使用closesocket关闭连接,回收资源。

程序清单 3 回声客户端程序

#include "aworks.h"

#include "aw_delay.h"

#include "aw_task.h"

#include "net/aw_net.h"

#include "net/aw_sockets.h"

#include <string.h>


/* 客户端访问的服务器IP地址 */

#define REMOTE_SERVER_ADDR          "192.168.1.34"      /* 对应服务器的 IP 地址,用户需要根据具体创建的服务端的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);


   /* 设置客户端访问的服务器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);/* 创建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); /* 向服务器发送数据 */

               rcv_len = recv(sock, net_buf, sizeof(net_buf), 0);/* 接收服务器发送的数据 */

               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);/*打印数据*/

               memset(net_buf,'\0',1500);/*清空缓存区*/

           }

       }


       closesocket(sock); /* 关闭此连接 */

       aw_mdelay(1000);

   }

}

程序编写完成后,我们使用TCP上位机软件测试。打开TCP调试软件,如图 3。创建服务器,如图 4。最后启动服务器,如图 5。

AWorks编程——socket通信

图 3 TCP调试工具

AWorks编程——socket通信

图 4 创建服务器

AWorks编程——socket通信

图 5 启动服务器

以上步骤就绪后,在主程序中调用demo_tcp_client_entry()入口函数,编译、下载程序到开发板,待程序运行之后,可以在shell界面看到TCP客户端连接成功,如图 6,此时在上位机软件上可以看到建立的TCP连接,在发送区域向客户端发送数据,在接收区将看到客户端回发的数据,如图 7。shell界面打印客户端收到的数据,如图 8。

AWorks编程——socket通信

图 6 TCP客户端连接成功

7

图 7 服务器数据显示

AWorks编程——socket通信

图 8 客户端数据打印

五、TCP服务器实例

按照基本的套接字编程流程,建立服务器,我们只需要调用socket、bind、listen、accept、send、recv、closesocket函数即可,如程序清单 4,首先使用socket函数创建TCP类型的套接字,然后调用bind函数绑定本地网卡的IP地址和端口号,使用listen监听客户端的请求,然后accept函数将阻塞等待客户端的请求连接,当服务器监听到有客户端请求连接时,accept退出阻塞状态,建立连接,进入循环,使用send、recv收发数据。当数据传输完成后,使用closesocket关闭连接,回收资源。

程序清单 4 非阻塞服务器

#include "aworks.h"

#include "aw_delay.h"

#include "aw_task.h"


#include "net/aw_net.h"

#include "net/aw_sockets.h"


#include <string.h>


#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));


   /*创建socket套接字,使用TCP连接 */

   server_sock = socket(AF_INET, SOCK_STREAM, 0);

   if (0 > server_sock) {

       AW_INFOF(("TCP server socket failed!" ENDL));

       return;

   }


   /*设置服务器的端口等属性*/

   server_addr.sin_family = AF_INET;

   server_addr.sin_port = htons(LOCAL_SERVER_PORT); /* 设置服务器的端口 */

   server_addr.sin_addr.s_addr = 0; /* 在所有接口上监听 */

   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;

   }


   /* 进入监听模式 */

   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);/*发送数据*/

                   rcv_len = recv(client_sock, &net_buf, sizeof(net_buf), 0);/* 接收数据,阻塞,直到收到数据 */

                   if (rcv_len <= 0) {

                       break;

                   }

                   aw_kprintf("recv:%s\r\n",net_buf);/*打印接收得到的数据*/

                   memset(net_buf,'\0',1500);/*清空缓存区*/

           }


           closesocket(client_sock); /* 关闭此连接 */

           AW_INFOF(("TCP server: client %s:%d leave." ENDL,

                   inet_ntoa(client_addr.sin_addr), htons(client_addr.sin_port)));


           aw_mdelay(5);


       }

   }

}

程序编写完成后,将服务器例程入口函数demo_tcp_server_block_entry()放入主函数中编译、下载到开发板,程序运行后在shell界面可看到服务器已经启动,如图 9。

AWorks编程——socket通信

图 9 服务器启动

服务器启动后,同样使用TCP上位机软件建立客户端进行测试,指定服务器的IP和端口号,如图 10。

10

图 10 创建客户端

客户端创建后,点击连接,如图 11。

AWorks编程——socket通信

图 11 连接到服务器

连接成功后,在发送区发送数据,接收区可以看到服务器回发的数据,如图 12所示。此时在串口界面也可以看到服务器收到的数据,如图 13。

AWorks编程——socket通信

图 12 客户端收发数据

AWorks编程——socket通信

图 13 服务器收到的数据

六、出错调试

如果程序运行后,没有成功建立连接,可按照以下步骤查看网络状态:

首先在串口界面调用 AWorks的Shell命令ip addr,查看以太网IP地址是否配置成功,如图 14。然后使用ping命令测试网络通信是否正常,如图 15。

AWorks编程——socket通信

图 14 查看ip地址

AWorks编程——socket通信

图 15 测试网络是否正常

声明:本内容为作者独立观点,不代表电源网。本网站原创内容,如需转载,请注明出处;本网站转载的内容(文章、图片、视频)等资料版权归原作者所有。如我们采用了您不宜公开的文章或图片,未能及时和您确认,避免给双方造成不必要的经济损失,请电邮联系我们,以便迅速采取适当处理措施;欢迎投稿,邮箱∶editor@netbroad.com。

微信关注
技术专题 更多>>
研发工程师的工具箱
智慧生活 创新未来

头条推荐

电子行业原创技术内容推荐
客服热线
服务时间:周一至周五9:00-18:00
微信关注
获取一手干货分享
免费技术研讨会
editor@netbroad.com
400-003-2006