MS-RTOS 网络

更新时间:
2023-08-09
下载文档

MS-RTOS 网络

本章将介绍 MS-RTOS 网络相关接口的使用。

网络编程接口

MS-RTOS 网络子系统屏蔽了不同网络实现(如 TCP/IP 协议栈和 ESP8266 等有线、无线通信模块)的差异,向上提供了统一的标准的 BSD/socket 套接字的网络编程 API,有效保证了上层物联网应用的跨平台能力,同时不同网络实现能够在同一套系统中共存。

网络相关 API

下表展示了网络操作 API 在两个权限空间下是否可用:

API用户空间内核空间
ms_net_set_impl
ms_net_get_dns_server
ms_net_set_dns_server
gethostname
sethostname
htonl
htons
ntohl
ntohs
inet_addr
inet_aton
inet_ntoa
inet_ntoa_r
inet_ntop
inet_pton
gethostbyname
gethostbyname_r
getaddrinfo
freeaddrinfo
gai_strerror
if_indextoname
if_nametoindex
socket
accept
bind
shutdown
getpeername
getsockname
getsockopt
setsockopt
connect
listen
recv
recvfrom
recvmsg
send
sendto
sendmsg

socket 除了可以使用以上特有的接口操作外,还可以使用 read、write、ioctl、 fcntl、close、select、poll 等 posix 标准和 MS-RTOS 原生的文件接口操作。

ms_net_set_impl()

  • 描述 设置当前进程的网络实现,默认的网络实现为系统第一个注册的网络实现,系统已经注册的网络实现可通过 nets 命令查看

  • 函数原型

ms_err_t ms_net_set_impl(const char *name);
  • 参数
输入/输出参数描述
[in]name网络实现名称
  • 返回值 MS-RTOS 内核错误码

  • 注意事项

  • 示例

ms_net_get_dns_server()

  • 描述 获得指定的 DNS 服务器地址

  • 函数原型

int ms_net_get_dns_server(ms_uint8_t numdns, ip_addr_t *dnsserver);
  • 参数
输入/输出参数描述
[in]numdnsDNS 服务器编号
[out]dnsserverDNS 服务器地址
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

ms_net_set_dns_server()

  • 描述 设置指定的 DNS 服务器地址

  • 函数原型

int ms_net_set_dns_server(ms_uint8_t numdns, const ip_addr_t *dnsserver);
  • 参数
输入/输出参数描述
[in]numdnsDNS 服务器编号
[in]dnsserverDNS 服务器地址
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

gethostname()

  • 描述 获取主机名称

  • 函数原型

int gethostname(char *name, size_t len);
  • 参数
输入/输出参数描述
[out]name主机名称缓存区
[in]len主机名称缓存区的大小
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

sethostname()

  • 描述 设置主机名称

  • 函数原型

int sethostname(const char *name, size_t len);
  • 参数
输入/输出参数描述
[in]name主机名称
[in]len主机名称的最大长度
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

htonl()

  • 描述 长整数主机字节序转网络字节序

  • 函数原型

ms_uint32_t htonl(ms_uint32_t x);
  • 参数
输入/输出参数描述
[in]x需要转换的长整数
  • 返回值 转换后的长整数

  • 注意事项

  • 示例

htons()

  • 描述 short 类型主机字节序转网络字节序

  • 函数原型

ms_uint16_t htons(ms_uint16_t x);
  • 参数
输入/输出参数描述
[in]x需要转换的 short 类型数
  • 返回值 转换后的 short 类型数

  • 注意事项

  • 示例

ntohl()

  • 描述 长整数网络字节序转主机字节序

  • 函数原型

ms_uint32_t ntohl(ms_uint32_t x);
  • 参数
输入/输出参数描述
[in]x需要转换的长整数
  • 返回值 转换后的长整数

  • 注意事项

  • 示例

ntohs()

  • 描述 short 类型网络字节序转主机字节序

  • 函数原型

ms_uint16_t ntohs(ms_uint16_t x);
  • 参数
输入/输出参数描述
[in]x需要转换的 short 类型数
  • 返回值 转换后的 short 类型数

  • 注意事项

  • 示例

inet_addr()

  • 描述 点分十进制字符串转换为 32 位网络字节序二进制值

  • 函数原型

ms_uint32_t inet_addr(const char *cp);
  • 参数
