记Android 发送PDU相关问题

2012年12月18日 13:15

 

最近一直搞在android上发送PDU的问题,下面做以下总结:
Android 1.6以后的sendRawPdu好像改为private了,应用程序不能直接发送pdu。下面是几种解决发送二进制数据的方法:
1.sendDataMessage: 这个发送的数据为byte就行。但是它有个端口问题,他的PDU里面有包含端口的信息,接收方也要对端口监听,这个方法网上有很多的介绍。这里不再重复。
sendDataMessage:(hello, bamboo...)
08 91 68 31 10 30 08 05 f0 64 0b a1 51 56 99 79 51 f2 00 04 21 21 11 02 65 11 23 17 06 05 04 03 e8 00 00 68 65 6c 6c 6f 2c 20 62 61 6d 62 6f 6f 2e 2e 2e 
 
sendTextMessage:(hello, bamboo...)
08 91 68 31 10 30 08 05 f0 24 0b a1 51 56 99 79 51 f2 00 00 21 21 11 02 75 51 23 10 e8 32 9b fd 66 81 c4 e1 b6 f8 fd 76 b9 5c 
 
sendDataMessage:(0x01,0x02,0x03,0x04,0x05)
08 91 68 31 10 30 08 05 f0 64 0b a1 51 56 99 79 51 f2 00 04 21 21 11 12 52 43 23 0c 06 05 04 03 e8 00 00 01 02 03 04 05 
2.用sendTextMessage发送
这个接口参数为String,那怎么传二进制给它呢?
例子如:byte[] pData = new byte[]{0x01,0x02,0x03,0x04,0x05,0x06};
sendStringMessage(phonenum,new String(p)),但是你们可以抓取数据包。他的PDU数据编码为Unicode。这个要注意
 
3.短信类SmsManager里,android2.3版本将sendRawPdu方法变成了私有成员,应用程序访问不了,将之改为外部可以正常访问的方法,则可以解决我们遇到的问题.但是这个就要重新编译源码了,这个没验证过
 
4.提供一个接口,将二进制数据传入给内核,然后你们操作modem将此数据作为pdu短信数据发送出去,则也可解决我们遇到的问题。把端口信息部分去掉
 
5.驱动提供AT接口,这个比较蛋疼
                                                                                                by ckfan
                                                                                               2012-12-18

ICMP构建报文,实现Ping

2012年11月05日 19:47

 

首先要了解下ICMP,先前用IcmpSendEcho实现了Ping功能,见 http://ckfan.is-programmer.com/posts/36168.html

    ICMP:是Internet 控制信息协议(ICMP)是 IP 组的一个整合部分,通过 IP 包传送的 ICMP 信息主要用于涉及网络
操作或错误操作的不可达信息。
    ICMP 的报文类型:

    这里再说说下,我原先一些误区: 1.ICMP没有端口,它并不像TCP/UDP那样有发到特定的接收端口,它是控制协议,服务于IP层。当某个网关发现传输错误时,立即向信源主机发 送 ICMP报文,报告出错信息,让信源主机采取相应处理措施,它是一种差错和控制报文协议,不仅用于传输差错报文,还传输控制报文。 2.ICMP 包发送是不可靠的。

 

code:

.h

#pragma once
#include <winsock2.h> 
#include <ws2tcpip.h> 
#pragma comment(lib, "ws2_32.lib")

#pragma pack(1)
// ICMP 头部
typedef struct _ihdr
{
	BYTE i_type;		// 8位类型
	BYTE i_code;		// 8位代码
	USHORT i_cksum;		// 16位校验和
	USHORT i_id;		// 识别号
	USHORT i_seq;		// 报文序列号
	ULONG timestamp;	// 时间戳
}ICMP_HEADER;

typedef struct _iphdr{
	unsigned char  h_lenver;		// 4 位IP版本号+4位首部长度
	unsigned char  tos;			// 8位服务类型TOS
	unsigned short total_len;		// 16位IP包总长度(字节)
	unsigned short ident;			// 16位标识, 用于辅助IP包的拆装,本实验不用,置零
	unsigned short frag_and_flags;	// 3位标志位+13位偏移位, 也是用于IP包的拆装,本实验不用,置零
	unsigned char  ttl;			// 8位IP包生存时间 TTL
	unsigned char  proto;			// 8位协议 (TCP, UDP 或其他), 本实验置ICMP,置为1
	unsigned short checksum;		// 16位IP首部校验和,最初置零,等
							// 所有包头都填写正确后,计算并替换.
	unsigned int   sourceIP;		// 32位源IP地址
	unsigned int   destIP;			// 32位目的IP地址
}IP_HEADER;
#pragma pack()

