|

楼主 |
发表于 2017-11-11 17:29:56
|
显示全部楼层
20.3 BSD Socket函数
使用如下14个函数可以实现RL-TCPnet的socket通信:
(1)accept
(2)bind
(3)closesocket
(4)connect
(5)gethostbyname
(6)getpeername
(7)getsockname
(8)ioctlsocket
(9)listen
(10)recv
(11)recvfrom
(12)send
(13)sendto
(14)socket
关于这14个函数的讲解及其使用方法可以看教程第 3 章 3.4 小节里面说的参考资料 rlarm.chm 文件:
这里我们重点的说以下9个函数,因为本章节配套的例子使用的是这9个函数:
(1)accept
(2)bind
(3)closesocket
(4)listen
(5)recv
(6)send
(7)connect
(8)socket
(9)ioctlsocket
关于这些函数注意以下三点:
1、BSD接口函数执行的是标准Berkeley Socket通信接口,但不是将BSD Socket的所有API都实现了。
2、BSD Socket是线程安全的,也就是支持多任务,因此使用Socket是务必需要多任务支持的。使用的RTOS不限,任何主流的RTOS都可以支持。
3、BSD Socket的底层是由前面章节讲解的TCP和UDP实现的。
20.3.1 函数socket
函数原型:- int socket (
- int family, /* 地址族 */
- int type, /* 通信类型 */
- int protocol); /* 通信协议 */
复制代码 函数描述:
函数socket用于创建一个socket。
(1)第1个参数用于指定地址族,当前仅支持AF_INET。
(2)第2个参数是通信类型,有如下两种可选。
(3)第3个参数是协议类型,支持以下三种。
(4)返回值有以下几种:
a. 创建成功的话,返回socket的句柄。
b. 返回SCK_EINVALIDPARA,表示函数参数无效或者参数不支持。
c. 返回SCK_ERROR,表示创建失败。
使用这个函数要注意以下问题:
1. 调用任何其它BSD Socket函数之前,务必优先调用函数socket。
2. 返回负值表示错误。所有错误类型代表的数值,详见本章节20.2小节。
使用举例:
- /*
- *********************************************************************************************************
- * 函 数 名: TCPnetTest
- * 功能说明: TCPnet应用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void TCPnetTest(void)
- {
- char dbuf[10];
- int len;
- int sock, res;
- unsigned long sck_mode;
- SOCKADDR_IN addr;
-
-
- while (1)
- {
- /* 创建一个socket
- 第1个参数AF_INET:当前仅支持这个类型的地址族。
- 第2个参数SOCK_STREAM:表示数据流通信类型,即使用的TCP。
- 第3个参数0 :配置为0的话,自动跟第2个参数进行协议匹配,这里就是TCP协议。
- */
- sock = socket (AF_INET, SOCK_STREAM, 0);
-
- /* 设置使能KEEP ALIVE,让客户端和服务器保存连接 */
- sck_mode = 1;
- res = ioctlsocket (sock, FIO_KEEP_ALIVE, &sck_mode);
- if (res == SCK_SUCCESS)
- {
- printf_debug("KEEP ALIVE设置成功\\r\\n");
- }
- else
- {
- printf_debug("KEEP ALIVE设置失败\\r\\n");
- }
-
- /* 端口号设置为1001 */
- addr.sin_port = htons(PORT_NUM);
-
- /* 与函数socket中的AF_INET作用一样 */
- addr.sin_family = PF_INET;
-
- addr.sin_addr.s_b1 = IP1;
- addr.sin_addr.s_b2 = IP2;
- addr.sin_addr.s_b3 = IP3;
- addr.sin_addr.s_b4 = IP4;
-
- /* 客户端连接远程服务器,如果远程服务器还未创建,此函数会立即返回 */
- res = connect (sock, (SOCKADDR *)&addr, sizeof (addr));
- printf_debug("客户端连接远程服务器状态%s\\r\\n", ReVal_Table[abs(res)]);
-
- /* 省略 */
- }
- }
复制代码
20.3.2 函数connect
函数原型:
- int connect (
- int sock, /* Socket 句柄 */
- SOCKADDR *addr, /* 远程地址指针变量*/
- int addrlen); /* SOCKADDR结构体变量大小,单位字节 */
复制代码 函数描述:
函数用于配置要连接的远程IP地址和端口,如果是SOCK_STREAM类型socket,将跟远程IP建立连接,这种情况主要用于本地客户端连接远程服务器,其实就是TCP通信。如果是SOCK_DGRAM类型的socket,调用此函数起到一个地址过滤的作用,设置要通信的远程IP和端口号,其实就是UDP通信,而用户再次调用这个函数,可以换一个远程IP和端口号。
如果系统检测到当前工程是工作在多任务环境,即用户使能了RTX操作系统或者其它RTOS(注意,其它RTOS是无法识别的,需要在MDK中Option->C/C++的预定义宏中加上__RTX才可以识别,这个在前面相应RTOS的移植章节有说明),函数connect工作在阻塞模式,等待通信连接建立,这里要特别注意一点,如果远程服务器或者远程设备不存在,这个函数会立即返回,并不会等待连接建立。如果用户没有使能RTX操作系统或者其它RTOS,函数connect会工作在非阻塞模式,调用此函数后会立即返回,如果返回的是SCK_EWOULDBLOCK,就需要用户再次调用函数connect进行连接,直到可以连接成功。
(1)第1个参数是Socket句柄,即函数socket的返回值。
(2)第2个参数是SOCKADDR类型结构体指针变量,此结构体变量中定义了要远程访问的IP地址和端口号。
(3)第3个参数是结构体变量SOCKADDR的大小,单位字节。
(4)返回值有以下几种:
a. 返回SCK_SUCCESS,表示连接成功。
b. 返回SCK_EINVALID,表示函数socket句柄参数无效。
c. 返回SCK_EINVALIDPARA,表示其它参数无效或者不支持。
d. 返回SCK_EWOULDBLOCK,表示函数connect要进入阻塞态,而此函数是工作在非阻塞方式。
e. 返回SCK_ECLOSED,表示远程端点已经关闭连接。
f. 返回SCK_ERROR,表示socket底层的UDP或者TCP通信出错。
使用这个函数要注意以下问题:
1. 调用此函数之前,务必优先调用函数socket。
2. 返回负值表示错误。所有错误类型代表的数值,详见本章节20.2小节。
使用举例:
- /*
- *********************************************************************************************************
- * 函 数 名: TCPnetTest
- * 功能说明: TCPnet应用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void TCPnetTest(void)
- {
- char dbuf[10];
- int len;
- int sock, res;
- unsigned long sck_mode;
- SOCKADDR_IN addr;
-
-
- while (1)
- {
- /* 创建一个socket
- 第1个参数AF_INET:当前仅支持这个类型的地址族。
- 第2个参数SOCK_STREAM:表示数据流通信类型,即使用的TCP。
- 第3个参数0 :配置为0的话,自动跟第2个参数进行协议匹配,这里就是TCP协议。
- */
- sock = socket (AF_INET, SOCK_STREAM, 0);
-
- /* 设置使能KEEP ALIVE,让客户端和服务器保存连接 */
- sck_mode = 1;
- res = ioctlsocket (sock, FIO_KEEP_ALIVE, &sck_mode);
- if (res == SCK_SUCCESS)
- {
- printf_debug("KEEP ALIVE设置成功\\r\\n");
- }
- else
- {
- printf_debug("KEEP ALIVE设置失败\\r\\n");
- }
-
-
- /* 端口号设置为1001 */
- addr.sin_port = htons(PORT_NUM);
-
- /* 与函数socket中的AF_INET作用一样 */
- addr.sin_family = PF_INET;
-
- addr.sin_addr.s_b1 = IP1;
- addr.sin_addr.s_b2 = IP2;
- addr.sin_addr.s_b3 = IP3;
- addr.sin_addr.s_b4 = IP4;
-
- /* 客户端连接远程服务器,如果远程服务器还未创建,此函数会立即返回 */
- res = connect (sock, (SOCKADDR *)&addr, sizeof (addr));
- printf_debug("客户端连接远程服务器状态%s\\r\\n", ReVal_Table[abs(res)]);
-
- /* 省略 */
- }
- }
复制代码
20.3.3 函数bind
函数原型:
- int bind (
- int sock, /* Socket句柄 */
- const SOCKADDR *addr, /* 本地地址指针变量 */
- int addrlen); /* SOCKADDR结构体变量大小,单位字节 */
复制代码 函数描述:
函数bind用于给创建的socket分配一个名称,主要是IP地址和端口号。
(1)第1个参数是Socket句柄,即函数socket的返回值。
(2)第2个参数是SOCKADDR类型结构体指针变量,此结构体变量中定义了IP地址和端口号。
(3)第3个参数是结构体变量SOCKADDR的大小,单位字节。
(4)返回值有以下几种:
a. 返回SCK_SUCCESS,表示函数调用成功。
b. 返回SCK_EINVALID,表示函数socket句柄参数无效。
c. 返回SCK_EINVALIDPARA,表示其它参数无效或者不支持。
使用这个函数要注意以下问题:
1. 调用此函数之前,务必优先调用函数socket。
2. 返回负值表示错误。所有错误类型代表的数值,详见本章节20.2小节。
使用举例:
- /*
- *********************************************************************************************************
- * 函 数 名: TCPnetTest
- * 功能说明: TCPnet应用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void TCPnetTest(void)
- {
- char dbuf[10];
- int len;
- int sock, sd, res;
- SOCKADDR_IN addr;
- SOCKADDR_IN ReAddr;
-
-
- while (1)
- {
- /* 创建一个socket
- 第1个参数AF_INET:当前仅支持这个类型的地址族。
- 第2个参数SOCK_STREAM:表示数据流通信类型,即使用的TCP。
- 第3个参数0 :配置为0的话,自动跟第2个参数进行协议匹配,这里就是TCP协议。
- */
- sock = socket (AF_INET, SOCK_STREAM, 0);
-
- /* 端口号设置为1001 */
- addr.sin_port = htons(LocalPort_NUM);
-
- /* 与函数socket中的AF_INET作用一样 */
- addr.sin_family = PF_INET;
- /*
- INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或所有地址,
- 任意地址。用在这里的话就表示监控端口号为ddr.sin_port的所有IP地址消息。一般主要用
- 于有多个网卡或者IP地址的情况。开发板只用了DM9161的网口,就是监听这个网口的IP地址。
- */
- addr.sin_addr.s_addr = INADDR_ANY;
-
- /* 给socket绑定IP和端口号 */
- bind (sock, (SOCKADDR *)&addr, sizeof(addr));
-
- /* 设置监听,最大监听1个连接 */
- listen (sock, 1);
-
- /*
- 等待soket连接请求,有的话,自动创建1个新的socket进行连接通信,没有的话,等待连接。
- 注意,能够accept的个数受到listen函数的限制,而listen函数又受到Net_Config.c中宏定义
- BSD_NUMSOCKS 的限制。
- */
- len = sizeof(ReAddr);
- sd = accept (sock, (SOCKADDR *)&ReAddr, &len);
- printf_debug ("远程客户端请求连接IP: %d.%d.%d.%d\\n", ReAddr.sin_addr.s_b1,
- ReAddr.sin_addr.s_b2,
- ReAddr.sin_addr.s_b3,
- ReAddr.sin_addr.s_b4);
- printf_debug ("远程客户端端口号: %d\\n", ntohs(ReAddr.sin_port));
-
- /* 关闭监听socket,这个监听socket是调用函数socket后自动创建的 */
- closesocket (sock);
- sock = sd;
-
-
- /* 省略 */
-
- }
- }
复制代码
20.3.4 函数listen
函数原型:- int listen (
- int sock, /* Socket句柄 */
- int backlog); /* 可以监听的最大连接数 */
复制代码 函数描述:
函数listen用于设置创建的socket工作在监听模式,调用此函数前务必优先调用bind。
(1)第1个参数是Socket句柄,即函数socket的返回值。
(2)第2个参数是可以监听的最大连接数,连接请求会放在一个专门的队列里面。
(3)返回值有以下几种:
a. 返回SCK_SUCCESS,表示函数调用成功。
b. 返回SCK_EINVALID,表示函数socket句柄参数无效。
c. 返回SCK_EINVALIDPARA,表示其它参数无效或者不支持。
d. 返回SCK_ERROR,表示没有socket可供使用了,即可用的socket数量小于第2个参数中设置的最大连接数。
使用这个函数要注意以下问题:
1. 调用此函数之前,务必优先调用函数socket。
2. 返回负值表示错误。所有错误类型代表的数值,详见本章节20.2小节。
3. 可供用户使用的socket数量是在Net_Config.c文件中定义的:#defineBSD_NUMSOCKS 5 设置监听数量的时候切不可超过这里定义的数值,此宏定义可设置的范围是1-20个socket。
4. 调用监听函数后,系统会自动开启一个socket用于监听连接请求。这个自动开启的socket不在宏定义BSD_NUMSOCKS配置的范围内。
使用举例:
- /*
- *********************************************************************************************************
- * 函 数 名: TCPnetTest
- * 功能说明: TCPnet应用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void TCPnetTest(void)
- {
- char dbuf[10];
- int len;
- int sock, sd, res;
- SOCKADDR_IN addr;
- SOCKADDR_IN ReAddr;
-
-
- while (1)
- {
- /* 创建一个socket
- 第1个参数AF_INET:当前仅支持这个类型的地址族。
- 第2个参数SOCK_STREAM:表示数据流通信类型,即使用的TCP。
- 第3个参数0 :配置为0的话,自动跟第2个参数进行协议匹配,这里就是TCP协议。
- */
- sock = socket (AF_INET, SOCK_STREAM, 0);
-
- /* 端口号设置为1001 */
- addr.sin_port = htons(LocalPort_NUM);
-
- /* 与函数socket中的AF_INET作用一样 */
- addr.sin_family = PF_INET;
- /*
- INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或所有地址,
- 任意地址。用在这里的话就表示监控端口号为ddr.sin_port的所有IP地址消息。一般主要用
- 于有多个网卡或者IP地址的情况。开发板只用了DM9161的网口,就是监听这个网口的IP地址。
- */
- addr.sin_addr.s_addr = INADDR_ANY;
-
- /* 给socket绑定IP和端口号 */
- bind (sock, (SOCKADDR *)&addr, sizeof(addr));
-
- /* 设置监听,最大监听1个连接 */
- listen (sock, 1);
-
- /*
- 等待soket连接请求,有的话,自动创建1个新的socket进行连接通信,没有的话,等待连接。
- 注意,能够accept的个数受到listen函数的限制,而listen函数又受到Net_Config.c中宏定义
- BSD_NUMSOCKS 的限制。
- */
- len = sizeof(ReAddr);
- sd = accept (sock, (SOCKADDR *)&ReAddr, &len);
- printf_debug ("远程客户端请求连接IP: %d.%d.%d.%d\\n", ReAddr.sin_addr.s_b1,
- ReAddr.sin_addr.s_b2,
- ReAddr.sin_addr.s_b3,
- ReAddr.sin_addr.s_b4);
- printf_debug ("远程客户端端口号: %d\\n", ntohs(ReAddr.sin_port));
-
- /* 关闭监听socket,这个监听socket是调用函数socket后自动创建的 */
- closesocket (sock);
- sock = sd;
-
-
- /* 省略 */
-
- }
- }
复制代码
20.3.5 函数accept
函数原型:- int accept (
- int sock, /* Socket 句柄 */
- SOCKADDR *addr, /* 远程连接的指针变量 */
- int *addrlen); /* 远程连接SOCKADDR结构体大小的指针变量,结构体大小的单位是字节 */
复制代码 函数描述:
函数accept用于接受监听socket队列中的连接请求,如果队列中有挂起的连接请求,调用accept函数后会把连接请求从监听socket队列中删除并创建一个新的socket用于连接。监听socket仍然保持打开,继续监听新的连接请求。
如果系统检测到当前工程是工作在多任务环境,即用户使能了RTX操作系统或者其它RTOS(注意,其它RTOS是无法识别的,需要在MDK中Option->C/C++的预定义宏中加上__RTX才可以识别,这个在前面相应RTOS的移植章节有说明),函数accept会工作在阻塞模式,等待连接请求。反之,如果用户没有使能RTX操作系统或者其它RTOS,函数accept会工作在非阻塞模式,调用此函数后会立即返回,如果返回的是SCK_EWOULDBLOCK,就需要用户再次调用函数accept进行查询。
(1)第1个参数是Socket句柄,即函数socket的返回值,必须得是SOCK_STREAM类型的socket。
(2)第2个参数是SOCKADDR类型结构体指针变量,通过此参数来记录远程连接的IP地址和端口号。
(3)第3个参数是用于记录远程连接地址结构体长度的指针变量。
(4)返回值有以下几种:
a. 返回大于0的数值,即新创建的socket句柄,表示函数调用成功。
b. 返回SCK_EINVALID,表示函数socket句柄参数无效。
c. 返回SCK_EWOULDBLOCK,表示函数accept要进入阻塞态,而此函数是工作在非阻塞方式。
d. 返回SCK_ECLOSED,表示远程连接请求已经取消,连接关闭。
使用这个函数要注意以下问题:
1. 调用此函数之前,务必优先调用函数socket。
2. 返回负值表示错误。所有错误类型代表的数值,详见本章节20.2小节。
使用举例:
- /*
- *********************************************************************************************************
- * 函 数 名: TCPnetTest
- * 功能说明: TCPnet应用
- * 形 参: 无
- * 返 回 值: 无
- *********************************************************************************************************
- */
- void TCPnetTest(void)
- {
- char dbuf[10];
- int len;
- int sock, sd, res;
- SOCKADDR_IN addr;
- SOCKADDR_IN ReAddr;
-
-
- while (1)
- {
- /* 创建一个socket
- 第1个参数AF_INET:当前仅支持这个类型的地址族。
- 第2个参数SOCK_STREAM:表示数据流通信类型,即使用的TCP。
- 第3个参数0 :配置为0的话,自动跟第2个参数进行协议匹配,这里就是TCP协议。
- */
- sock = socket (AF_INET, SOCK_STREAM, 0);
-
- /* 端口号设置为1001 */
- addr.sin_port = htons(LocalPort_NUM);
-
- /* 与函数socket中的AF_INET作用一样 */
- addr.sin_family = PF_INET;
- /*
- INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或所有地址,
- 任意地址。用在这里的话就表示监控端口号为ddr.sin_port的所有IP地址消息。一般主要用
- 于有多个网卡或者IP地址的情况。开发板只用了DM9161的网口,就是监听这个网口的IP地址。
- */
- addr.sin_addr.s_addr = INADDR_ANY;
-
- /* 给socket绑定IP和端口号 */
- bind (sock, (SOCKADDR *)&addr, sizeof(addr));
-
- /* 设置监听,最大监听1个连接 */
- listen (sock, 1);
-
- /*
- 等待soket连接请求,有的话,自动创建1个新的socket进行连接通信,没有的话,等待连接。
- 注意,能够accept的个数受到listen函数的限制,而listen函数又受到Net_Config.c中宏定义
- BSD_NUMSOCKS 的限制。
- */
- len = sizeof(ReAddr);
- sd = accept (sock, (SOCKADDR *)&ReAddr, &len);
- printf_debug ("远程客户端请求连接IP: %d.%d.%d.%d\\n", ReAddr.sin_addr.s_b1,
- ReAddr.sin_addr.s_b2,
- ReAddr.sin_addr.s_b3,
- ReAddr.sin_addr.s_b4);
- printf_debug ("远程客户端端口号: %d\\n", ntohs(ReAddr.sin_port));
-
- /* 关闭监听socket,这个监听socket是调用函数socket后自动创建的 */
- closesocket (sock);
- sock = sd;
-
-
- /* 省略 */
-
- }
- }
复制代码
|
|