输入/输出参数描述
[in]cp点分十进制地址,如 192.168.1.15
  • 返回值 成功时返回 32 位二进制的网络字节序地址,失败返回 INADDR_NONE

  • 注意事项 此函数存在一个问题,不能表示全部有效的 IP 地址(从 0.0.0.0 到 255.255.255.255)

  • 示例

inet_aton()

  • 描述 点分十进制字符串转换为 32 位网络字节序二进制值

  • 函数原型

int inet_aton(const char *cp, in_addr_t *addr);
  • 参数
输入/输出参数描述
[in]cp点分十进制地址,如 192.168.1.15
[out]addr用于保存网络字节序二进制值的缓存地址
  • 返回值 字符串有效时返回 1, 无效时返回 0

  • 注意事项

  • 示例

inet_ntoa()

  • 描述 32 位网络字节序二进制值转换为点分十进制字符串

  • 函数原型

char *inet_ntoa(const in_addr_t *addr);
  • 参数
输入/输出参数描述
[in]addr32 位网络字节序地址
  • 返回值 正确时返回字符串指针,错误时返回 MS_NULL

  • 注意事项 此函数是不可重入的,建议使用可重入版本 inet_ntoa_r

  • 示例

inet_ntoa_r()

  • 描述 32 位网络字节序二进制值转换为点分十进制字符串

  • 函数原型

char *inet_ntoa_r(const in_addr_t *addr, char *buf, int buflen);
  • 参数
输入/输出参数描述
[in]addr32 位网络字节序地址
[in]buf点分十进制字符串缓冲区
[in]buflen缓冲区长度
  • 返回值 正确时返回字符串指针,错误时返回 MS_NULL

  • 注意事项

  • 示例

inet_ntop()

  • 描述 将二进制数值转换为字符串表达式

  • 函数原型

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
  • 参数
输入/输出参数描述
[in]af必须是 AF_INET 或者 AF_INET6。其他地址簇不被支持,且返回错误
[in]src二进制数值
[in]dst用于保存转换后的地址字符串
[in]sizedst 缓冲区的大小
  • 返回值 正确时返回地址串的指针,错误时返回 MS_NULL

  • 注意事项

  • 示例

inet_pton()

  • 描述 将字符串表达式转换为二进制数值

  • 函数原型

int  inet_pton(int af, const char *src, void *dst);
  • 参数
输入/输出参数描述
[in]af必须是 AF_INET 或者 AF_INET6。其他地址簇不被支持,且返回错误
[in]src地址字符串,如 IPv4 的 192.168.1.15
[in]dst用于保存二进制数值结果
  • 返回值 正确时返回 1, 错误时返回 -1,输入不是有效的表达格式时返回 0

  • 注意事项

  • 示例

gethostbyname()

  • 描述 用域名或者主机名获取地址

  • 函数原型

struct hostent *gethostbyname(const char *name);
  • 参数
输入/输出参数描述
[in]name域名或者主机名
  • 返回值 成功返回获取的网络地址,失败时返回 MS_NULL,并设置 errno

  • 注意事项 该函数是不可重入的,建议使用可重入版本 gethostbyname_r

  • 示例

gethostbyname_r()

  • 描述 用域名或者主机名获取地址

  • 函数原型

int  gethostbyname_r(const char *name, struct hostent *ret, char *buf,
                       size_t buflen, struct hostent **result, int *h_errnop);
  • 参数
输入/输出参数描述
[in]name域名或者主机名
[in]ret返回的网络地址
[out]buf结果缓冲区
[in]buflen结果缓冲区的长度
[out]result如果成功,指向 ret,如果失败为 MS_NULL
[out]h_errnop存储错误码
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

getaddrinfo()

  • 描述 获取地址信息

  • 函数原型

int  getaddrinfo(const char *nodename, const char *servname, 
                   const struct addrinfo *hints, struct addrinfo **res);
  • 参数
输入/输出参数描述
[in]nodename地址字符串
[in]servname服务名
[in]hints输入地址信息
[out]res结果地址信息
  • 返回值 成功返回 0,失败返回非 0 值

  • 注意事项getaddrinfo 函数获取到的地址结构,需要使用 freeaddrinfo 函数进行释放

  • 示例

freeaddrinfo()

  • 描述 释放地址信息结构

  • 函数原型

void freeaddrinfo(struct addrinfo *ai);
  • 参数
输入/输出参数描述
[in]ai由 getaddrinfo 函数返回的地址信息结构
  • 返回值

  • 注意事项

  • 示例

gai_strerror()

  • 描述 获取网络错误码对应字符串

  • 函数原型

const char *gai_strerror(int error);
  • 参数
输入/输出参数描述
[in]error错误码
  • 返回值 错误类型字符串

  • 注意事项

  • 示例

if_indextoname()

  • 描述 根据网络接口索引获得网络接口名

  • 函数原型

char *if_indextoname(unsigned int ifindex, char *ifname);
  • 参数
输入/输出参数描述
[in]ifindex网络接口索引
[out]ifname网络接口名
  • 返回值 成功返回网络接口名,失败返回 MS_NULL

  • 注意事项

  • 示例

if_nametoindex()

  • 描述 根据网络接口名获得网络接口索引

  • 函数原型

unsigned int if_nametoindex(const char *ifname);
  • 参数
输入/输出参数描述
[in]ifname网络接口名
  • 返回值 网络接口索引

  • 注意事项

  • 示例

socket()

  • 描述 创建一个套接字

  • 函数原型

int socket(int domain, int type, int protocol);
  • 参数
输入/输出参数描述
[in]domain协议域
[in]type套接字类型
[in]protocol协议类型
  • 返回值 成功时返回套接字描述符,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

accept()

  • 描述 仅由 TCP 服务器调用,用于返回一个已完成的连接

  • 函数原型

int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
  • 参数
输入/输出参数描述
[in]s套接字,socket 函数返回
[out]addr返回已连接对端的协议地址结构信息
[out]addrlen返回已连接协议地址结构大小
  • 返回值 成功返回非负已连接套接字描述符,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

bind()

  • 描述 把一个本地协议地址赋予一个套接字,协议地址的含义只取决于协议本身

  • 函数原型

int bind(int s, const struct sockaddr *name, socklen_t namelen);
  • 参数
输入/输出参数描述
[in]s套接字,socket 函数返回
[in]name一个指向特定协议域的 sockaddr 结构体类型指针
[in]namelenname 结构的长度
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

shutdown()

  • 描述 关闭一个套接字

  • 函数原型

int shutdown(int s, int how);
  • 参数
输入/输出参数描述
[in]s需要关闭的套接字
[in]how关闭的方式

how 的取值类型

参数含义
SHUT_RD关闭 socket 上的读功能,后续将不允许 socket 进行读操作。
SHUT_WR关闭 socket 的写功能,后续将不允许 socket 进行写操作。
SHUT_RDWR关闭 socket 的读写功能。
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

getpeername()

  • 描述 用于返回与某个套接字关联的远端协议地址

  • 函数原型

int getpeername(int s, struct sockaddr *name, socklen_t *namelen);
  • 参数
输入/输出参数描述
[in]s套接字,socket 接口返回
[out]name返回已连接对端的协议地址结构信息
[out]namelen用于返回已连接协议地址结构大小
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

getsockname()

  • 描述 用于返回与某个套接字关联的本地协议地址

  • 函数原型

int getsockname(int s, struct sockaddr *name, socklen_t *namelen);
  • 参数
输入/输出参数描述
[in]s套接字,socket 接口返回
[out]name返回本地的协议地址结构信息
[out]namelen用于返回本地地址结构大小
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

getsockopt()

  • 描述 返回套接字选项

  • 函数原型

int getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen);
  • 参数
输入/输出参数描述
[in]s套接字,socket 接口返回
[in]level选项等级
[in]optname选项名
[out]optval选项值
[in]optlen选项长度

套接字选项如下:

