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:zzzhc0508@hotmail.com">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具有所有的网络功能.