class CIPing
{
public:
	CIPing();
	~CIPing();
	// PING X.X.X.X
	BOOL Ping(LPCTSTR ip, int timeout);
private:
	SOCKET s;
};

.cpp
#include "stdafx.h"
#include "IPing.h"

// 校验和
unsigned short CheckSum(unsigned short *addr, int count)
{
       /* Compute Internet Checksum for "count" bytes
        * beginning at location "addr".
       */
       register long sum = 0;

       while( count > 1 )
       {
           /* This is the inner loop */
           sum += *addr++;
           count -= sizeof(USHORT);
        }

       /* Add left-over byte, if any */
       if( count > 0 )
           sum += * (unsigned char *) addr;

       /* Fold 32-bit sum to 16 bits */
       while (sum>>16)
           sum = (sum & 0xffff) + (sum >> 16);

       return (USHORT)~sum;
}
CIPing::CIPing()
{

}

CIPing::~CIPing()
{

}

// PING X.X.X.X
BOOL CIPing::Ping(LPCTSTR ip, int timeout)
{
	struct sockaddr_in addrSrc;
	struct sockaddr_in addrDest;
	ICMP_HEADER head;
	UCHAR ucSendBuf[1024];
	UCHAR ucRecBuf[1024];
	int recFromLen;
	IP_HEADER   *pIpHdr = NULL;
	unsigned short len;

	// create
	if((s = WSASocket(AF_INET,SOCK_RAW,IPPROTO_ICMP,NULL,0,WSA_FLAG_OVERLAPPED))==INVALID_SOCKET)
	{
		return FALSE;
	}

	// 发送时限
	setsockopt( s, SOL_SOCKET, SO_SNDTIMEO, ( char * )&timeout, sizeof( int ) );
	
	// 接收时限
	setsockopt( s, SOL_SOCKET, SO_RCVTIMEO, ( char * )&timeout, sizeof( int ) );

	memset(&addrSrc, 0, sizeof(sockaddr_in));

	// addr
	addrSrc.sin_family = AF_INET;
	addrSrc.sin_addr.s_addr = inet_addr(ip);

	// 填充ICMP首部
	memset(&head, 0, sizeof(ICMP_HEADER));
	// 回显应答
	head.i_type = 0;
	head.i_code = 0;	
	// 校验和置0
	head.i_cksum = 0; 
	head.i_id = 2;
	// 时间戳
	head.timestamp = GetTickCount(); 
	head.i_seq = 999;

	memset(ucSendBuf, 0, 1024);
	memcpy(ucSendBuf, &head, sizeof(ICMP_HEADER));
	memset(ucSendBuf + sizeof(ICMP_HEADER), '1', 32);
	head.i_cksum = CheckSum((USHORT*)ucSendBuf, sizeof(ICMP_HEADER) + 32);
	memcpy(ucSendBuf, &head, sizeof(ICMP_HEADER));

	// 发送
	if(sendto(s, (char*)ucSendBuf, sizeof(ICMP_HEADER) + 32, 0, (struct sockaddr*)&addrSrc,sizeof(addrSrc)) ==	SOCKET_ERROR)
	{
		closesocket(s);
		s = NULL;
		return FALSE;
	}

	memset(ucRecBuf, 0, 1024);
	memset(&addrDest, 0, sizeof(struct sockaddr));
	recFromLen = sizeof(sockaddr);
	addrDest.sin_family = AF_INET;
	if (recvfrom(s, (char*)ucRecBuf, 1023, 0, (struct sockaddr*)&addrDest, &recFromLen) == SOCKET_ERROR)
	{
		if(WSAGetLastError() == WSAETIMEDOUT)
		{
			OutputDebugString("Time out...\n");
		}
		closesocket(s);
		s = NULL;
		return FALSE;
	}
	closesocket(s);
	s = NULL;
	
	// 判断包的正确性
	pIpHdr = (IP_HEADER *)ucRecBuf;
	// Number of 32-bit words * 4 = bytes
	// 计算ip包头长度
	len = sizeof(unsigned long) * (pIpHdr->h_lenver & 0xf);  
	memcpy(&head,& ucRecBuf[len], sizeof(ICMP_HEADER));
	// 标识
	if (head.i_id != 2)
	{
		return FALSE;
	}
	return TRUE;
}

在用recvfrom收包的时候,发现都是失败(SOCK_ERROR),后面才发现参数传错了。见MSDN:

 

int recvfrom(
  _In_         SOCKET s,
  _Out_        char *buf,
  _In_         int len,
  _In_         int flags,
  _Out_        struct sockaddr *from,
  _Inout_opt_  int *fromlen
);


Parameters
s [in] 
A descriptor identifying a bound socket.

buf [out] 
A buffer for the incoming data.

len [in] 
The length, in bytes, of the buffer pointed to by the buf parameter.

flags [in] 
A set of options that modify the behavior of the function call beyond the options specified for the associated socket. See the Remarks below for more details.

from [out] 
An optional pointer to a buffer in a sockaddr structure that will hold the source address upon return.

fromlen [in, out, optional] 
An optional pointer to the size, in bytes, of the buffer pointed to by the from parameter.

今天在进行对一张单色bmp读取数据时,碰到了挺多的问题。
这里总结下。
1.对BMP的头部信息了解有误。BMP包含图像文件头,图像由于BMP中信息头,调色板数据,图像数据。
这里调色板数据的大小为N*4byte.这里需要注意的是调试板并不是所有的图片都有调试板的,需要取
决于biCompression,当biCompression成员的值是BI_RGB时,它没有调色板。而且biBitCount大
等于24的时候,没有调试版,biBitCount为32的时候,biCompression为BI_RGB时,没有调试板,
为BI_BITFIELDS原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、
绿、蓝分量在32位中所占的位置。具体的BMP的调试板相关问题就到这里,详细信息MSDN(BITMAPINFOHEADER),
如果想中文版的,可以百科http://baike.baidu.com/view/189487.htm

2.字节对齐问题
// 位图文件头
typedef struct tagBITMAPFILEHEADER
{
	int16u	bfType;			// 位图文件的类型,必须为BM
	int32u	bfSize;			// 位图文件的大小,以字节为单位
	int16u	bfReserved1;	// 位图文件保留字,必须为0
	int16u	bfReserved2;	// 位图文件保留字,必须为0
	int32u	bfOffBits;		// 位图数据的起始位置,以相对于位图
				// 文件头的偏移量表示,以字节为单位
}BITMAPFILEHEADER;

/*test.bmp 头部HEX
00000000h: 42 4D FE 3F 00 00 00 00 00 00 3E 00 00 00 28 00 ; BM?......>...(.
00000010h: 00 00 80 01 00 00 54 01 00 00 01 00 01 00 00 00 ; ..€...T.........
00000020h: 00 00 C0 3F 00 00 00 00 00 00 00 00 00 00 00 00 ; ..?............
00000030h: 00 00 00 00 00 00 FF 00 00 00 00 00 FF 00       ; ............
*/
code:
fs.open("./test.bmp", ios::in);
			
if(fs.is_open() == false)
			
{
	
     printf("\r\n TestMid: open print_header.tmp failed... \r\n");

     continue;
		
}
			
// 读取位图文件头
memset(&file_header, 0, sizeof(BITMAPFILEHEADER));
fs.read((char*)&file_header, 14);
fs.seekg(file_header.bfOffBits);
fs.read(...);
....
可是每次数据读出来什么都没有,加了各种调试信息。后面发现file_header.bfOffBits 为0,
这时候我就奇怪了......后面才想起字节对齐问题,平时也了解了字节对齐相关问题,
但是在实际编程中还是忽略了。

解决上面的问题:
方法一:
fs.read((char*)&file_header.bfType, 2);
			
fs.read((char*)&file_header.bfSize, 4);
			
fs.read((char*)&file_header.bfReserved1, 2);
			
fs.read((char*)&file_header.bfReserved2, 2);
			
fs.read((char*)&file_header.bfOffBits, 4);

方法二:
#pragma pack(1)
 /*指定按1字节对齐*/
// 位图文件头
typedef struct tagBITMAPFILEHEADER
{
	int16u	bfType;			// 位图文件的类型,必须为BM
	int32u	bfSize;			// 位图文件的大小,以字节为单位
	int16u	bfReserved1;	// 位图文件保留字,必须为0
	int16u	bfReserved2;	// 位图文件保留字,必须为0
	int32u	bfOffBits;		// 位图数据的起始位置,以相对于位图
	// 文件头的偏移量表示,以字节为单位
}BITMAPFILEHEADER;
#pragma pack()// 恢复


这里就不再重复字节问题了,具体可以见这个BLOG:http://www.cnblogs.com/logogcn/archive/2010/11/30/1891699.html,写的很好。

IP数据包校验和 source code

2012年11月01日 21:22

最近在学习IP数据包时,学习下了数据包校验和的计算方法

下面是RFC1071 source

 

unsigned short csum(unsigned char *addr, int count)
{
       /* Compute Internet Checksum for "count" bytes
        * beginning at location "addr".
       */
       register long sum = 0;

       while( count > 1 )

       {
           /* This is the inner loop */
           sum += * (unsigned short) addr++;
           count -= 2;
        }

       /* Add left-over byte, if any */
       if( count > 0 )
           sum += * (unsigned char *) addr;

       /* Fold 32-bit sum to 16 bits */
       while (sum>>16)
           sum = (sum & 0xffff) + (sum >> 16);

       return ~sum;
}

Ping (ICMP.dll) 简单实现

2012年10月31日 17:24

代码:
.h
#pragma once
#include <WinSock.h>
// ping 操作接口
typedef struct ip_option_information {
	UCHAR Ttl;
	UCHAR Tos;
	UCHAR Flags;
	UCHAR OptionsSize;
	PUCHAR OptionsData;
} IP_OPTION_INFORMATION, *PIP_OPTION_INFORMATION;

typedef struct icmp_echo_reply
{
	ULONG Address;
	ULONG Status;
	ULONG RoundTripTime;		// 往返时间
	USHORT DataSize;
	USHORT Reserved;
	PVOID Data;
	IP_OPTION_INFORMATION Options;
} ICMP_ECHO_REPLY, *PICMP_ECHO_REPLY; 
typedef HANDLE (WINAPI IcmpCreateFile)(VOID);
typedef BOOL (WINAPI IcmpCloseHandle)(HANDLE IcmpHandle);
typedef DWORD (WINAPI IcmpSendEcho)(
						// 传入由IcmpCreateFile创建的一个测试句柄
						HANDLE IcmpHandle,
						// 要测试的IP地址,(需要用到inet_addr() 函数来转换) 
						ULONG DestinationAddress,
						// 需要发送到IP地址的数据,传入指针
						LPVOID RequestData,
						// 发送的数据大小
						WORD RequestSize,
						// IP头选项,传入指针 
						PIP_OPTION_INFORMATION RequestOptions,
						// 测试后返回的数据,用icmp_echo_reply来接收
						LPVOID ReplyBuffer,
						// ReplyBuffer的大小,一般是 sizeof(icmp_echo_reply) 
						DWORD ReplySize,
						// 一个以MS为单位的值,代表着一个超时值
						DWORD Timeout);
class CPingI
{
public:
	CPingI();
	~CPingI();
	BOOL Ping(int timeout, LPCTSTR pHost);
private:
	HINSTANCE hIcmpDLL;
	IcmpCreateFile* lpIcmpCreateFile;
	IcmpCloseHandle* lpIcmpCloseHandle;
	IcmpSendEcho* lpIcmpSendEcho;
};


.cpp
#include "stdafx.h"
#include "PingI.h"

CPingI::CPingI()
{
	// load
	hIcmpDLL = LoadLibrary("ICMP.DLL");
	if (hIcmpDLL == NULL)
	{
		return;
	}
	lpIcmpCreateFile = (IcmpCreateFile*)GetProcAddress(hIcmpDLL,"IcmpCreateFile");
	lpIcmpCloseHandle = (IcmpCloseHandle*)GetProcAddress(hIcmpDLL,"IcmpCloseHandle");
	lpIcmpSendEcho = (IcmpSendEcho*)GetProcAddress(hIcmpDLL,"IcmpSendEcho");

	if (lpIcmpSendEcho == NULL || lpIcmpCloseHandle == NULL || lpIcmpCreateFile == NULL)
	{
		return;
	}
}
CPingI::~CPingI()
{
	if (hIcmpDLL != NULL)
	{
		FreeLibrary(hIcmpDLL);
		hIcmpDLL = NULL;
	}
}

