用jni实现ping

类别:Java 点击:0 评论:0 推荐:
jdk没有提供访问raw socket的类,在java中要实现ping有两种方法:
调用操作系统自带的ping程序,如下:
 /**
  * ping 的一种实现,调用操作系统的ping命令
  */
 public static int ping(String host) {
  String system = (String) (System.getProperty("os.name")).toLowerCase();
  String command = "";
  if (system.indexOf("win") != -1) {
   command += "ping -w 500 " + host;//设置500毫秒的超时
  } else if (system.indexOf("linux") != -1) {
   command += "ping -t 4 " + host; //ping 四次
  } else {
   command += "ping " + host;
  }
  int minTime = Integer.MAX_VALUE, curTime;
  try {
   Process process = Runtime.getRuntime().exec("ping " + host);
   BufferedReader in = new BufferedReader(new InputStreamReader(
     process.getInputStream()));
   String line = null;
   int count = 10, index;
   //最多只读10行
   while ((line = in.readLine()) != null && count-- != 0) {
    line = line.toLowerCase();
    if ((index = line.indexOf("time")) != -1) {
     byte[] buf = line.getBytes();
     int start = 0, end = buf.length, i, j;
     for (i = index + 4; i < buf.length; i++) {
      if (Character.isDigit((char) buf[i])) {
       start = i;
       break;
      }
     }
     if (i == buf.length)
      continue;
     for (j = start; j < buf.length; j++) {
      if (Character.isLetter((char) buf[j])) {
       end = j;
       break;
      }
     }
     curTime = Integer.parseInt(new String(buf, start, end
       - start));
     if (curTime < minTime) {
      minTime = curTime;
     }
    }
   }
  } catch (Exception ex) {
   return Integer.MAX_VALUE;
  }
  return minTime;
 }
用jni,如下:

先写一个Ping类,包含本地方法pingCore(..)

       package zzzhc.net;

/**
 * @author <a href="zzzhc'>mailto:[email protected]">zzzhc </a>
 * 
 */
public class Ping {

    private String host;

    private int timeout = 1000;//mm

    public Ping(String host) {
        this.host = host;
    }

    public Ping(String host, int timeout) {
        this(host);
        this.timeout = timeout;
    }

    /**
     *
     * @return ping time
     */
    public int ping() {
        return pingCore(host, timeout);
    }

    /**
     *
     * @param host
     * @param timeout
     * @return ping time
     */
    public static int ping(String host, int timeout) {
        return pingCore(host, timeout);
    }

    private static native int pingCore(String host, int timeout);

    /**
     * @return Returns the host.
     */
    public String getHost() {
        return host;
    }

    /**
     * @param host
     *            The host to set.
     */
    public void setHost(String host) {
        this.host = host;
    }

    /**
     * @return Returns the timeout.
     */
    public int getTimeout() {
        return timeout;
    }

    /**
     * @param timeout
     *            The timeout to set.
     */
    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public static void main(String[] args) {
        final Ping ping = new Ping("192.168.1.1");
        System.out.println("time=" + ping.ping());
    }

    static {
        System.loadLibrary("ping");
    }
}

实现本地方法
    切换到classes目录下:
    在console下执行javah zzzhc.net.Ping
    得到zzzhc_net_Ping.h,然后再实现这个头文件,生成一个dll
    在vc下新建一个空的dll项目,加入头文件zzzhc_net_Ping.h,新建一个zzzhc_net_Ping.c文件,实现ping的c源代码很容易找到,在zzzhc_net_Ping.c文件里只要简单调用一下就行了,代码:
ping的c源码,忘记在哪找到的了,小有修改
头文件
//
// Ping.h
//
#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>
#include <Ws2tcpip.h>
#ifdef _MSC_VER
#pragma comment(lib,"ws2_32.lib")
#endif
#pragma pack(1)

#define ICMP_ECHOREPLY 0
#define ICMP_ECHOREQ 8

// IP Header -- RFC 791
typedef struct tagIPHDR
{
 unsigned char  VIHL;   // Version and IHL
 unsigned char TOS;   // Type Of Service
 short TotLen;   // Total Length
 short ID;    // Identification
 short FlagOff;  // Flags and Fragment Offset
 unsigned char TTL;   // Time To Live
 unsigned char Protocol;  // Protocol
 unsigned short Checksum;  // Checksum
 struct in_addr iaSrc; // Internet Address - Source
 struct in_addr iaDst; // Internet Address - Destination
}IPHDR, *PIPHDR;


// ICMP Header - RFC 792
typedef struct tagICMPHDR
{
 unsigned char Type;   // Type
 unsigned char Code;   // Code
 u_short Checksum;  // Checksum
 u_short ID;    // Identification
 u_short Seq;   // Sequence
 unsigned char Data;   // Data
}ICMPHDR, *PICMPHDR;


#define REQ_DATASIZE 32  // Echo Request Data size

// ICMP Echo Request
typedef struct tagECHOREQUEST
{
 ICMPHDR icmpHdr;
 DWORD dwTime;
 unsigned char cData[REQ_DATASIZE];
}ECHOREQUEST, *PECHOREQUEST;


// ICMP Echo Reply
typedef struct tagECHOREPLY
{
 IPHDR ipHdr;
 ECHOREQUEST echoRequest;
 unsigned char    cFiller[256];
}ECHOREPLY, *PECHOREPLY;

int Ping(LPCSTR pstrHost,int timeout);
#pragma pack()

实现:
//
// PING.C -- Ping program using ICMP and RAW Sockets
//


#include "ping.h"

// Internal Functions

void ReportError(LPCSTR pstrFrom);
int  WaitForEchoReply(SOCKET s,int timeout);
u_short in_cksum(u_short *addr, int len);

// ICMP Echo Request/Reply functions
int  SendEchoRequest(SOCKET, LPSOCKADDR_IN);
DWORD RecvEchoReply(SOCKET, LPSOCKADDR_IN, u_char *);

static int inited = 0;

void init() {
 WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(1,1);
 if (inited==0) {
  WSAStartup(wVersionRequested, &wsaData);
  inited = 1;
 }
}

void destory() {
 if (inited==1) {
  WSACleanup();
 }
}


// Ping()
// Calls SendEchoRequest() and
// RecvEchoReply() and prints results
int Ping(LPCSTR pstrHost,int timeout)
{
 SOCKET   rawSocket;
 LPHOSTENT lpHost;
 struct    sockaddr_in saDest;
 struct    sockaddr_in saSrc;
 DWORD   dwTimeSent;
 DWORD   dwElapsed;
 u_char    cTTL;
 int       nRet;

 init();

 // Create a Raw socket
 rawSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
 if (rawSocket == SOCKET_ERROR)
 {
  ReportError("socket()");
  return -1;
 }
 
 // Lookup host
 lpHost = gethostbyname(pstrHost);
 if (lpHost == NULL)
 {
  fprintf(stderr,"\nHost not found: %s\n", pstrHost);
  return -1;
 }
 
 // Setup destination socket address
 saDest.sin_addr.s_addr = *((u_long FAR *) (lpHost->h_addr));
 saDest.sin_family = AF_INET;
 saDest.sin_port = 0;

 // Tell the user what we're doing
 printf("\nPinging %s [%s] with %d bytes of data:\n",
    pstrHost,
    inet_ntoa(saDest.sin_addr),
    REQ_DATASIZE);

 
 // Send ICMP echo request
 SendEchoRequest(rawSocket, &saDest);

 // Use select() to wait for data to be received
 nRet = WaitForEchoReply(rawSocket,timeout);
 if (nRet == SOCKET_ERROR)
 {
  ReportError("select()");
 }
 if (!nRet)
 {
  printf("\nTimeOut");
 }

 // Receive reply
 dwTimeSent = RecvEchoReply(rawSocket, &saSrc, &cTTL);

 // Calculate elapsed time
 dwElapsed = GetTickCount() - dwTimeSent;
 printf("\nReply from: %s: bytes=%d time=%ldms TTL=%d",
           inet_ntoa(saSrc.sin_addr),
     REQ_DATASIZE,
           dwElapsed,
           cTTL);
 
 printf("\n");
 nRet = closesocket(rawSocket);
 if (nRet == SOCKET_ERROR)
  ReportError("closesocket()");
 return dwElapsed;
}