选项等级选项名说明数据类型
SOL_SOCKETSO_BROADCAST运行发送广播数据报int
SOL_SOCKETSO_ERROR获取待处理错误并消除int
SOL_SOCKETSO_KEEPALIVE周期性测试连接是否存活int
SOL_SOCKETSO_LINGER若有数据待发送则延迟关闭struct linger
SOL_SOCKETSO_DONTLINGER关闭 SO_LINGER 选项int
SOL_SOCKETSO_RCVBUF接收缓冲区大小int
SOL_SOCKETSO_RCVTIMEO接收超时struct timeval
SOL_SOCKETSO_SNDTIMEO发送超时struct timeval
SOL_SOCKETSO_REUSEADDR允许重用本地地址int
SOL_SOCKETSO_REUSEPORT允许重用本地端口int
SOL_SOCKETSO_TYPE取得套接字类型int
SOL_SOCKETSO_CONTIMEO连接超时struct timeval
IPPROTO_IPIP_TOS服务类型和优先级int
IPPROTO_IPIP_TTL存活时间int
IPPROTO_IPIP_MULTICAST_IF指定外出接口struct in_addr
IPPROTO_IPIP_MULTICAST_TTL指定外出 TTLunsigned char
IPPROTO_IPIP_MULTICAST_LOOP指定是否回馈unsigned char
IPPROTO_IPIP_ADD_MEMBERSHIP加入多播组struct in_mreq
IPPROTO_IPIP_DROP_MEMBERSHIP离开多播组struct in_mreq
IPPROTO_TCPTCP_KEEPALIVE控测对方是否存活前连接闲置秒数int
IPPROTO_TCPTCP_KEEPIDLE对一个连接探测前的允许时间int
IPPROTO_TCPTCP_KEEPINTVL两个探测的时间间隔int
IPPROTO_TCPTCP_KEEPCNT探测的最大次数int
IPPROTO_IPV6IPV6_V6ONLY只允许 IPV6(SylixOS 不支持数据报通信)int
IPPROTO_UDPLITEUDPLITE_SEND_CSCOV执行发送校验和int
IPPROTO_UDPLITEUDPLITE_RECV_CSCOV执行接收校验和int
IPPROTO_RAWIPV6_CHECKSUMIPV6 校验和int
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

setsockopt()

  • 描述 设置套接字选项

  • 函数原型

int setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
  • 参数
输入/输出参数描述
[in]s套接字,socket 接口返回
[in]level选项等级
[in]optname选项名
[in]optval选项值
[in]optlen选项长度
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

connect()

  • 描述 TCP 客户端建立与 TCP 服务器的连接

  • 函数原型

int connect(int s, const struct sockaddr *name, socklen_t namelen);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[in]name一个指向特定协议域的 sockaddr 结构体类型的指针
[in]namelenname 结构的长度
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

listen()

  • 描述 由 TCP 服务器调用,表示可以接受指向该套接字的连接请求

  • 函数原型

int listen(int s, int backlog);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[in]backlog表示对应套接字可以接受的最大连接数
  • 返回值 成功返回 0,失败返回 -1,并设置 errno

  • 注意事项

  • 示例

recv()

  • 描述 接收报文数据

  • 函数原型

ssize_t recv(int s, void *mem, size_t len, int flags);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[out]mem存储接收的数据
[in]len表示读取数据的字节长度
[in]flags指定消息类型
  • 返回值 成功时返回读到的数据字节数,失败时返回 -1,并设置 errno

  • 注意事项

  • 示例

recvfrom()

  • 描述 接收报文数据

  • 函数原型

ssize_t recvfrom(int s, void *mem, size_t len, int flags,
                   struct sockaddr *from, socklen_t *fromlen);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[out]mem存储接收的数据
[in]len表示读取数据的字节长度
[in]flags指定消息类型
[in]from用于表示 UDP 数据报发送者的地址协议
[in]fromlen指定 from 地址大小的指针
  • 返回值 成功时返回读到的数据字节数,失败时返回 -1,并设置 errno

  • 注意事项

  • 示例

recvmsg()

  • 描述 接收报文数据

  • 函数原型

ssize_t recvmsg(int s, struct msghdr *message, int flags);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[out]message接收到的数据结构
[in]flags指定消息类型
struct msghdr {
    void          *msg_name;        // protocol address
    socklen_t      msg_namelen;     // size of protocol address
    struct iovec  *msg_iov;         // scatter/gather array
    int            msg_iovlen;      // elements in msg_iov
    void          *msg_control;     // ancillary data (cmsghdr struct)
    socklen_t      msg_controllen;  // length of ancillary data
    int            msg_flags;       // flags returned by recvmsg()
};
  • 返回值 成功时返回读到的数据字节数,失败时返回 -1,并设置 errno

  • 注意事项

  • 示例

send()

  • 描述 发送报文数据

  • 函数原型

ssize_t send(int s, const void *dataptr, size_t size, int flags);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[in]dataptr存储发送的数据
[in]len表示读取数据的字节长度
[in]flags指定消息类型
  • 返回值 成功时返回发送的数据字节数,失败时返回 -1,并设置 errno

  • 注意事项

  • 示例

sendto()

  • 描述 发送报文数据

  • 函数原型

