分享

内网穿透实现

 筑心wup 2013-06-02

内网穿透实现

如果内网A机器要访问外网C机器,可通过TCP/IP直接连接或发送目标地址C地址的数据包对象。
C机器如果要发送数据给A机器,则分两种情况。
1.如果是TCP Socket连接,必须在AC建立TCP/IP连接
2.如果UDPC必须收到A发来UDP包后,从这个数据包中得到A的回送地址,才可将数据回送给A,但其实C得到的地址并不是内网A的地址,而是网关B上的IP地址和端口。
如果A1要和B1通信,在TCP/IP通信模式下,必须要有一个相当于中转功能的公网服务器C存在,通过C实现了A1B1的通信,交换数据的全程必须经过C机器。
如果是UDP发送模式下,A1B1必须首先向C发送一个UDP包,C在收到数据包后,分别记录下A1B1的数据包的发送地址,当A1要向B1发送数据时,先将数据发送到CC再将这个数据包的目标地址设定为已记录的B1的地址后发送,B1即可接收到C转发来的A1的数据。
事实是,C得到A1B1的地址并不是A1B1在内网发送时的地址。C得到A1的地址是A2的网关地址,B1则是B2的网关IP地址,而端口则是网关上的端口(系统自动选择),并不是你指定的A1B1上的发送端口
这意味着CAB发送数据包时,数据包的目标地址其实是它们的网关IP地址和某一端口,数据包是通过它们的网关转发到A1B1的。
A网关和B网关是如何得知收到的数据包药发送给内网的某台机器呢?这就必须理解网关的NAT机制,NAT即网络地址转换(Network Address Translation)功能,当A1通过A网关向C发送数据包时,A网关则打开一个UDP端口,在此端口上接收到的数据即认为是应该给A1的数据,A网关则理解为具有端口映射功能的转发器。利用UDP通信的这种映射功能,就可以实现A1B1之间的P2P通信
A1要和B1要实现相互间直接通信(P2P),必须经过以下步骤。
1.A1向公网服务器C发送数据包,C记录网关A2上的映射地址
2.B1向公网服务器C发送数据包,C记录网关B2上的映射地址。
3.CA2B2的地址发送给B1A1
4.A1B1再发送数据包时,数据包的目标地址写为收到的对方映射地址,即将数据包发给B2A1上的地址。网关即可将收到的数据包转发给其映射的内网主机。
到这一步后的通信就和C没关系了,A1B1只要记住对方的映射地址,即可点对点地将数据发送给对端机器,即实现了P2P通信。
建立P2P通信时,双方首先要通过服务器C交换地址信息,服务器C启动后,当接收到一个客户端发来的数据包时,将这个包的来源地址存入服务器的Set集合,然后将这个集合中的所有已存地址以对象为单位发送给这个客户端。

服务端:(如腾讯服务器)

import java.net.*;
import java.io.*;
import java.util.*;


public class DatagramRouteServer {
      private Set<InetSocketAddress> clientAddSet=new HashSet();  //该容器存放所有客户端的地址
     
      //启动UDP服务器,接收消息,转发消息
      public void startServer()throws Exception{
              DatagramSocket socket=new DatagramSocket(9090);
              System.out.println("UDP服务器等待接收数据:"+socket.getLocalAddress());
             
              while(true){
                    byte[] buffer=new byte[256];
                   
                    //创建数据包
                    DatagramPacket  packet=new DatagramPacket(buffer,buffer.length);
                   
                    socket.receive(packet);
                   
                    //得到发送方IP和端口
                    InetAddress clientAdd=packet.getAddress();
                    int clientPort=packet.getPort();
                   
                    InetSocketAddress address=new InetSocketAddress(clientAdd,clientPort);
                    //将这个地址加入到Set
                    clientAddSet.add(address);
                   
                    //提取发送内容
                    byte[] recvData=packet.getData();
                    String s=new String(recvData).trim();
                    System.out.println("服务器收到数据:"+s+"  来自:"+address);
                   
                   
                    //Set中的每一个地址发送信息,信息内容为新登入服务器的发送方来取地址了
                    for(InetSocketAddress dclient:clientAddSet){
                            String temf=address+",到服务器取地址了";
                            //转发服务器端的地址列表数据
                            ByteArrayOutputStream bous=new ByteArrayOutputStream();
                            ObjectOutputStream oos=new ObjectOutputStream(bous);
                            oos.writeObject(temf);  //这里是字符串
                            oos.flush();
                           
                            byte[] data=bous.toByteArray();
                            DatagramPacket mp=new DatagramPacket(data,data.length);
                            mp.setSocketAddress(dclient);
                            //DatagramPacket mp=new DatagramPacket(data,data.length,dclient);
                            socket.send(mp);
                    }
                   
                    ByteArrayOutputStream bous=new ByteArrayOutputStream();
                    ObjectOutputStream oos=new ObjectOutputStream(bous);
                    oos.writeObject(clientAddSet); //将一个Set写入了流中。object包装过了 。和上面的都是Object强制转换的,故接收时要判断
                    oos.flush();
                   
                    byte[] data=bous.toByteArray();
                    DatagramPacket sendP=new DatagramPacket(data,data.length);
                    sendP.setSocketAddress(address);
                    socket.send(sendP);
              }
      }
     
     
      public static void main(String[] arg) throws Exception{
              new DatagramRouteServer().startServer();
      }
     

}

客户端(如QQA用户和QQB用户)

import java.net.*;
import java.io.*;
import java.util.*;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

public class DatagramNetClient extends Thread{
      //公网服务器地址
      private SocketAddress destAdd=new InetSocketAddress("172.16.1.25",9090);
     
      private DatagramSocket sendSocket;  //构造函数里初始化
     
      //显示消息的文本框
      private JTextArea jta_recive=new JTextArea(10,50);  //10*25
     
      //显示其他的客户地址
      private JComboBox jta_addList=new JComboBox();
     
     
      public DatagramNetClient(){
              try{
                    sendSocket=new DatagramSocket();
              }catch(Exception e){
                    e.printStackTrace();
              }
      }


      @Override
      public void run() {
              try{
                    while(true){
                            byte[] recvData=new byte[2048];
                            DatagramPacket recvPacket=new DatagramPacket(recvData,recvData.length);
                            sendSocket.receive(recvPacket);
                            byte[] data=recvPacket.getData();  //收到的是Set中的ip地址,应该也是个对象流
                           
                            //读取信息
                            ByteArrayInputStream bins=new ByteArrayInputStream(data);
                            ObjectInputStream oins=new ObjectInputStream(bins);
                            Object dataO=oins.readObject();
                           
                            //收到的是两种信息
                            if(dataO instanceof Set){
                                  Set<InetSocketAddress> othersAdds=(Set)dataO;
                                  jta_addList.removeAllItems();
                                 
                                  for(InetSocketAddress it:othersAdds){
                                          jta_addList.addItem(it);
                                  }
                            }else if(dataO instanceof String){
                                  String s=(String)dataO;
                                  System.out.println(s+"
");

                            }else{
                                  String s="unkown msg:"+dataO;
                                  jta_recive.append(s+"
");

                            }
                           
                    }
                   
              }catch(Exception e){
                    e.printStackTrace();
              }
      }
     
     
      public void setUpUI(){
              JFrame jf=new JFrame("p2p测试-客户端");
              jf.setLayout(new FlowLayout());
              jf.setSize(500,400);
             
             
              JButton jb_get=new JButton("获取其他客户端地址");
              jf.add(jb_get);
              jf.add(jta_addList);
              jb_get.addActionListener(new ActionListener(){

                    @Override
                    public void actionPerformed(ActionEvent e) {
                            sendRequestMsg("取得地址");
                           
                    }});
             
              JLabel la_name=new JLabel("接收到的信息:");
              JLabel la_users=new JLabel("发送给:");
             
              final JTextField jtf_send=new JTextField(20);  //发送输入框
              JButton bu_send=new JButton("发送");
             
             
              JScrollPane js=new JScrollPane(jta_recive);
             
             
              jf.add(la_name);
              jf.add(js);
              jf.add(la_users);
              jf.add(jtf_send);
              jf.add(bu_send);
             
             
             
              ActionListener sendListener=new ActionListener(){

                    @Override
                    public void actionPerformed(ActionEvent e) {
                            String msg=jtf_send.getText();
                            InetSocketAddress dest=(InetSocketAddress)jta_addList.getSelectedItem(); //核心代码!!
                            sendP2PMsg(msg,dest);
                            jtf_send.setText("");
                    }
              };
             
             
              bu_send.addActionListener(sendListener);
              jtf_send.addActionListener(sendListener);
             
              jf.setVisible(true);
              jf.setDefaultCloseOperation(3);
             
      }
     
      public void sendRequestMsg(String msg){
              try{
                    byte[] buffer=msg.getBytes();
                    DatagramPacket dp=new DatagramPacket(buffer,buffer.length,destAdd);
                    sendSocket.send(dp);
              }catch(Exception e){
                    e.printStackTrace();
              }
      }
     
     
      public void sendP2PMsg(String msg,InetSocketAddress dest){
              try{
                    ByteArrayOutputStream bous=new ByteArrayOutputStream();
                    ObjectOutputStream oos=new ObjectOutputStream(bous);
                    oos.writeObject(msg);
                   
                    byte[] buffer=bous.toByteArray();
                    DatagramPacket dp=new DatagramPacket(buffer,buffer.length,dest);
                    sendSocket.send(dp);
                   
                   
              }catch(Exception e){
                    e.printStackTrace();
              }
             
      }
     
     
     
     
      public static void main(String[] args){
              DatagramNetClient sender=new DatagramNetClient();
              sender.start();
              sender.setUpUI();
      }
     
     

}

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多