2014年1月3日 星期五

iOS -- 程式中指定透過WIFI 或 3G 進行連線

在手機中使用3G上網往往需要較昂貴的費用,因此app開發者應該要讓使用者可以自由選擇透過3G或是WIFI上網。

要指定建立連線的網路介面,只要使用 bind 函數,指定對應的網路介面即可。在 iOS 中選擇 WIFI 或是 3G 也是同樣的道理,以iPhone為例,其網路介面名稱定義如下

  • WIFI 為 en0
  • 3G 為 pdp_ip0



這裡提供一個簡單的程式範例,可以列出設備上所有的網路卡介面,建立 socket 並綁定到對應的網路介面,接著使用者只要使用此 socket 傳送資料,便可以透過指定的網路介面傳送出去了。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdarg.h>
#include <ifaddrs.h>    // getifaddrs()
#include <sys/ioctl.h>  // struct ifreq


#define INTERFACE_WIFI  "en"        // en0
#define INTERFACE_WWAN	"pdp_ip"    // pdp_ip0

typedef enum
{
    eStunNIC_WIFI,
    eStunNIC_WWAN //GPRS, EDGE, 3G
}eStunNIC;


int GetNICAddr(eStunNIC veStunNIC, struct sockaddr_in *pSockAddr_In);

main()
{
	int sock;
	struct sockaddr_in client;
	
	bzero(&client, sizeof(client));
	
	// List all NIC, and select WIFI or 3G NIC
	res = GetNICAddr(eStunNIC_WIFI, &client);
	if( res < 0 ) {
	  printf( "GetNICAddr Fail\n");
	  return 0;
	}
		
	sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
	
	if( sock < 0 ) {
	  printf( "Error creating socket\n");
	  return 0;
	}
		
	// Bind the socket to interface WIFI or 3G 
	if (bind(sock, (struct sockaddr*)&client, sizeof(client)) < 0) {
	  printf("bind fail\n");
	  close(sock);
	}
	else
	{
	  printf( "bind success\n");
	}
	
	return 0;	
}

int GetNICAddr(eStunNIC veStunNIC, struct sockaddr_in *pSockAddr_In)
{
    int result=0;
    char *pNIC=NULL;
    struct ifaddrs * ifAddrStruct=NULL;
    struct ifaddrs * ifa=NULL;
    void * tmpAddrPtr=NULL;
    
    if(veStunNIC==eStunNIC_WIFI)
        pNIC = INTERFACE_WIFI;
    else if(veStunNIC==eStunNIC_WWAN)
        pNIC = INTERFACE_WWAN;
    else
        return -1;
       
    result = getifaddrs(&ifAddrStruct);
    if(result!=0)
    {
        printf("getifaddrs error!!\n");
        return -1;
    }
    
    printf("List all network interface of this machine!!\n");
    for (ifa = ifAddrStruct; ifa != NULL; ifa = ifa->ifa_next)
    {
        if (ifa ->ifa_addr->sa_family==AF_INET)
        {
            // check it is IP4
            // is a valid IP4 Address
            char addressBuffer[INET_ADDRSTRLEN];
            tmpAddrPtr=&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr;
            
            inet_ntop(AF_INET, tmpAddrPtr, addressBuffer, INET_ADDRSTRLEN);
            printf("\tNIC name:%s, IP Address %s\n", ifa->ifa_name, 
                   addressBuffer);

            // Assume only one WIFI interface and only one 3G interface
            if(strncmp(ifa->ifa_name, pNIC, strlen(pNIC))==0)
            {
                memcpy(pSockAddr_In, ((struct sockaddr_in *)ifa->ifa_addr), 
                       sizeof(struct sockaddr_in));
            }
        }
        else if (ifa->ifa_addr->sa_family==AF_INET6)
        {
            tmpAddrPtr=&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
            char addressBuffer[INET6_ADDRSTRLEN];
            inet_ntop(AF_INET6, tmpAddrPtr, addressBuffer, INET6_ADDRSTRLEN);
            printf("\tNIC name:%s, IP Address %s\n", ifa->ifa_name, 
                   addressBuffer);
        }
    }
    
    if (ifAddrStruct!=NULL)
        freeifaddrs(ifAddrStruct);
    
    if(veStunNIC==eStunNIC_WIFI)
    {
        printf("The address of WIFI NIC is  %s\n\n", inet_ntoa(pSockAddr_In->
               sin_addr));
    }
    else if(veStunNIC==eStunNIC_WWAN)
    {
        printf("The address of WWAN NIC is  %s\n\n", inet_ntoa(pSockAddr_In->
               sin_addr));
    }
    return 0;
}