ssize_t sendto(int s, const void *dataptr, size_t size, int flags,
                 const struct sockaddr *to, socklen_t tolen);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[in]dataptr存储发送的数据
[in]size表示发送数据的字节长度
[in]flags指定消息类型
[in]to用于表示 UDP 数据报发送者的地址协议
[in]tolen指定 to 地址大小的指针
  • 返回值 成功时返回发送的数据字节数,失败时返回 -1,并设置 errno

  • 注意事项

  • 示例

sendmsg()

  • 描述 发送报文数据

  • 函数原型

ssize_t sendmsg(int s, const struct msghdr *message, int flags);
  • 参数
输入/输出参数描述
[in]s套接字,由 socket 接口返回
[in]message发送的数据结构
[in]flags指定消息类型
  • 返回值 成功时返回发送的数据字节数,失败时返回 -1,并设置 errno

  • 注意事项

  • 示例

TCP 回显服务器示例

以下的示例建立了一个 TCP 回显服务器,它监听 8101 端口,PC 机可以使用网络工具,建立一个 TCP 客户端,连接它的 8101 端口,然后发送数据,将收到发送的原样数据:

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>

#define __TCP_ECHO_PORT_SERVER          8101                            /* 服务器端口号                 */
#define __TCP_ECHO_BUFF_SIZE_SERVER     257                             /* 服务器接收缓冲区大小         */

static char             cRecvBuff[__TCP_ECHO_BUFF_SIZE_SERVER] = {0};   /* 接收缓冲区                   */

int main (int argc, char *argv[])
{
    int                 iRet        = -1;
    int                 sockFd      = -1;
    int                 sockFdNew   = -1;
    socklen_t           uiAddrLen   = sizeof(struct sockaddr_in);       /* 地址结构大小                 */
    register ssize_t    sstRecv     = 0;                                /* 接收到的数据长度             */
    struct sockaddr_in  sockaddrinLocal;                                /* 本地地址                     */
    struct sockaddr_in  sockaddrinRemote;                               /* 远端地址                     */

    fprintf(stdout, "TCP echo server start.\r\n");

    sockFd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sockFd < 0) {
        fprintf(stderr, "TCP echo server socket error.\r\n");
        return  (-1);
    }

    /*
     * 初始化本地地址结构
     */
    memset(&sockaddrinLocal, 0, sizeof(sockaddrinLocal));
    sockaddrinLocal.sin_len         = sizeof(struct sockaddr_in);
    sockaddrinLocal.sin_family      = AF_INET;                          /* 地址族                       */
    sockaddrinLocal.sin_addr.s_addr = INADDR_ANY;
    sockaddrinLocal.sin_port        = htons(__TCP_ECHO_PORT_SERVER);    /* 绑定服务器端口               */
    iRet = bind(sockFd, (struct sockaddr *)&sockaddrinLocal, sizeof(sockaddrinLocal));
                                                                        /* 绑定本地地址与端口           */
    if (iRet < 0) {                                                     /* 绑定操作失败                 */
        close(sockFd);                                                  /* 关闭已经创建的 socket        */
        fprintf(stderr, "TCP echo server bind error.\r\n");
        return  (-1);                                                   /* 错误返回                     */
    }

    listen(sockFd, 2);

    sockFdNew = accept(sockFd, (struct sockaddr *)&sockaddrinRemote, &uiAddrLen);
    if (sockFdNew < 0) {
        close(sockFd);                                                  /* 关闭已经创建的 socket        */
        fprintf(stderr, "TCP echo server accept error.\r\n");
        return  (-1);                                                   /* 错误返回                     */
    }

    for (;;) {
        memset(&cRecvBuff[0], 0, __TCP_ECHO_BUFF_SIZE_SERVER);          /* 清空接收缓冲区               */

        sstRecv = read(sockFdNew, (void *)&cRecvBuff[0], __TCP_ECHO_BUFF_SIZE_SERVER);
                                                                        /* 从远端接收数据               */
        if (sstRecv <= 0) {                                             /* 接收数据失败                 */
            if ((errno != ETIMEDOUT) && (errno != EWOULDBLOCK)) {       /* 非超时与非阻塞               */
                 close(sockFdNew);                                      /* 关闭已经连接的 socket        */
                 fprintf(stderr, "TCP echo server recvfrom error.\r\n");
                 return  (-1);                                          /* 错误返回                     */
            }

            continue;                                                   /* 超时或非阻塞后重新运行       */
        }

        cRecvBuff[sstRecv] = 0;
        printf("I recv %s\r\n", cRecvBuff);
                                                                        /* 将回射数据发回远端           */
        write(sockFdNew, (const void *)&cRecvBuff[0], sstRecv);
    }

    return  (0);
}

