日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java实现的端口映射器

發布時間:2024/8/1 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java实现的端口映射器 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
java實現的端口映射器

下載源代碼


〖 作者:javafound 〗〖 大小:152k 〗〖 發布日期:2007-05-18 〗〖 瀏覽:0 〗

1.介紹:
????? 本文手把手的詳解了jPortMap端口映射程序開發中的每一步,做為己運行在實際的企業項目中的應用, jPortMap程序較全面的展示了Thread、List、Vector、Socket、ServerSocket、Input/OutpuStream、File Read/Write、Properties等核心API的用法,是初學者快速進階的一個優秀案例。

??????? 在涉及內外網數據交換的網絡應用系統開發中,我們經常需要做端口映射,比如放在外部網絡主機上的程序要與內部網絡上的某臺機器建主TCP/IP連結,如下圖(1)示:

??? C機器可以與A機連通,但要與B機連通,由與不在同一網絡,就無能為力了;這時,就需在A機器上做交換或是轉發,來接通C與B之間的TCP/IP連結,即C機先與A機器建立Socket連結,A再與B機建立連結,然后由A在中間轉發C與B通信的數據;B機器上可能運行著數據庫,WebService等在Tcp/IP上通信的程序,而C機器必須訪問這些服務。這里A機器就充當像現實生活中介紹人的角色,負責將C、B之間的通信數據流在Socket上轉發;

圖1

????? 因此,A機需實現端口轉發功能,在Liunx上,可以通過配置IPTable由OS實現,在本例中,我們將開發一個java實現的端口轉發程序jPortMap,此程序將運行在A機器上,以實現轉發C與B之間通信的轉發。

2.源碼下載及測試說明:

從www.NetJava.cn上下載源代碼解壓后,可看到如下目錄結構:



???? 現在,你可以修改一下jPortMap.cfg中的配置,比如,想通過本機的127.0.0.1地址上的8899端口轉發到10.10.3.156,則這樣配置:
##本地IP
LocalIP.1 = 127.0.0.1
##本地端口
LocalPort.1 = 8899
##目標IP
DestHost.1 = 10.10.3.156
##目標端口
DestPort.1 = 80
##客戶端IP過濾表,*表示許可模糊匹配
AllowClient.1 = *.*.*.*

,雙擊jPortMap.bat啟動程序后,在你的IE里輸入http://127.0.0.1:8899試試看:)

3.jPortMap程序類的構成說明:
jPortMap由Main.java、Server.java、Transfer.java、Route.java、SysLog.java五個類構成。

類文件功能概要:
Main.java:程序啟動主類,負責從配置文件讀取轉發的配置參數,啟動轉發服務器;

Server.java:其實是一個ServerSocket服務器,接受C機器進入的Socket連結請求,生成Transfer.對象,由Transfer負責在本機(A上)轉發B和C之間的通信。

Route.java:轉發對象的數據模板類,用來將轉發配置映射為java對象,以由Server,ransfer對象使用。

Transfer.java:按字面意思,可理解為“傳送者”,如在圖示中,當C要通過A連結B時,是先連結到A機上,這里在C和A間生成一個socket對象,Transfer對象則使用這個生成的socket對象和這個傳輸任務的Route對象執行具體的轉發任務。

SysLog.java:jPortMap是一個服務器端程序,在運行中可能會出現錯誤,因此需要一個日志工具,日志工具在jPortMap中只有一個對象存在,負責記錄每天程序運行的信息,如錯誤,警行,一般信息等。

配置文件:
??? cfg/jPortMap.cfg:這是一個文本文件,其中存放jPortMap的配置數據,當程序啟動時,主類會從中讀取數據配置程序,以生成多個Route對象在內存中保持數據。

4.Route.java解析:

我們己經說明,Route類是轉發對象配置數據的模板類,當jPortMap啟運時,它需要知道如下配置:
1. 有多少處轉發任務(意味著要監聽哪幾個ServerSocket);
2. jPortMap程序對每個轉發任務要啟動的監聽ServerSocket端口及所綁定的IP地址;
3. 每個轉發任務的目標IP地址和端口;

