开发板的主芯片是NXP的imx6ull,PC端虚拟机是运行在VirtualBox环境下的Ubuntu。开发板的以太网口接网线,利用ifconfig查看它的ip地址信息:
可见其ip地址为192.168.0.104。虚拟机连接的网络和开发板网络是同一个局域网,同样可查到虚拟机的ip信息:
它的ip地址是192.168.0.103。在开发板上ping虚拟机的ip地址,结果如下:
可见从开发板侧可以ping通虚拟机。反过来在虚拟机侧ping开发板,如下:
从结果看也是没有问题的。我们让开发板作为服务端,让虚拟机作为客户端。服务端执行经典的socket、bind、listen和accept等套接字操作流程实现对客户端的监听、接入,客户端则调用socket和connect主动向服务端发起连接请求。连接成功后,客户端从标准输入stdin获取要发送给服务端的数据,服务端接收到数据后给到标准输出,收到exit则服务端发起close。开发板服务端代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SERVER_PORT 9876 // server监听在这个端口号
int main(void)
{
struct sockaddr_in server_addr = {0}; // server套接字结构体
struct sockaddr_in client_addr = {0}; // client套接字结构体
char ip_str[20] = {0};
int sockfd, connfd; // 套接字描述符和已连接描述符
int addrlen = sizeof(client_addr);
char recvbuf[512];
int ret;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ // 以流式套接字socket
perror("socket error");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 设置可连接ip地址为通配ip
server_addr.sin_port = htons(SERVER_PORT); // 设置监听端口号
if((ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) < 0){
perror("bind error");
close(sockfd);
exit(EXIT_FAILURE);
}
if((ret = listen(sockfd, 50)) < 0){ // 最大可处理的排队待连接客户端数为50
perror("listen error");
close(sockfd);
exit(EXIT_FAILURE);
}
if((connfd = accept(sockfd, (struct sockaddr *)&client_addr, &addrlen)) < 0){ // 阻塞等待客户端连接
perror("accept error");
close(sockfd);
exit(EXIT_FAILURE);
}
inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_str, sizeof(ip_str)); // 解析客户端ip地址
printf("客户端主机的IP地址和进程的端口号分别为: %s %d\n", ip_str, client_addr.sin_port);
for(;;){ // 处理客户端发来的数据
memset(recvbuf, 0x0, sizeof(recvbuf));
if((ret = recv(connfd, recvbuf, sizeof(recvbuf), 0)) <= 0){ //读取数据
perror("recv error");
close(connfd);
break;
}
printf("server从client获取的数据: %s\n", recvbuf);
if(strncmp("exit", recvbuf, 4) == 0){
printf("server exit...\n");
close(connfd);
break;
}
}
close(sockfd);
exit(EXIT_SUCCESS);
}
虚拟机客户端代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define SERVER_PORT 9876
int main(int argc, char *argv[])
{
struct sockaddr_in server_addr = {0};
char buf[512];
int sockfd;
int ret;
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
perror("socket error");
exit(EXIT_FAILURE);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, argv[1], &server_addr.sin_addr); // ip地址
if((ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr))) < 0){
perror("connect error");
close(sockfd);
exit(EXIT_FAILURE);
}
printf("服务器连接成功...\n\n");
for(;;){ // 向服务器发送数据
memset(buf, 0x0, sizeof(buf));
printf("Please enter a string: ");
fgets(buf, sizeof(buf), stdin); // 从标准输入获取
if((ret = send(sockfd, buf, strlen(buf), 0)) < 0){
perror("send error");
break;
}
if((strncmp(buf, "exit", 4)) == 0)
break;
}
close(sockfd);
exit(EXIT_SUCCESS);
}
分别使用gcc和arm-linux-gnueabihf-gcc编译客户端和服务端程序,编译完成后先在开发板启动服务器进程,然后在虚拟机启动客户端进程,若连接成功则在虚拟机端输入字符串并发送,正常情况下可看到开发板侧显示来自虚拟机的数据,如下: