网络是很多电子产品应用的基础,因此尤为重要。近期做的互联网电视项目 也是在建立在网络连接的基础之上,可惜我做的只是简单的porting层工作,对各种网络协议、应用以及底层驱动知之甚少。在这里小结一下network模块的工作流程。
首先设备启动的时候会创建一个线程用来检测网络连接的状态,当网线拔出或者插上以及ip地址发生变化就会向显示界面发送相应的消息。目前是通过获取上一次网络连接状态和当前网络连接状态进行比较来判断该发送的消息类型,通过功能强大的ioctl函数。感觉这种实现方法不太好,可能会增加网卡的负担。接下来是获取DNS/IP/MAC/GATEWAY,如果获取不到IP说明网卡设备还没有正式进入工作状态,网络连接也还没有正式连通,因此需要在这里等待直到获取到IP或者能判断网络通,然后就向显示界面发送一条指定的消息,实现从初始化页面向主页面的跳转。因为进行网络连接的工作都是底层完成的,所以我的porting层的实际上只需要做对网络状态是否发生变化(插拔网线)进行检测,以及开机后等待直到网络连通之后发送跳转消息给界面的工作。
这个过程中用到了几个通用的函数:
struct ifreq
{
#ifndef IFNAMSIZ
#define IFNAMSIZ 16
#endif
char ifr_name[IFNAMSIZ];
union {
struct sockaddr ifru_addr;
struct sockaddr ifru_dstaddr;
struct sockaddr ifru_broadaddr;
__ulong32_t ifru_flags;
int ifru_metric;
caddr_t ifru_data;
u_short ifru_site6;
__ulong32_t ifru_mtu;
int ifru_baudrate;
} ifr_ifru;
一、检测网卡工作状态(物理连接)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <net/if.h>
1.通过SIOCGIFFLAGS
static int network_get_netlink_status(const char *if_name) { int skfd; struct ifreq ifr = {0}; if ((skfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { fprintf(stderr,"socket create failed/n"); return LINK_ERR; } strcpy(ifr.ifr_name, if_name); if (ioctl(skfd,SIOCGIFFLAGS,&ifr) < 0) { fprintf("SIOCGIFFLAGS failed: %s/n", strerror(errno)); close(skfd); return LINK_ERR; } if (ifr.ifr_ifru.ifru_flags & IFF_RUNNING) return LINK_UP; else return LINK_DOWN; }
2.通过SIOCETHTOOL
static int network_detect_ethtool(int skfd, const char *if_name) { struct ifreq ifr; struct ethtool_value edata; memset(&ifr, 0, sizeof(ifr)); edata.cmd = ETHTOOL_GLINK; strncpy(ifr.ifr_name, if_name, sizeof(ifr.ifr_name)-1); ifr.ifr_data = (char *)&edata; if (ioctl(skfd, SIOCETHTOOL, &ifr) == -1) { printf("ETHTOOL_GLINK failed: %s/n", strerror(errno)); return 2; } return edata.data; }
3.通过SIOCGMIIREG
static int network_detect_mii(int skfd,const char *ifname) { struct ifreq ifr = {0}; unsigned phy_id; unsigned short *data, mii_val; strncpy(ifr.ifr_name, ifname, IFNAMSIZ); if (ioctl(skfd, SIOCGMIIPHY, &ifr) < 0) { fprintf(stderr, "SIOCGMIIPHY failed: %s/n",strerro(errno)); (void) close(skfd); return 2; } data = (u16 *)(&ifr.ifr_data); phy_id = data[0]; data[1] = 1; if (ioctl(skfd, SIOCGMIIREG, &ifr) < 0) { fprintf(stderr, "SIOCGMIIREG failed: %s/n", strerror(errno)); return 2; } // mii_val |= data[3]; mii_val = data[3]; return(((mii_val & 0x0016) == 0x0004) ? 0 : 1); }
二、检测接口的inet addr,Bcast ,Mask
const int network_get_eth_info(const char *if_name) { int sockfd; char *address = NULL; struct ifreq ifr = {0}; struct sockaddr_in *addr = {0}; if ((sockfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { printf("socket create failed."); return 2; } strncpy(ifr.ifr_name,if_name,IFNAMSIZ-1); if(ioctl(sockfd,SIOCGIFADDR,&ifr) < 0) { printf("SIOCGIFADDR on %s failed: %s/n", if_name, strerror(errno)); return 2; } addr = (struct sockaddr_in *)&(ifr.ifr_addr); address = inet_ntoa(addr->sin_addr); printf("inet addr: %s ",address); if(ioctl(sockfd,SIOCGIFBRDADDR,&ifr) < 0) { printf("SIOCGIFADDR on %s failed: %s/n", if_name, strerror(errno)); return 2; } addr = (struct sockaddr_in *)&ifr.ifr_broadaddr; address = inet_ntoa(addr->sin_addr); printf("Bcast: %s ",address); if(ioctl(sockfd,SIOCGIFNETMASK,&ifr) < 0) { printf("SIOCGIFADDR on %s failed: %s/n", if_name, strerror(errno)); return 2; } addr = (struct sockaddr_in *)&ifr.ifr_addr; address = inet_ntoa(addr->sin_addr); printf("Mask: %s ",address); printf("/n"); return 0; }
三、检测接口的MAC地址
static int network_get_mac(const char *if_name, void *arg) { int skfd; u_char * ptr; char *mac = (char *)arg; struct ifreq ifr = {0}; if (NULL == arg) { fprintf(stderr, "(out)arg is null"); return -1; } if ((skfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { fprintf(stderr, "create socket failed/n"); return -1; } strcpy(ifr.ifr_name, if_name); if (ioctl(skfd,SIOCGIFHWADDR,&ifr) < 0) { fprintf(stderr, "(out)SIOCGIFHWADDR failed: %s", strerror(errno)); return -1; } ptr =(u_char *)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0]; sprintf(mac, "%02x-%02x-%02x-%02x-%02x-%02x",*ptr,*(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5)); return 0; }
四、获取网卡设备名
static int network_get_interface_name(char (*interface_name)[10]) { int skfd; int i, len, count; char *ifreq_pointer = NULL; struct ifreq *result = NULL; struct ifconf get_info = {0}; len = 10 * sizeof(struct ifreq); ifreq_pointer = (char *)calloc(1, len); get_info.ifc_len = len; get_info.ifc_ifcu.ifcu_buf = ifreq_pointer; if ((skfd = socket(AF_INET,SOCK_STREAM,0)) < 0) { fprintf(stderr, "create socket failed/n"); free(ifreq_pointer); ifreq_pointer = NULL; return -1; } if (ioctl(skfd, SIOCGIFCONF, &get_info) < 0) { fprintf(stderr, "SIOCGIFCONF failed: %s", strerror(errno)); free(ifreq_pointer); ifreq_pointer = NULL; return -1; } count = get_info.ifc_len / sizeof(struct ifreq); result = (struct ifreq *)ifreq_pointer; for(i=0; i<count; i++) { strcpy(interface_name[i], result[i].ifr_name); fprintf(stderr, "the %d interface name: %s/n", i, interface_name[i]); } free(ifreq_pointer); ifreq_pointer = NULL; return count; } void main() { int count = 0; char eth_name[5][10] = {0}; count = network_get_interface_name(eth_name); }
五、获取IP地址
static int network_get_ipaddr(char *interface_name, char *ipaddr) { int sockfd; struct ifreq ifr; struct sockaddr_in *sin; if((NULL == interface_name) || (NULL == ipaddr)) { printf("Error argument \n"); return -1; } sockfd = socket(AF_INET, SOCK_DGRAM, 0); if(sockfd < 0) { printf("Socket error \n"); return -1; } bzero((char *)&ifr, sizeof(ifr)); strcpy(ifr.ifr_name, interface_name); if (ioctl(sockfd, SIOCGIFADDR, &ifr) < 0) { printf("ioctl SIOCGIFADDR failed: %s", strerror(errno)); return -1; } #if 1 sin = (struct sockaddr_in *)&ifr.ifr_addr; strcpy(ipaddr, (char *)inet_ntoa(sin->sin_addr)); #else struct sockaddr_in * ptr; ptr = (struct sockaddr_in *)&(ifr.ifr_ifru.ifru_addr); memcpy((char*)ipaddr, (char*)&ptr->sin_addr, sizeof(unsigned long)); *ipaddr = swap32(*ipaddr); printf("IP = %s \n",inet_ntoa(ptr->sin_addr)); #endif close(sockfd); return 0; }