編最近對Node特別感興趣,所以就趁著周末的時間搭了一個簡單的登錄注冊的一個小后臺。
這部分主要是,從前端發(fā)送ajax請求到后臺,如果返回參數(shù)是成功,則登錄,否則,打印相關(guān)參數(shù);
代碼
這是界面
后臺用的是Node.js,框架用的express,數(shù)據(jù)庫用的mysql;
數(shù)據(jù)庫結(jié)構(gòu)
表結(jié)構(gòu)
node代碼如下:
express是node的基礎(chǔ)框架,這里邊有坑的地方是:返回值的時候,必須返回對象。
掌握一門后臺語言,對于現(xiàn)在的前端工作來說,越來越重要了,代碼之路,漫漫求索,共勉之,兄弟姐妹們。
于網(wǎng)絡(luò)用戶來說,一定都經(jīng)歷過出門在外無法直接在異地訪問公司的ERP系統(tǒng)、或是難以部署異地遠(yuǎn)程桌面,因此心急如焚的情況;對于企業(yè)來說,無論是財(cái)務(wù)管理軟件難以將分店信息同步到總部進(jìn)行統(tǒng)計(jì)匯總、還是員工出差在外或在家里就不能訪問企業(yè)內(nèi)部辦公系統(tǒng),都極大地影響了公司整體效率;對于個人開發(fā)者來說,微信小程序或者在線支付系統(tǒng)等開發(fā)環(huán)境往往需要一個可以環(huán)境進(jìn)行調(diào)試,不然的話,難以進(jìn)行開發(fā)調(diào)試。
諸如此類的難題眾多,但解決方法其實(shí)很簡單,那就是使用軟件或者自己手寫一個,可以支持訪問我的電腦上的微信支付接口,從而實(shí)現(xiàn)這一系列的簡易操作。目前國內(nèi)這方面企業(yè)級的服務(wù)商有**殼和神卓互聯(lián),我接觸過很多公司在用,**殼的技術(shù)是PHTunnel ,神卓互聯(lián)用的是Wangooe Tunnel技術(shù),這里就介紹神卓互聯(lián)的,接下來就介紹和分析這款軟件的用法和技術(shù)要點(diǎn)。如果沒有接觸過這方面技術(shù)的同學(xué)可以看一下這個圖:
首先用法很簡單,就是在界面上創(chuàng)建一條映射規(guī)則,填寫應(yīng)用名稱和要連接的內(nèi)網(wǎng)應(yīng)用主機(jī)地址和端口號,
填寫自己要穿透的應(yīng)用名稱和端口號,如果需要獲取原訪問者IP最好是選擇Web應(yīng)用。提交提交就可以了。
例如我需要發(fā)布一個Tomcat應(yīng)用,訪問端口號是7070,那么應(yīng)用名稱填寫tomcat,內(nèi)網(wǎng)主機(jī)填寫127.0.0.1,內(nèi)網(wǎng)端口填7070點(diǎn)提交就可以。
首先新建一個web項(xiàng)目
新建login.jsp登陸文件,內(nèi)容如下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>登錄系統(tǒng)</title>
<style type="text/css">
table td{font: 14px/1.5 'Microsoft YaHei',arial,tahoma,\5b8b\4f53,sans-serif;}
</style>
</head>
<body>
<table>
<tr><td>用戶名</td><td><input type="text"></td></tr>
<tr><td>密碼</td><td><input type="text"></td></tr>
<tr><td> </td><td><input type="submit" value="登錄"></td></tr>
</table>
</body>
</html>
先在本地運(yùn)行,看項(xiàng)目是否可以正常運(yùn)行:
本地運(yùn)行沒有問題,可以正常打開,接下來就試一下外網(wǎng)訪問
打開神卓互聯(lián)軟件主界面,右鍵選擇外網(wǎng)訪問
如果需要綁定域名訪問的話也很簡單,這里不多說。
接下來就分析是如何做到將請求轉(zhuǎn)發(fā)到內(nèi)網(wǎng)因?yàn)橛址祷亟o訪問客戶端的。
InetAddress
//獲取本機(jī)的InetAddress實(shí)例
InetAddress address=InetAddress.getLocalHost();
address.getHostName();//獲取計(jì)算機(jī)名
address.getHostAddress();//獲取IP地址
byte[] bytes=address.getAddress();//獲取字節(jié)數(shù)組形式的IP地址,以點(diǎn)分隔的四部分
//獲取其他主機(jī)的InetAddress實(shí)例
InetAddress address2=InetAddress.getByName("其他主機(jī)名");
InetAddress address3=InetAddress.getByName("IP地址");
URL類
//創(chuàng)建一個URL的實(shí)例
URL baidu=new URL("http://www.baidu.com");
URL url=new URL(baidu,"/index.html?username=tom#test");//?表示參數(shù),#表示錨點(diǎn)
url.getProtocol();//獲取協(xié)議
url.getHost();//獲取主機(jī)
url.getPort();//如果沒有指定端口號,根據(jù)協(xié)議不同使用默認(rèn)端口。此時getPort()方法的返回值為 -1
url.getPath();//獲取文件路徑
url.getFile();//文件名,包括文件路徑+參數(shù)
url.getRef();//相對路徑,就是錨點(diǎn),即#號后面的內(nèi)容
url.getQuery();//查詢字符串,即參數(shù)
以下就是P2P打洞核心代碼(TCP)
假設(shè)現(xiàn)在有以下3臺機(jī)器:
外網(wǎng)機(jī)器,IP:121.56.21.85 , 以下簡稱“主機(jī)A”
處在內(nèi)網(wǎng)1下的機(jī)器,外網(wǎng)IP:106.116.5.45 ,內(nèi)網(wǎng)IP:192.168.1.10, 以下簡稱“主機(jī)1”
處在內(nèi)網(wǎng)2下的機(jī)器,外網(wǎng)IP:104.128.52.6 ,內(nèi)網(wǎng)IP:192.168.0.11,以下簡稱“主機(jī)2”
很顯然內(nèi)網(wǎng)的兩臺機(jī)器不能直接連接,我們現(xiàn)在要實(shí)現(xiàn)的是借助外網(wǎng)機(jī)器,讓兩臺內(nèi)網(wǎng)機(jī)器進(jìn)行tcp直連通訊。
實(shí)現(xiàn)過程如下:
1、主機(jī)A啟動服務(wù)端程序,監(jiān)聽端口8888,接受TCP請求。
2、啟動主機(jī)1的客戶端程序,連接主機(jī)A的8888端口,建立TCP連接。
3、啟動主機(jī)2的客戶端程序,連接主機(jī)A的8888端口,建立TCP連接。
4、主機(jī)2發(fā)送一個命令告訴主機(jī)A,我要求與其他設(shè)備進(jìn)行連接,請求協(xié)助進(jìn)行穿透。
5、主機(jī)A接收到主機(jī)2的命令之后,會返回主機(jī)1的外網(wǎng)地址和端口給主機(jī)2,同時把主機(jī)2的外網(wǎng)地址和端口發(fā)送給主機(jī)1。
6、主機(jī)1和主機(jī)2在收到主機(jī)A的信息之后,同時異步發(fā)起對對方的連接。
7、在與對方發(fā)起連接之后,監(jiān)聽本地與主機(jī)A連接的端口(也可以在發(fā)起連接之前),(由于不同的操作系統(tǒng)對tcp的實(shí)現(xiàn)不盡相同,有的操作系統(tǒng)會在連接發(fā)送之后,把對方的連接當(dāng)作是回應(yīng),即發(fā)出SYN之后,把對方發(fā)來的SYN當(dāng)作是本次SYN的ACK,這種情況就不需要監(jiān)聽也可建立連接,本文的代碼所在測試環(huán)境就不需要監(jiān)聽,測試環(huán)境為:服務(wù)器centos 7.3, 內(nèi)網(wǎng)1 win10,內(nèi)網(wǎng)2 win10和centos7.2都測試過)。
8、主機(jī)1和主機(jī)2成功連上,可以關(guān)閉主機(jī)A的服務(wù),主機(jī)1和主機(jī)2的連接依然會持續(xù)生效,不關(guān)閉就形成了一個3方直連的拓?fù)渚W(wǎng)狀結(jié)構(gòu)網(wǎng)絡(luò)。
服務(wù)器端代碼:
package org.inchain.p2p;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
* 外網(wǎng)端服務(wù),穿透中繼
*
* @author ln
*
*/
public class Server {
public static List<ServerThread> connections=new ArrayList<ServerThread>();
public static void main(String[] args) {
try {
// 1.創(chuàng)建一個服務(wù)器端Socket,即ServerSocket,指定綁定的端口,并監(jiān)聽此端口
ServerSocket serverSocket=new ServerSocket(8888);
Socket socket=null;
// 記錄客戶端的數(shù)量
int count=0;
System.out.println("***服務(wù)器即將啟動,等待客戶端的連接***");
// 循環(huán)監(jiān)聽等待客戶端的連接
while (true) {
// 調(diào)用accept()方法開始監(jiān)聽,等待客戶端的連接
socket=serverSocket.accept();
// 創(chuàng)建一個新的線程
ServerThread serverThread=new ServerThread(socket);
// 啟動線程
serverThread.start();
connections.add(serverThread);
count++;// 統(tǒng)計(jì)客戶端的數(shù)量
System.out.println("客戶端的數(shù)量:" + count);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
package org.inchain.p2p;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
/**
* 外網(wǎng)端服務(wù)多線程處理內(nèi)網(wǎng)端連接
*
* @author ln
*
*/
public class ServerThread extends Thread {
// 和本線程相關(guān)的Socket
private Socket socket=null;
private BufferedReader br=null;
private PrintWriter pw=null;
public ServerThread(Socket socket) throws IOException {
this.socket=socket;
this.br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
this.pw=new PrintWriter(socket.getOutputStream());
}
// 線程執(zhí)行的操作,響應(yīng)客戶端的請求
public void run() {
InetAddress address=socket.getInetAddress();
System.out.println("新連接,客戶端的IP:" + address.getHostAddress() + " ,端口:" + socket.getPort());
try {
pw.write("已有客戶端列表:" + Server.connections + "\n");
// 獲取輸入流,并讀取客戶端信息
String info=null;
while ((info=br.readLine()) !=null) {
// 循環(huán)讀取客戶端的信息
System.out.println("我是服務(wù)器,客戶端說:" + info);
if (info.startsWith("newConn_")) {
//接收到穿透消息,通知目標(biāo)節(jié)點(diǎn)
String[] infos=info.split("_");
//目標(biāo)節(jié)點(diǎn)的外網(wǎng)ip地址
String ip=infos[1];
//目標(biāo)節(jié)點(diǎn)的外網(wǎng)端口
String port=infos[2];
System.out.println("打洞到 " + ip + ":" + port);
for (ServerThread server : Server.connections) {
if (server.socket.getInetAddress().getHostAddress().equals(ip)
&& server.socket.getPort()==Integer.parseInt(port)) {
//發(fā)送命令通知目標(biāo)節(jié)點(diǎn)進(jìn)行穿透連接
server.pw.write("autoConn_" + socket.getInetAddress().getHostAddress() + "_" + socket.getPort()
+ "\n");
server.pw.flush();
break;
}
}
} else {
// 獲取輸出流,響應(yīng)客戶端的請求
pw.write("歡迎您!" + info + "\n");
// 調(diào)用flush()方法將緩沖輸出
pw.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println("客戶端關(guān)閉:" + address.getHostAddress() + " ,端口:" + socket.getPort());
Server.connections.remove(this);
// 關(guān)閉資源
try {
if (pw !=null) {
pw.close();
}
if (br !=null) {
br.close();
}
if (socket !=null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
public String toString() {
return "ServerThread [socket=" + socket + "]";
}
}
最后附上測試方法和運(yùn)行效果:
使用方法:
1、在服務(wù)器啟動Server。
2、在客戶端1啟動Client,輸入notwait命令,等待服務(wù)器通知打洞。
3、在客戶端2啟動Client,輸入conn命令,然后輸入服務(wù)器返回的客戶端1的外網(wǎng)ip和端口,接下來就會自動完成連接。
運(yùn)行效果:
客戶端1運(yùn)行結(jié)果 (穿透成功之后,客戶端會把穿透對方返回的內(nèi)容發(fā)送給服務(wù)器,服務(wù)器再返回)
客戶端1使用netstat查看的網(wǎng)絡(luò)連接
客戶端2的運(yùn)行結(jié)果
客戶端2使用netstat查看的網(wǎng)絡(luò)連接
可以看到客戶端2對應(yīng)的端口不同,那是因?yàn)殡娦臢AT的問題,本地獲取的Ip是電信10開頭的內(nèi)網(wǎng)地址,相當(dāng)于在客戶端2的上層還進(jìn)行了一次中繼。
s:由于沒有對稱型的NAT設(shè)備,無法做深入研究,對稱型設(shè)備的端口太難猜測,穿透成功概率很小。
team登錄不上/網(wǎng)頁打不開無法加載怎么辦 最新解決方法分享
Steam國服和國際服啊,它們還是有區(qū)別的。國服Steam有個新名字,叫蒸汽平臺,因?yàn)榉?wù)器就在咱們國內(nèi),所以網(wǎng)頁打開速度飛快,基本不會出錯。
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。