??? 因此,jPortMap一但啟動,可能會創建多個Route對象,而每個具體的Route對象則保布著一個轉發任務的以上配置數據。
另外,從安全方面著想,我們的jPortMap程序還需要對請求進入的連結進行安全管理,這里我們簡單的用IP過濾的方法,即jPortMap中ServerSocekt監聽到的進入連結請求會認證IP地址,如發現IP地址沒有在許可的列表中,則斷開這個請求;所以Route類還要保存每個任務對應的許可IP表;
我們的Route.java源文件如下:

/** Route.java** Created on 2006年12月28日, 下午12:36** To change this template, choose Tools | Template Manager* and open the template in the editor.*/ package org.netjava.jportmap; /***轉發任務的配置數據對象模板*

Company: www.NetJava.org

* @author javafound*/ public class Route {public Route() {}//jPortMap綁定的IPString LocalIP="";//監聽的端口int LocalPort=0;//轉發數據的目標機器IPString DestHost="";//轉發的目標端口int DestPort=0;//這個轉發上許可進入的IP列表String AllowClient="";
//重寫的toString方法,輸出具體Route對象的信息以便debug public String toString() {StringBuffer stb = new StringBuffer();stb.append(" LocalADD " + LocalIP);stb.append(" :" + LocalPort);stb.append(" --->DestHost " + DestHost);stb.append(" :" + DestPort);stb.append(" (AllowClient) " + AllowClient);return stb.toString();} }
????? 可以比對cfg/jPortMap.cfg(可用notepad打開)中的內容,Route類只需要據文本件中的配配生成多個Route對象或者說轉發任務,再由其它對象來使用,因此,Route類的功能和結構很簡單,就像映射表結構的javaBean一樣,只是負責保存數據在內存中。

5. SysLog.java解析:

SysLog保存每天的日志信息到指定的目錄下,簡單的說就是提供方法供別的對象來調用,寫內容到文件中:
package org.netjava.jportmap; import java.io.*; import java.util.Calendar;
/*** Title: 端口轉發器
* Description:日志工具類 * Copyright: Copyright (c) 2005* Company: www.NetJava.org* @author javafound* @version 1.0*/
public class SysLog {//記錄輸出一般信息public static void info(String s) {writeToTodayLog("INFO :", s);}記錄警告信息public static void warning(String s) {writeToTodayLog("WARN:", s);}//記錄錯誤信息 public static void severe(String s) {writeToTodayLog("ERROR:", s);} //輸出到當天日志文件的具體實現private static void writeToTodayLog(String flag, String msg) {RandomAccessFile raf = null;try {Calendar now = Calendar.getInstance();String yyyy = String.valueOf(now.get(java.util.Calendar.YEAR));String mm = String.valueOf(now.get(Calendar.MONTH) + 1);String dd = String.valueOf(now.get(Calendar.DAY_OF_MONTH));String hh = String.valueOf(now.get(Calendar.HOUR_OF_DAY));String ff = String.valueOf(now.get(Calendar.MINUTE));String ss = String.valueOf(now.get(Calendar.SECOND));mm = (1 == mm.length()) ? ("0" + mm) : mm;dd = (1 == dd.length()) ? ("0" + dd) : dd;hh = (1 == hh.length()) ? ("0" + hh) : hh;ff = (1 == ff.length()) ? ("0" + ff) : ff;ss = (1 == ss.length()) ? ("0" + ss) : ss;String yyyymmdd = yyyy + mm + dd;String hhffss=hh+ff+ss;String path = System.getProperties().getProperty("user.dir")+ File.separator + "log";File p = new File(path);if (!p.exists()) {p.mkdirs();}path += File.separator + "jPortMap_" + yyyymmdd + ".log";File f = new File(path);if (f.isDirectory()) {f.delete();}raf = new RandomAccessFile(f, "rw");raf.seek(raf.length());raf.writeBytes(hhffss+" "+flag + " : " + msg + "/r/n");raf.close();} catch (Exception ex) {System.out.println("write file has error=" + ex);}}/** Creates a new instance of SysLog *做為一個工具類,一般不需要實例化,所以此處private*/private SysLog() {} }

說明:
首先我們看到提供的三個公用靜態方法:
//記錄一般信息
public static void info(String s)

記錄警告信息
public static void warning(String s)

//記錄錯誤信息
public static void severe(String s)

???? SysLog做為系統中的工具類,一般是不需要實例化的,所以只提供調用功能即可,這三個調用方法為其它對象提供了調用接口,分別輸出不同類型的信息到目志中,而調用對象并不需要去關心具體日志的格式,日志文件命令,文件讀寫等問題----只需傳入要記錄的消息即可。

???? System.getProperties()返回一個Properties對象,其實是一個Map接口的實現,其中存入格式為 名字:值 一一對應的表,系統的許多環境變量,如程序運行的當前目錄user.dir,操作系統類型,java當前版本等都在其中存放。

???? RandomAccessFile:在寫日志時使用了這個類向日志文件中寫入內容,其中seek(int length)可以指定跳過文件中內容的長度后再開始寫入;這樣我們的日志就不會丟失。

6.Server.java解析:
????? 如其名,Server是一個轉發服務器的實現類,我們的jPortMap可同時執行多個轉發服務,所以每個Server對象都將做為一個獨立的線程運行,在jPortMap.cfg中配置了幾個轉發任務,系統就會實例幾個Route對象,并生成對應個數的的Server對象,每個Server對象使用自己的一個Route對象的數據在指定的端口啟動監聽服務,等待客戶端(如前面圖示則是C機器)發起的連結,接收到連結請求并通過IP驗證后,這個Server對象則將具體的轉發任務交給自己的一個Transfer對象去獨立處理,而Server對象則繼續運行,等待到來的連結請求。

????? 我們可以將這個Server理解為一個看門人的角色---使用ServerSocket監聽指定端口,等待到來的連結,它只負責接待來客,并核查來客的身份,如核查通過,至于來客進的門怎么辦,它不管-----由它所持有的另外一個對象Transfer類的一個實例去處理。解析代碼如下:

package org.netjava.jportmap; import java.net.*; import java.util.*; /*** Title: 端口轉發器* Description:啟動監聽服務 * Copyright: Copyright (c) 2005* Company: www.NetJava.org* @author javafound* @version 1.0*/
public class Server extends Thread {//創建一個轉發服務器public Server(Route route, int id) {this.route = route;connectionQueue = new Vector();myID = id;start();}//關閉這個服務器:public void closeServer() {isStop = true;if (null != myServer) {closeServerSocket();} while (this.connectionQueue.size() > 0) {Transfer tc = (Transfer) connectionQueue.remove(0);tc.closeSocket(tc.socket);tc = null;}} //啟動轉發服務器的執行線程public void run() {SysLog.info(" start Transfer......:" + route.toString());ServerSocket myServer = null;try {InetAddress myAD = Inet4Address.getByName(route.LocalIP);myServer = new ServerSocket(route.LocalPort, 4, myAD);} catch (Exception ef) {SysLog.severe("Create Server " + route.toString() + " error:" + ef);closeServerSocket();return;}SysLog.info("Transfer Server : " + route.toString() + " created OK");while (!isStop) {String clientIP = "";try {Socket sock = myServer.accept();clientIP = sock.getInetAddress().getHostAddress();if (checkIP(route, clientIP)) {SysLog.warning(" ransfer Server : " + route.toString() +" Incoming:" + sock.getInetAddress());sock.setSoTimeout(0);connCounter++;Transfer myt = new Transfer(sock, route);connectionQueue.add(myt);} else {SysLog.warning(" ransfer Server : " + route.toString() +" Refuse :" + sock.getInetAddress());closeSocket(sock);}} catch (Exception ef) {SysLog.severe(" Transfer Server : " + route.toString() +" accept error" + ef);}}}//檢測進入的IP是否己許可private static boolean checkIP(Route route, String inIP) {String[] inI = string2StringArray(inIP, ".");String[] list = string2StringArray(route.AllowClient, ".");if (inI.length != list.length) {SysLog.severe(" Transfer Server Error Cfg AllowClient : " +route.toString());return false;}for (int i = 0; i < inI.length; i++) {if ((!inI[i].equals(list[i])) && !(list[i].equals("*"))) {System.out.println(": " + inI[i] + " :" + list[i]);return false;}}return true;}/** @param srcString 原字符串* @param separator 分隔符* @return 目的數組*/private static final String[] string2StringArray(String srcString,String separator) {int index = 0;String[] temp;StringTokenizer st = new StringTokenizer(srcString, separator);temp = new String[st.countTokens()];while (st.hasMoreTokens()) {temp[index] = st.nextToken().trim();index++;}return temp;}//關閉ServerSocketprivate void closeServerSocket() {try {this.myServer.close();} catch (Exception ef) {}}private void closeSocket(Socket s) {try {s.close();} catch (Exception ef) {}}//服務器private ServerSocket myServer = null;//連結隊列控制private boolean isStop = false;//private Vector connectionQueue = null;private int connCounter = 0;// 路由對象private Route route = null;//連結的ID號,暫未用private static int myID = 0; }?? Server類關鍵功能是在一個獨立的線程中執行監聽任務,當我們實例化一個ServerSocket時,即綁定了本機的一個IP和端口,這個ServerSocket對象就在這個地址(由IP和端口組成)上通過調用accept()方法等待客戶端連結,默認情況下,這個等待會一直持續,直到有一個連結進入----生成一個socket對象;
而我們的ServerSocket.accept()是在一個wilhe循環中,這保證了監聽服務器不會中途退出。

7. Transfer.java解析
????? 在分析Server.java中我們看到,Server做為一個服務器,在與客戶端建立連結后使用生成的Socket對象和自己的Routc對象來實例化一個Transfer,具體的傳輸工作就交給了Transfer對象完成。

??? Server生成的Socket對象是機器C與A之間連結的一個代碼,通過這個Socekt對象上的Input/OutPut Stream,可以讓C與A之間通信----工作還只完成了一半,這里我們還需要建立A與B之間的Socket連結,這里就出現了兩個Socket連結,分別是C與A間,我們叫SocketCA;A與B間我們假設叫做SocketAB; Transfer對象的任務就是行建立SocketAB,然后,將SocketCA的輸入寫入到SocketAB的輸出流,將SocketAB的輸出流寫到SocketCA的輸出流中,這樣,就完成了C,B機器之間的數據轉發。
package org.netjava.jportmap; import java.net.*; import java.io.*;
/*** Title: 端口轉發器* Description: 對連結進行轉發處理* Copyright: Copyright (c) 2005* Company: www.NetJava.org* @author javafound* @version 1.0*/
public class Transfer extends Thread {/*** 創建傳輸對象* @param s Socket :進入的socket* @param route Route:轉發配置*/public Transfer(Socket s, Route route) {this.route = route;this.socket = s;this.start();}// 執行操作的線程public void run() {Socket outbound = null;try {outbound = new Socket(route.DestHost, route.DestPort);socket.setSoTimeout(TIMEOUT);InputStream is = socket.getInputStream();outbound.setSoTimeout(TIMEOUT);OutputStream os = outbound.getOutputStream();pipe(is, outbound.getInputStream(), os, socket.getOutputStream());} catch (Exception e) {SysLog.severe(" transfer error:" +route.toString()+ " :" + e);} finally {SysLog.warning("Disconnect :"+ route.toString());closeSocket(outbound);closeSocket(socket);}}/***傳輸的實現方法*/private void pipe(InputStream is0, InputStream is1,OutputStream os0, OutputStream os1) {try {int ir;byte bytes[] = new byte[BUFSIZ];while (true) {try {if ((ir = is0.read(bytes)) > 0) {os0.write(bytes, 0, ir);} else if (ir < 0) {break;}} catch (InterruptedIOException e) {}try {if ((ir = is1.read(bytes)) > 0) {os1.write(bytes, 0, ir);// if (logging) writeLog(bytes,0,ir,false);} else if (ir < 0) {break;}} catch (InterruptedIOException e) {}}} catch (Exception e0) {SysLog.warning(" Method pipe" + this.route.toString() + " error:" +e0);}}//關閉socketvoid closeSocket(Socket s) {try {s.close();} catch (Exception ef) {}}//傳輸任務的Route對象 Route route = null;// 傳入數據用的Socket Socket socket;//超時static private int TIMEOUT = 1000;//緩存static private int BUFSIZ = 1024; }8.Main.java解析
OK,至此己萬事具備!我們需要一個啟動主類,根據讀入的配置文件數據來啟動轉發服務器,執行轉發工作:
package org.netjava.jportmap; import java.io.*; import java.util.*; import java.net.*; /*** Title: 端口轉發器* Description:啟動主類:讀取配置,啟動監聽服務 * Copyright: Copyright (c) 2005* Company: www.NetJava.org* @author javafound* @version 1.0*/
public class Main {//start......public static void main(String args[]) {startService();} //startpublic static void startService() {if (!loadCfgFile()) {System.exit(1);} while (serverList.size() > 0) {Server ts = serverList.remove(0);ts.closeServer();}for (int i = 0; i < routeList.size(); i++) {Route r = routeList.get(i);Server server = new Server(r, i);serverList.add(server);}} // 停止服務接口,備用其它模塊調用public static void stop() {while (serverList.size() > 0) {Server ts = serverList.remove(0);ts.closeServer();}}/***從配置文件讀取數據,生成Route對象* read cfg parameter* @return boolean*/private static boolean loadCfgFile() {try {String userHome = System.getProperties().getProperty("user.dir");if (userHome == null) {userHome = "";} else {userHome = userHome + File.separator;}userHome += "cfg" + File.separator + "jPortMap.cfg";InputStream is = new FileInputStream(userHome);Properties pt = new Properties();pt.load(is);//共有幾個業務模塊int ServiceCount = Integer.parseInt(pt.getProperty("TransferCount"));for (; ServiceCount > 0; ServiceCount--) {Route r = new Route();r.LocalIP = pt.getProperty("LocalIP." + ServiceCount).trim();r.LocalPort = Integer.parseInt(pt.getProperty("LocalPort." +ServiceCount).trim());r.DestHost = pt.getProperty("DestHost." + ServiceCount).trim();r.DestPort = Integer.parseInt(pt.getProperty("DestPort." +ServiceCount).trim());r.AllowClient = pt.getProperty("AllowClient." + ServiceCount).trim();routeList.add(r);}is.close();SysLog.info("ystem Read cfg file OK");} catch (Exception e) {System.out.println("找不到配置文件:"+e);SysLog.severe("loadCfgFile false :" + e);return false;}return true;}//Server服務器集合private static List< Server> serverList = new ArrayList();//Route集合private static List< Route> routeList = new ArrayList(); }????

Main類中需要注意的是loadCfgFile()方法,它用來讀取當前目錄下面cfg/jPortMap.cfg文件中的配置數據,如讀取成功,返加ture值,如讀取失敗,程序測會退出。
另外:
//Server服務器集合
private static List<Server> serverList = new ArrayList();

//Route集合
private static List<Route> routeList = new ArrayList();

這兩行代碼,生成兩個列表,來保存己啟動的Server對象和Route對象。

???? 現在,我們只要啟動Main類,jPortMap就開始運行了,同時會在log目錄下行成每天的運行日志;當然,千萬不要忘了cfg/目錄下面jPortMap.cfg中配置轉發的參數,配置的具體說明在該文件中有注解。
源碼目錄結構圖(NetBean中):

9.改進設想:
?????? 無論如何,這還是個比較簡陋的程序!假如我們把配置改成XML格式、假如我們使用Thread Pool來執行任務、假如我們使用NIO、假如我們再做一套PL的UI界面….,您的任何建議,都會是對jPortMap走向完美的支持,請登陸www.NetJava.cn發表您的看法,發布您的創新!當然,www.NetJava.cn現在己增加了許多新東東讓您欣賞!

總結

以上是生活随笔為你收集整理的java实现的端口映射器的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。