前言
最近有个需求,所有本地海思的板子,都要能查询到他们的IP。如果是用串口调试,当然很容易知道IP,但是大部分情况下,我们还是用telnet远程连接,这时候,不知道IP就啥也干不了。
因此我想着在海思的板子上跑一个服务器程序,然后在本地运行客户端程序去查询,服务器接收到数据就将自己的IP返回给客户端,这样就知道所有海思芯片的IP了。
一开始是做了一个用TCP协议通信的,但是考虑到ABC三类IP加起来数量太多了,线程开太多,程序会把电脑卡死。然后又尝试了UDP广播,但是又不能做到跨网段,最终的解决方案是不使用C-S方式(客户端-服务器),而是采用对等连接方式P2P(peer to peer),在通信的时候,并不区分是服务器还是客户端,而是两台主机同时具有服务器和客户端的能力。并且发送信息的方式,都是往255.255.255.255广播,这样就能跨网段进行接收了。
本文档将所有码记录下来,并对关键点进行解释,方便以后查阅。
windows端所使用的线程API,是一个朋友封装的,文档中不提供代码,但是下载资源中可以找到
本文档资源地址:https://download.csdn.net/download/whitefish520/12831608
TCP方式
服务器
服务器端监听所有IP,并在客户端接入后,创建线程,发送自己的IP
//linux arm-hisiv500-linux-gcc server.c -o server -lpthread
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<string.h>
#include<strings.h>
#include<ctype.h>
#include<stdlib.h>
#include<pthread.h>
void *run(void *arg);
void trim(char *strIn, char *strOut);
int main(){
//lfd:socket返回的文件描述符,用lfd监听并接受客户端连接
//cfd:客户端链接上来返回的cfd,用cfd和客户端通信
int lfd,cfd;
int opt = 1;
char client_ip[256];
//serv_addr服务器地址
//client_addr客户端地址
struct sockaddr_in serv_addr,client_addr;
socklen_t client_len;
pthread_t tid;
//socket创建服务器返回lfd文件描述符
lfd = socket(AF_INET,SOCK_STREAM,0);
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&serv_addr,sizeof(serv_addr));
//ipv4
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//bind
bind(lfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr));
//监听是否有客户连接
listen(lfd,256);
while(1){
client_len = sizeof(client_addr);//客户端地址长度
//连接成功返回cfd,用cfd和客户通信
cfd = accept(lfd,(struct sockaddr*)&client_addr,&client_len);
printf("client:%s\t%d\n",
inet_ntop(AF_INET,&client_addr.sin_addr.s_addr,client_ip,sizeof(client_ip)),
ntohs(client_addr.sin_port));
//创建线程 回调 run方法 cfd做参数传入
pthread_create(&tid,NULL,run,(void *)cfd);
pthread_detach(tid);
}
return 0;
}
void *run(void *arg){
int cfd = (int ) arg;
char buf[1024];//缓冲区
char ipbuf[1024];//缓冲区
int len,i;
char *pinfo;
FILE *fp;
while(1){
system("ifconfig -a | grep 'inet addr' > ipinfo"); //获取ip并写入文件
fp = fopen("ipinfo", "r");
if (fp == NULL)
{
printf("fopen error!\n");
return;
}
while (!feof(fp))
{
fgets(buf,1024,fp); //从文件中一行一行读出数据
if(!feof(fp)) {
trim(buf, ipbuf); //取出首尾的空格
write(cfd,ipbuf,strlen(ipbuf));//发送给服务器
}
}
fclose(fp);
system("rm ipinfo"); //发送完毕,删除ip信息文件
break;
}
close(cfd);
}
//实现去除首尾空格的效果
void trim(char *strIn, char *strOut)
{
int i, j ;
i = 0;
j = strlen(strIn) - 1;
while(strIn[i] == ' ')
++i;
while(strIn[j] == ' ')
--j;
strncpy(strOut, strIn + i , j - i + 1);
strOut[j - i + 1] = '\0';
}
客户端
客户端程序做了对Windows的适配,需要Windows上安装MinGW gcc编译器,编译命令在注释中
客户端要求执行时输入一个参数,例如./client 192.168.10
,注意只要三段,客户端会对1-255尝试连接,成功则创建线程,打印信息
//win32 gcc client.c pps_thread_win32.c -o client.exe -lwsock32
//linux gcc client.c -o client -lpthread
#ifdef _WIN32
#include <stdio.h>
#include <winsock2.h>
#include "pps_thread.h"
#pragma comment(lib,"ws2_32.lib")
#else
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<sys/wait.h>
#endif
#define DEST_PORT 8080//目标地址端口号
#define MAX_DATA 100//接收到的数据最大程度
char targetIP[32];
int stop = 0;
void *run(void *arg)
{
#ifdef _WIN32
SOCKET sockfd;
WSADATA wsaData;
WORD w