// ping x.x.x.x
BOOL CPingI::Ping(int timeout, LPCTSTR pHost)
{
	IP_OPTION_INFORMATION ip_option;
	UCHAR sendData[128];
	UCHAR recData[128];
	HANDLE handle;
	PICMP_ECHO_REPLY pEchoReply;
	// 应答个数
	int nReply;
	// error
	if (lpIcmpSendEcho == NULL || lpIcmpCloseHandle == NULL || lpIcmpCreateFile == NULL)
	{
		return FALSE;
	}
	
	memset(&ip_option, 0, sizeof(IP_OPTION_INFORMATION));
	// TTL
	ip_option.Ttl = 128;
	
	// send Data
	memset(sendData, '1', 128);

	// rec Data
	memset(recData, 0, 128);

	pEchoReply = (PICMP_ECHO_REPLY)recData;

	// create handle
	handle = lpIcmpCreateFile();
	if (handle == INVALID_HANDLE_VALUE)
	{
		return FALSE;
	}
	
	// send
	nReply = lpIcmpSendEcho(handle, 
					   inet_addr(pHost),
					   sendData,
					   64,
					   &ip_option,
					   pEchoReply,
					   sizeof(ICMP_ECHO_REPLY) + 64,
					   timeout);

	// close
	lpIcmpCloseHandle(handle);
	if (nReply == 0)
	{
		// GetLastErr
		SetLastError(pEchoReply->Status);
		return FALSE;
	}
	return TRUE;
}

仿函数应用

2012年10月26日 14:31

从未接触过仿函数,这个也是这两天才接触的。它主要是一个class重载了“()”来达到目的。直接见代码吧

// 仿函数
struct max_class
{
public:
	int operator()(int x, int y)
	{
		return x > y ? x : y;
	}
};
template <class T>
int get_max(int x, int y, T t)
{
	return t(x,y);
}
int main()
{
      cout << get_max(11,2,max_class()) << endl;
}

这是在is-programmer的第一篇blog,拉宾米勒算法由一同学提供,谢谢他!

#define type int
type get_rand()
{
     type a;
     a  = rand();
     a* = rand();
     return abs(a);
}

/******a^b mod c *******/
type PowerMod(type a, type b, type c)
{
     type r =1 ;
     while(b)
     {
          if( b & 1 )
          {
               r = r * a % c;
          }
          b >>= 1;
          a = a * a % c;
     }
     return r;
}

/********* 素数随机判定算法 *******/
type mod_mul (type x , type y , type n)
{
     type T =(type)( sqrt((double)n) + 0.5);
     type t = T * T - n;
     type a = x / T;
     type b = x %T;
     type c = y / T;
     type d = y % T;
     type e = a * c / T;
     type f = a * c % T;
     type v = (( a * d + b * c) % n + e * t ) % n;
     type g = v / T;
     type h = v % T;
     type ans = ((( f + g) * t % n + b * d) % n + h * T) % n;
     while ( ans < 0)
     ans += n;
     return ans ;
}
/*
 米勒-拉宾法算法
*/
type Rabin_Miller(type n)
{
     type k = 0, i, j, m, a;
     if( n < 2)
          return 0;
     if(n == 2)
          return 1;
     if( !(n & 1) )
      return 0;

     m = n - 1;
 
     while( !( m & 1 ) )
          m >>= 1, k++;
 
     for(i = 0; i < 10; i++)
     {
          a = get_rand() % (n - 2) + 2;
          a = PowerMod(a, m, n);
          if(a == 1)
               continue;
          for(j = 0; j < k; j++)
          {
               if(a == n-1)
                    break;
               a = mod_mul(a, a, n);
          }
          if(j < k)
               continue;
      return 0;
     }
     return 1;
}

/*
获取特定长度的随机素数
*/
type Get_Rand_Prime(type bytes)
{
     type i;
     type prime;

     bytes = 0;
     prime = 0;
     i = 0;
     while (1)
     {
          prime = 0;
          i = bytes;
          while(i--)
          {
               prime = prime * 10 + rand() % 10;
               if (i ==( bytes - 1) && prime == 0)
               {
                    prime = 1;
               }
          }
          prime = prime | 0x01;
          if (Rabin_Miller(prime))
          {
               break;
          }
    }
    return prime;
}