UDP 多 IP 回显服务器示例

#include <ms_rtos.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>

#define __UDP_ECHO_PORT_SERVER          8101                            /* 服务器端口号                 */
#define __UDP_ECHO_BUFF_SIZE_SERVER     257                             /* 服务器接收缓冲区大小         */
#define __UDP_ECHO_TIMEOUT_SERVER       5

static char             cRecvBuff[__UDP_ECHO_BUFF_SIZE_SERVER];         /* 接收缓冲区                   */

int main (int argc, char **argv)
{
    int                 iRet;
    int                 sockFd1;
    int                 sockFd2;
    int                 sockFd3;
    int                 maxFd;
    socklen_t           uiAddrLen;                                      /* 地址结构大小                 */
    register ssize_t    sstRecv;                                        /* 接收到的数据长度             */
    struct sockaddr_in  sockaddrinLocal;                                /* 本地地址                     */
    struct sockaddr_in  sockaddrinRemote;                               /* 远端地址                     */
    fd_set              rfds;
    struct timeval      tv;
    char                ipAddrString[sizeof("255.255.255.255")];

    /*
     * 创建 socket 1
     */
    sockFd1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockFd1 < 0) {
        fprintf(stderr, "UDP echo server socket error, line %d.\r\n", __LINE__);
        return  (-1);
    }

    /*
     * 设置网络接口 st0 的 mac 地址
     */
    {
        struct ifreq ifreq;

        bzero(&ifreq, sizeof(struct ifreq));

        strcpy(ifreq.ifr_name, "st0");

        ifreq.ifr_hwaddr.sa_data[0] = 0x11;
        ifreq.ifr_hwaddr.sa_data[1] = 0x22;
        ifreq.ifr_hwaddr.sa_data[2] = 0x33;
        ifreq.ifr_hwaddr.sa_data[3] = 0x44;
        ifreq.ifr_hwaddr.sa_data[4] = 0x55;
        ifreq.ifr_hwaddr.sa_data[5] = 0x66;
        ioctl(sockFd1, SIOCSIFHWADDR, &ifreq);

        /*
         * 获取网络接口 st0 的 mac 地址
         */
        ioctl(sockFd1, SIOCGIFHWADDR, &ifreq);
        ms_printf("MAC addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
                  (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[0],
                  (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[1],
                  (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[2],
                  (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[3],
                  (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[4],
                  (ms_uint8_t)ifreq.ifr_hwaddr.sa_data[5]);
    }

    /*
     * 设置网络接口 st0 的 ip 地址
     */
    {
        struct ifreq ifreq;
        struct sockaddr_in *psockaddrin;

        bzero(&ifreq, sizeof(struct ifreq));

        strcpy(ifreq.ifr_name, "st0");

        psockaddrin = (struct sockaddr_in *)&(ifreq.ifr_addr);
        psockaddrin->sin_len         = sizeof(struct sockaddr_in);
        psockaddrin->sin_family      = AF_INET;
        psockaddrin->sin_addr.s_addr = inet_addr("192.168.1.33");
        ioctl(sockFd1, SIOCSIFADDR, &ifreq);

        /*
         * 获取网络接口 st0 的 ip 地址
         */
        ioctl(sockFd1, SIOCGIFADDR, &ifreq);
        inet_ntoa_r(psockaddrin->sin_addr, ipAddrString, sizeof(ipAddrString));
        ms_printf("IP addr: %s\n", ipAddrString);
    }

    /*
     * 为 st0 增加一个 ip, 新增加的网络接口名为 mi0
     */
    {
        struct ifaliasreq ifalias;
        struct sockaddr_in *psockaddrin;

        bzero(&ifalias, sizeof(struct ifaliasreq));

        strcpy(ifalias.ifra_name, "st0");

        /*
         * 设置 ip
         */
        psockaddrin = (struct sockaddr_in *)&(ifalias.ifra_addr);
        psockaddrin->sin_len         = sizeof(struct sockaddr_in);
        psockaddrin->sin_family      = AF_INET;
        psockaddrin->sin_addr.s_addr = inet_addr("192.168.1.100");

        /*
         * 设置子网掩码
         */
        psockaddrin = (struct sockaddr_in *)&(ifalias.ifra_mask);
        psockaddrin->sin_len         = sizeof(struct sockaddr_in);
        psockaddrin->sin_family      = AF_INET;
        psockaddrin->sin_addr.s_addr = inet_addr("255.255.255.0");

        ioctl(sockFd1, SIOCAIFADDR, &ifalias);
    }

    /*
     * socket 1 绑定服务器端口
     */
    memset(&sockaddrinLocal, 0, sizeof(sockaddrinLocal));
    sockaddrinLocal.sin_len         = sizeof(struct sockaddr_in);
    sockaddrinLocal.sin_family      = AF_INET;                          /* 地址族                       */
    sockaddrinLocal.sin_addr.s_addr = inet_addr("192.168.1.33");        /* 绑定到 192.168.1.33          */
    sockaddrinLocal.sin_port        = htons(__UDP_ECHO_PORT_SERVER);    /* 绑定服务器端口               */
    iRet = bind(sockFd1, (struct sockaddr *)&sockaddrinLocal, sizeof(sockaddrinLocal));
    if (iRet < 0) {                                                     /* 绑定操作失败                 */
        fprintf(stderr, "UDP echo server bind error, line %d.\r\n", __LINE__);
        return  (-1);                                                   /* 错误返回                     */
    }

    /*
     * 创建 socket 2
     */
    sockFd2 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockFd2 < 0) {
        fprintf(stderr, "UDP echo server socket error, line %d.\r\n", __LINE__);
        return  (-1);
    }

    /*
     * socket 2 绑定服务器端口
     */
    memset(&sockaddrinLocal, 0, sizeof(sockaddrinLocal));
    sockaddrinLocal.sin_len         = sizeof(struct sockaddr_in);
    sockaddrinLocal.sin_family      = AF_INET;                          /* 地址族                       */
    sockaddrinLocal.sin_addr.s_addr = inet_addr("192.168.1.100");       /* 绑定到 192.168.1.100         */
    sockaddrinLocal.sin_port        = htons(__UDP_ECHO_PORT_SERVER + 1);/* 绑定服务器端口               */
    iRet = bind(sockFd2, (struct sockaddr *)&sockaddrinLocal, sizeof(sockaddrinLocal));
    if (iRet < 0) {                                                     /* 绑定操作失败                 */
        fprintf(stderr, "UDP echo server bind error, line %d.\r\n", __LINE__);
        return  (-1);                                                   /* 错误返回                     */
    }

    /*
     * 创建 socket 3
     */
    sockFd3 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (sockFd3 < 0) {
        fprintf(stderr, "UDP echo server socket error, line %d.\r\n", __LINE__);
        return  (-1);
    }

    /*
     * socket 3 绑定服务器端口
     */
    memset(&sockaddrinLocal, 0, sizeof(sockaddrinLocal));
    sockaddrinLocal.sin_len         = sizeof(struct sockaddr_in);
    sockaddrinLocal.sin_family      = AF_INET;                          /* 地址族                       */
    sockaddrinLocal.sin_addr.s_addr = INADDR_ANY;                       /* 绑定到任意 IP 地址           */
    sockaddrinLocal.sin_port        = htons(__UDP_ECHO_PORT_SERVER + 2);/* 绑定服务器端口               */
    iRet = bind(sockFd3, (struct sockaddr *)&sockaddrinLocal, sizeof(sockaddrinLocal));
    if (iRet < 0) {                                                     /* 绑定操作失败                 */
        fprintf(stderr, "UDP echo server bind error, line %d.\r\n", __LINE__);
        return  (-1);                                                   /* 错误返回                     */
    }

    /*
     * 计算最大的文件描述符
     */
    maxFd = MS_MAX(sockFd1, sockFd2);
    maxFd = MS_MAX(maxFd, sockFd3);

    while (1) {

        /*
         * 将文件描述符加入到可读文件描述符集
         */
        FD_ZERO(&rfds);
        FD_SET(sockFd1, &rfds);
        FD_SET(sockFd2, &rfds);
        FD_SET(sockFd3, &rfds);

        /*
         * 等待至少一个 socket 可读,并设置等待的超时时间
         */
        tv.tv_sec  = __UDP_ECHO_TIMEOUT_SERVER;
        tv.tv_usec = 0;

        iRet = select(maxFd + 1, &rfds, MS_NULL, MS_NULL, &tv);
        if (iRet < 0) {
            /*
             * 出错
             */
            fprintf(stderr, "UDP echo server select error.\r\n");

        } else if (iRet == 0) {
            /*
             * 超时
             */
            fprintf(stderr, "UDP echo server select timeout.\r\n");

        } else {
            /*
             * 至少一个 socket 可读
             */
            if (FD_ISSET(sockFd1, &rfds)) {
                /*
                 * 读 socket 1
                 */
                uiAddrLen = sizeof(sockaddrinRemote);
                sstRecv = recvfrom(sockFd1, cRecvBuff, sizeof(cRecvBuff), 0, (struct sockaddr *)&sockaddrinRemote, &uiAddrLen);
                if (sstRecv > 0) {
                    /*
                     * 打印远端 IP 和端口及接收到的数据
                     */
                    cRecvBuff[sstRecv] = 0;
                    inet_ntoa_r(sockaddrinRemote.sin_addr, ipAddrString, sizeof(ipAddrString));
                    printf("I recv %s from %s:%u\r\n", cRecvBuff, ipAddrString, ntohs(sockaddrinRemote.sin_port));

                    /*
                     * 回显
                     */
                    if (sendto(sockFd1, cRecvBuff, sstRecv, 0, (struct sockaddr *)&sockaddrinRemote, sizeof(sockaddrinRemote)) != sstRecv) {
                        fprintf(stderr, "UDP echo server send error, line %d, errno %d.\r\n", __LINE__, errno);
                    }
                } else {
                    fprintf(stderr, "UDP echo server read error, line %d.\r\n", __LINE__);
                }

            } else if (FD_ISSET(sockFd2, &rfds)) {
                /*
                 * 读 socket 2
                 */
                uiAddrLen = sizeof(sockaddrinRemote);
                sstRecv = recvfrom(sockFd2, cRecvBuff, sizeof(cRecvBuff), 0, (struct sockaddr *)&sockaddrinRemote, &uiAddrLen);
                if (sstRecv > 0) {
                    /*
                     * 打印远端 IP 和端口及接收到的数据
                     */
                    cRecvBuff[sstRecv] = 0;
                    inet_ntoa_r(sockaddrinRemote.sin_addr, ipAddrString, sizeof(ipAddrString));
                    printf("I recv %s from %s:%u\r\n", cRecvBuff, ipAddrString, ntohs(sockaddrinRemote.sin_port));

                    /*
                     * 回显
                     */
                    if (sendto(sockFd2, cRecvBuff, sstRecv, 0, (struct sockaddr *)&sockaddrinRemote, sizeof(sockaddrinRemote)) != sstRecv) {
                        fprintf(stderr, "UDP echo server send error, line %d, errno %d.\r\n", __LINE__, errno);
                    }
                } else {
                    fprintf(stderr, "UDP echo server read error, line %d.\r\n", __LINE__);
                }

            } else if (FD_ISSET(sockFd3, &rfds)) {
                /*
                 * 读 socket 3
                 */
                uiAddrLen = sizeof(sockaddrinRemote);
                sstRecv = recvfrom(sockFd3, cRecvBuff, sizeof(cRecvBuff), 0, (struct sockaddr *)&sockaddrinRemote, &uiAddrLen);
                if (sstRecv > 0) {
                    /*
                     * 打印远端 IP 和端口及接收到的数据
                     */
                    cRecvBuff[sstRecv] = 0;
                    inet_ntoa_r(sockaddrinRemote.sin_addr, ipAddrString, sizeof(ipAddrString));
                    printf("I recv %s from %s:%u\r\n", cRecvBuff, ipAddrString, ntohs(sockaddrinRemote.sin_port));

                    /*
                     * 回显
                     */
                    if (sendto(sockFd3, cRecvBuff, sstRecv, 0, (struct sockaddr *)&sockaddrinRemote, sizeof(sockaddrinRemote)) != sstRecv) {
                        fprintf(stderr, "UDP echo server send error, line %d, errno %d.\r\n", __LINE__, errno);
                    }
                } else {
                    fprintf(stderr, "UDP echo server read error, line %d.\r\n", __LINE__);
                }
            }
        }
    }

    close(sockFd1);
    close(sockFd2);
    close(sockFd3);

    return  (0);
}
文档内容是否对您有所帮助?
有帮助
没帮助