目录
[1 硬件接口介绍](#1 硬件接口介绍)
[1.1 接口介绍](#1.1 接口介绍)
[1.2 硬件接口](#1.2 硬件接口)
[2 TCP Client相关接口函数](#2 TCP Client相关接口函数)
[2.1 接口函数](#2.1 接口函数)
[2.2 认识函数](#2.2 认识函数)
[2.2.1 socket函数](#2.2.1 socket函数)
[2.2.2 bind函数](#2.2.2 bind函数)
[2.2.3 connect函数](#2.2.3 connect函数)
[2.2.4 读数据函数](#2.2.4 读数据函数)
[2.2.5 写数据函数](#2.2.5 写数据函数)
[3 TCP/IP Clinet功能实现](#3 TCP/IP Clinet功能实现)
[3.1 代码实现](#3.1 代码实现)
[3.2 详尽代码](#3.2 详尽代码)
[4 测试](#4 测试)
[4.1 编译代码](#4.1 编译代码)
[4.2 运行代码](#4.2 运行代码)
概述
本文主要介绍在IOT-9608I-L(工控板)实现TCP Client 的方法,其内容包括:板卡的NET硬件端口,以及和TCP Client相关接口函数的功能和用法,还编写一个实例,实现了TCP/IP Client,并在PC上配置一个servers实现数据的接收和发送功能。
1 硬件接口介绍
1.1 接口介绍
IoT-9608提供两路以太网接口,一路百兆网,一路千兆网。连接器物理形式为双网口RJ45插座,每个网口均有两个LED指示灯,用来指示以太网的工作情况。双网口工位为J12,其中,在主板上百兆网的标志为"NET1", 在主板上千兆网的标志为"NET2"。
系统上电以后NET1的绿灯亮,表示配置为百兆网,接入网线后黄灯亮,表示连接正常;NET2的指示灯在不接入网线时均不亮,当接入网线后,黄灯亮,表示连接正常,当有数据传输时,绿灯闪烁。
1.2 硬件接口
1)百兆RJ45接口引脚定义(NET1)
管脚号 | 符号 | 功能 | 管脚号 | 符号 | 功能 | 管脚号 | 符号 | 功能 |
---|---|---|---|---|---|---|---|---|
1 | TX+ | 发送+ | 4 | NC | 空 | 7 | NC | 空 |
2 | TX- | 发送- | 5 | NC | 空 | 8 | NC | 空 |
3 | RX+ | 接收+ | 6 | RX- | 接收- |
2)千兆RJ45接口引脚定义(NET2)
管脚号 | 符号 | 功能 | 管脚号 | 符号 | 功能 | 管脚号 | 符号 | 功能 |
---|---|---|---|---|---|---|---|---|
1 | MX1+ | 差分对1数据正 | 4 | MX3+ | 差分对3数据正 | MX4+ | NC | 差分对4数据正 |
2 | MX1- | 差分对1数据负 | 5 | MX3- | 差分对3数据负 | MX4- | NC | 差分对4数据负 |
3 | MX2+ | 差分对2数据正 | 6 | MX2- | 差分对2数据负 |
2 TCP Client相关接口函数
2.1 接口函数
Socket接口提供了一些接口函数,用于实现Client功能,其具体函数接口如下:
函数名称 | 功能描述 |
---|---|
socket | 创建一个Socket通信节点 |
bind | 绑定地址和端口 |
connect | 连接服务器端口 |
sendto | 发送数据接口 |
recvfrom | 接收数据接口 |
2.2 认识函数
2.2.1 socket函数
函数原型:
cpp
int socket(int domain, int type, int protocol);
参数介绍
参数名称 | 功能描述 |
---|---|
domain | Socket所使用的地址类型 |
type | Socket的类型 |
protocol | 协议类型 |
函数返回值:
ERROR 类型 | 错误含义 |
---|---|
EAFNOSUPPORT | 本实现不支持指定的地址族 |
EMFILE | 进程不再有文件描述符可用 |
ENFILE | 系统不再有文件描述符可用 |
EPROTONOSUPPORT | 地址族或本实现不支持协议 |
EPROTOTYPE | 协议不支持套接字类型 |
2.2.2 bind函数
函数原型
cpp
int bind(int socket, const struct sockaddr *address, socklentaddress_len);
参数介绍:
参数名称 | 功能描述 |
---|---|
socket | 指向Socket的有效文件描述符 |
address | 一个指向struct sockaddr结构的指针,根据不同的协议,可以有不同的具体结构,对于IP地址,就是struct sockaddr_in |
address_len | 指明使用的地址数据结构的长度 |
返回值:
当bind()调用成功时返回0,失败时返回-1,这时需要检查的errno值见表。
ERROR 类型 | 含义 |
---|---|
EADDRINUSE | 指定的地址和端口已经被占用 |
EADDRNOTAVAIL | 本机不存在指定的地址 |
EAFNOSUPPORT | 对于指定的地址族来说,地址无效 |
ENOTSOCK | socket不是指向socket的文件描述符 |
EBADF | socket不是有效的文件描述符 |
EINVAL | socket已经保定到一个地址,协议不支持绑定到新地址,或者socket已关闭 |
EOPNOTSUPP | socket类型不支持对地址的绑定 |
2.2.3 connect函数
函数原型:
cpp
int connect(int socket, const struct sockaddr *address, socklentaddress_len);
参数介绍:
参数名称 | 功能描述 |
---|---|
socket | 指向Socket的有效文件描述符 |
address | 一个指向struct sockaddr结构的指针,根据不同的协议,可以有不同的具体结构,对于IP地址,就是struct sockaddr_in |
address_len | 指明使用的地址数据结构的长度 |
connect()需要处理的errno
errno | 含义 |
---|---|
EADDRNOTAVAIL | 本机上没有指定地址 |
EAFNOSUPPORT | 对于指定Socket的地址族来说,指定地址无效 |
EALREADY | Socket已经做过connect()操作 |
EBADF | 参数Socket不是有效的文件描述符 |
ECONNREFUSED | 对方未监听Socket或拒绝连接 |
EINPROGRSS | 已经设置为O_NONBLOCK的Socket不能立即建立连接,需要异步建立连接 |
EINTR | 信号中断了建立连接的尝试,需要异步建立连接 |
EISCONN | 指定的Socket已经连接 |
ENETUNREACH | 没有到目标网络的路由 |
ENOTSOCK | 文件描述符未指向Socket |
EPROTOTYPE | 指定的地址类型与绑定到指定对等地址的Socket类型不同 |
ETIMEDOUT | 试图建立连接超时 |
2.2.4 读数据函数
函数原型
cpp
ssize_t read(int fd, void *buf, size_t count);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
2.2.5 写数据函数
函数原型
cpp
ssize_t write(int fd, const void *buf, size_t count);
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
3 TCP/IP Clinet功能实现
3.1 代码实现
1)创建socket
- 使用connect连接服务器端口
3)接收/发送数据
3.2 详尽代码
1) 创建client.c文件,编写client功能代码
cpp
/***************************************************************
Copyright 2024-2029. All rights reserved.
文件名 : client.c
作者 : tangmingfei2013@126.com
版本 : V1.0
描述 : IOT_9608 TCP/IP Client测试程序
其他 : 无
日志 : 初版V1.0 2024/04/23
***************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define PORT 5000 // The port which is communicate with server
#define LENGTH 256 // Buffer length
int main(int argc, char *argv[])
{
int sockfd; // Socket file descriptor
int num; // Counter of received bytes
char revbuf[LENGTH]; // Receive buffer
char buff[LENGTH];
struct sockaddr_in remote_addr; // Host address information
/* Check parameters number */
if (argc != 2)
{
printf ("Usage: client HOST IP (ex: ./client 192.168.1.4).\n");
return (0);
}
/* Get the Socket file descriptor */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
printf("ERROR: Failed to obtain Socket Descriptor!\n");
return (0);
}
/* Fill the socket address struct */
remote_addr.sin_family = AF_INET; // Protocol Family
remote_addr.sin_port = htons(PORT); // Port number
inet_pton(AF_INET, argv[1], &remote_addr.sin_addr); // Net Address
bzero(&(remote_addr.sin_zero), 8); // Flush the rest of struct
/* Try to connect the remote */
if (connect(sockfd, (struct sockaddr *)&remote_addr, sizeof(struct sockaddr)) == -1)
{
printf ("ERROR: Failed to connect to the host!\n");
return (0);
}
else
{
printf ("OK: Have connected to the %s\n",argv[1]);
}
/* Try to connect the server */
while (strcmp(revbuf,"exit") != 0) // Check remoter command
{
bzero(revbuf,LENGTH);
num = recv(sockfd, revbuf, LENGTH, 0);
switch(num)
{
case -1:
printf("ERROR: Receive string error!\n");
close(sockfd);
return (0);
case 0:
close(sockfd);
return(0);
default:
printf ("OK: Receviced numbytes = %d\n", num);
break;
}
revbuf[num] = '\0';
printf ("OK: Receviced string is: %s \n", revbuf);
// send response message to server
memset(buff,'\0',sizeof(buff));
sprintf(buff,"IOT_9608 TCP/IP Client,i am %s:%d\n",
inet_ntoa(remote_addr.sin_addr),ntohs(remote_addr.sin_port));
ssize_t send_ret = send(sockfd, buff, strlen(buff),0);
if(send_ret < 0)
{
close(sockfd);
return(0);
}
}
close (sockfd);
return (0);
}
2)编写Makefile
bash
EXEC= client
OBJS= client.o
CROSS = arm-none-linux-gnueabi-
CC= ${CROSS}gcc
LDFLAGS =
CFLAGS += -g -Wall
EXTRA_LIBS +=
all: $(EXEC)
$(EXEC): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS) $(EXTRA_LIBS)
clean:
-rm -f $(EXEC) *.elf *.gdb *.o
4 测试
4.1 编译代码
使用make编译代码,然后将编译的可执行文件共享至NFS目录
4.2 运行代码
1)使用NetAssist,创建服务器,端口设置为5000
2)在板卡上运行代码,使用如下命令:
cpp
./client 192.168.1.4
板卡上的log信息:
服务器软件上收到的log: