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] | numdns | DNS 服务器编号 |
[out] | dnsserver | DNS 服务器地址 |
返回值 成功返回 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] | numdns | DNS 服务器编号 |
[in] | dnsserver | DNS 服务器地址 |
返回值 成功返回 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] | addr | 32 位网络字节序地址 |
返回值 正确时返回字符串指针,错误时返回 MS_NULL
注意事项 此函数是不可重入的,建议使用可重入版本
inet_ntoa_r
示例 无
inet_ntoa_r()
描述 32 位网络字节序二进制值转换为点分十进制字符串
函数原型
char *inet_ntoa_r(const in_addr_t *addr, char *buf, int buflen);
- 参数
输入/输出 | 参数 | 描述 |
---|---|---|
[in] | addr | 32 位网络字节序地址 |
[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] | size | dst 缓冲区的大小 |
返回值 正确时返回地址串的指针,错误时返回 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] | namelen | name 结构的长度 |
返回值 成功返回 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_SOCKET | SO_BROADCAST | 运行发送广播数据报 | int |
SOL_SOCKET | SO_ERROR | 获取待处理错误并消除 | int |
SOL_SOCKET | SO_KEEPALIVE | 周期性测试连接是否存活 | int |
SOL_SOCKET | SO_LINGER | 若有数据待发送则延迟关闭 | struct linger |
SOL_SOCKET | SO_DONTLINGER | 关闭 SO_LINGER 选项 | int |
SOL_SOCKET | SO_RCVBUF | 接收缓冲区大小 | int |
SOL_SOCKET | SO_RCVTIMEO | 接收超时 | struct timeval |
SOL_SOCKET | SO_SNDTIMEO | 发送超时 | struct timeval |
SOL_SOCKET | SO_REUSEADDR | 允许重用本地地址 | int |
SOL_SOCKET | SO_REUSEPORT | 允许重用本地端口 | int |
SOL_SOCKET | SO_TYPE | 取得套接字类型 | int |
SOL_SOCKET | SO_CONTIMEO | 连接超时 | struct timeval |
IPPROTO_IP | IP_TOS | 服务类型和优先级 | int |
IPPROTO_IP | IP_TTL | 存活时间 | int |
IPPROTO_IP | IP_MULTICAST_IF | 指定外出接口 | struct in_addr |
IPPROTO_IP | IP_MULTICAST_TTL | 指定外出 TTL | unsigned char |
IPPROTO_IP | IP_MULTICAST_LOOP | 指定是否回馈 | unsigned char |
IPPROTO_IP | IP_ADD_MEMBERSHIP | 加入多播组 | struct in_mreq |
IPPROTO_IP | IP_DROP_MEMBERSHIP | 离开多播组 | struct in_mreq |
IPPROTO_TCP | TCP_KEEPALIVE | 控测对方是否存活前连接闲置秒数 | int |
IPPROTO_TCP | TCP_KEEPIDLE | 对一个连接探测前的允许时间 | int |
IPPROTO_TCP | TCP_KEEPINTVL | 两个探测的时间间隔 | int |
IPPROTO_TCP | TCP_KEEPCNT | 探测的最大次数 | int |
IPPROTO_IPV6 | IPV6_V6ONLY | 只允许 IPV6(SylixOS 不支持数据报通信) | int |
IPPROTO_UDPLITE | UDPLITE_SEND_CSCOV | 执行发送校验和 | int |
IPPROTO_UDPLITE | UDPLITE_RECV_CSCOV | 执行接收校验和 | int |
IPPROTO_RAW | IPV6_CHECKSUM | IPV6 校验和 | 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] | namelen | name 结构的长度 |
返回值 成功返回 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);
}