// SendEchoRequest()
// Fill in echo request header
// and send to destination
int SendEchoRequest(SOCKET s,LPSOCKADDR_IN lpstToAddr)
{
 static ECHOREQUEST echoReq;
 static nId = 1;
 static nSeq = 1;
 int nRet;

 // Fill in echo request
 echoReq.icmpHdr.Type  = ICMP_ECHOREQ;
 echoReq.icmpHdr.Code  = 0;
 echoReq.icmpHdr.Checksum = 0;
 echoReq.icmpHdr.ID   = nId++;
 echoReq.icmpHdr.Seq   = nSeq++;

 // Fill in some data to send
 for (nRet = 0; nRet < REQ_DATASIZE; nRet++)
  echoReq.cData[nRet] = ' '+nRet;

 // Save tick count when sent
 echoReq.dwTime    = GetTickCount();

 // Put data in packet and compute checksum
 echoReq.icmpHdr.Checksum = in_cksum((u_short *)&echoReq, sizeof(ECHOREQUEST));

 // Send the echo request           
 nRet = sendto(s,      /* socket */
     (LPSTR)&echoReq,   /* buffer */
     sizeof(ECHOREQUEST),
     0,       /* flags */
     (LPSOCKADDR)lpstToAddr, /* destination */
     sizeof(SOCKADDR_IN));   /* address length */

 if (nRet == SOCKET_ERROR)
  ReportError("sendto()");
 return (nRet);
}


// RecvEchoReply()
// Receive incoming data
// and parse out fields
DWORD RecvEchoReply(SOCKET s, LPSOCKADDR_IN lpsaFrom, u_char *pTTL)
{
 ECHOREPLY echoReply;
 int nRet;
 int nAddrLen = sizeof(struct sockaddr_in);

 // Receive the echo reply 
 nRet = recvfrom(s,     // socket
     (LPSTR)&echoReply, // buffer
     sizeof(ECHOREPLY), // size of buffer
     0,     // flags
     (LPSOCKADDR)lpsaFrom, // From address
     &nAddrLen);   // pointer to address len

 // Check return value
 if (nRet == SOCKET_ERROR)
  ReportError("recvfrom()");

 // return time sent and IP TTL
 *pTTL = echoReply.ipHdr.TTL;
 return(echoReply.echoRequest.dwTime);     
}

// What happened?
void ReportError(LPCSTR pWhere)
{
 fprintf(stderr,"\n%s error: %d\n",
  WSAGetLastError());
}


// WaitForEchoReply()
// Use select() to determine when
// data is waiting to be read
int WaitForEchoReply(SOCKET s,int timeout)
{
 struct timeval Timeout;
 fd_set readfds;

 readfds.fd_count = 1;
 readfds.fd_array[0] = s;
 Timeout.tv_sec = timeout/1000;
    Timeout.tv_usec = timeout%1000;
 printf ("timeout=%d\n,sec=%d,usec=%d\n",timeout,timeout/1000,timeout%1000);

 return(select(1, &readfds, NULL, NULL, &Timeout));
}


//
// Mike Muuss' in_cksum() function
// and his comments from the original
// ping program
//
// * Author -
// * Mike Muuss
// * U. S. Army Ballistic Research Laboratory
// * December, 1983

/*
 *   I N _ C K S U M
 *
 * Checksum routine for Internet Protocol family headers (C Version)
 *
 */
u_short in_cksum(u_short *addr, int len)
{
 register int nleft = len;
 register u_short *w = addr;
 register u_short answer;
 register int sum = 0;

 /*
  *  Our algorithm is simple, using a 32 bit accumulator (sum),
  *  we add sequential 16 bit words to it, and at the end, fold
  *  back all the carry bits from the top 16 bits into the lower
  *  16 bits.
  */
 while( nleft > 1 )  {
  sum += *w++;
  nleft -= 2;
 }

 /* mop up an odd byte, if necessary */
 if( nleft == 1 ) {
  u_short u = 0;

  *(u_char *)(&u) = *(u_char *)w ;
  sum += u;
 }

 /*
  * add back carry outs from top 16 bits to low 16 bits
  */
 sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
 sum += (sum >> 16);   /* add carry */
 answer = ~sum;    /* truncate to 16 bits */
 return (answer);
}

zzzhc_net_Ping.c代码如下:
#include "zzzhc_net_Ping.h"
#include "ping.h"

JNIEXPORT jint JNICALL Java_zzzhc_net_Ping_pingCore
(JNIEnv *env, jclass jc, jstring host, jint timeout) {
 const char *str = (*env)->GetStringUTFChars(env, host, 0);
 int elapse = Ping(str,timeout);
 (*env)->ReleaseStringUTFChars(env, host, str);
 return elapse;
}

编译生成ping.dll,将ping.dll放入classes下,切换到classes目录下,执行java zzzhc.net.Ping

这种方法还算直接,更好一点的办法应该是实现一个具有raw socket功能的类,再调用该类的方法实现ping,这样的话可以使java具有所有的网络功能.

本文地址:http://com.8s8s.com/it/it12611.htm