与STUN的P2P UDP连接

时间:2015-01-31 17:33:08

标签: ios udp p2p gcdasyncsocket stun

大家好! 我已经尝试了很长一段时间使用UDP和GCDAsyncUDPSocket库发送数据包p2p。所以我发现一个开源库连接到STUN服务器并保持外部端口打开。它工作得很好,我已经成功地在本地网络中发送数据。 (在自己的路由器出现问题后,拒绝通过本地网络中的udp连接p2p - Android创建的网络仍然没有问题)

当我尝试使用UDP打孔发送数据时出现问题。

STUNClient: https://github.com/soulfly/STUN-iOS

UDPClient:https://github.com/robbiehanson/CocoaAsyncSocket/tree/master/GCD/Xcode/UdpEchoClient/Mobile/UdpEchoClient

UDPServer:https://github.com/robbiehanson/CocoaAsyncSocket/tree/master/GCD/Xcode/UdpEchoServer/Mobile/UdpEchoServer

我修改了UDPServer的代码:

  

ViewController.h:

#import <UIKit/UIKit.h>
#import "STUNClient.h"

@interface ViewController : UIViewController <STUNClientDelegate>
{
    IBOutlet UITextField *portField;
    IBOutlet UIButton *startStopButton;
    IBOutlet UIWebView *webView;
}
- (IBAction)startStop:(id)sender;
- (IBAction)startStun:(id)sender;
@end
  

ViewController.m

#import "ViewController.h"
#import "GCDAsyncUdpSocket.h"
#import "DDLog.h"

// Log levels: off, error, warn, info, verbose
static const int ddLogLevel = LOG_LEVEL_VERBOSE;

#define FORMAT(format, ...) [NSString stringWithFormat:(format), ##__VA_ARGS__]


@interface ViewController ()
{
    BOOL isRunning;
    GCDAsyncUdpSocket *udpSocket;
    NSMutableString *log;
    STUNClient* stunClient;
}

@end

@implementation ViewController

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
    if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil]))
    {
        log = [[NSMutableString alloc] init];

        // Setup our socket.
        // The socket will invoke our delegate methods using the usual delegate paradigm.
        // However, it will invoke the delegate methods on a specified GCD delegate dispatch queue.
        // 
        // Now we can configure the delegate dispatch queues however we want.
        // We could simply use the main dispatch queue, so the delegate methods are invoked on the main thread.
        // Or we could use a dedicated dispatch queue, which could be helpful if we were doing a lot of processing.
        // 
        // The best approach for your application will depend upon convenience, requirements and performance.
        // 
        // For this simple example, we're just going to use the main thread.
        stunClient= [[STUNClient alloc] init];

        udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];


    }
    return self;
}


- (void)viewDidLoad
{
    [super viewDidLoad];

    webView.dataDetectorTypes = UIDataDetectorTypeNone;

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardWillShowNotification 
                                               object:nil];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillHide:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];
}

- (void)viewDidUnload
{
    [super viewDidUnload];

    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)getKeyboardHeight:(float *)keyboardHeightPtr
        animationDuration:(double *)animationDurationPtr
                     from:(NSNotification *)notification
{
    float keyboardHeight;
    double animationDuration;

    // UIKeyboardCenterBeginUserInfoKey:
    // The key for an NSValue object containing a CGRect
    // that identifies the start frame of the keyboard in screen coordinates.

    CGRect beginRect = [[[notification userInfo] objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue];
    CGRect endRect   = [[[notification userInfo] objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];

    if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation))
    {
        keyboardHeight = ABS(beginRect.origin.x - endRect.origin.x);
    }
    else
    {
        keyboardHeight = ABS(beginRect.origin.y - endRect.origin.y);
    }

    // UIKeyboardAnimationDurationUserInfoKey
    // The key for an NSValue object containing a double that identifies the duration of the animation in seconds.

    animationDuration = [[[notification userInfo] objectForKey:UIKeyboardAnimationDurationUserInfoKey] doubleValue];

    if (keyboardHeightPtr) *keyboardHeightPtr = keyboardHeight;
    if (animationDurationPtr) *animationDurationPtr = animationDuration;
}

- (void)keyboardWillShow:(NSNotification *)notification
{
    float keyboardHeight = 0.0F;
    double animationDuration = 0.0;

    [self getKeyboardHeight:&keyboardHeight animationDuration:&animationDuration from:notification];

    CGRect webViewFrame = webView.frame;
    webViewFrame.size.height -= keyboardHeight;

    void (^animationBlock)(void) = ^{

        webView.frame = webViewFrame;
    };

    UIViewAnimationOptions options = 0;

    [UIView animateWithDuration:animationDuration
                          delay:0.0
                        options:options
                     animations:animationBlock
                     completion:NULL];
}

- (void)keyboardWillHide:(NSNotification *)notification
{
    float keyboardHeight = 0.0F;
    double animationDuration = 0.0;

    [self getKeyboardHeight:&keyboardHeight animationDuration:&animationDuration from:notification];

    CGRect webViewFrame = webView.frame;
    webViewFrame.size.height += keyboardHeight;

    void (^animationBlock)(void) = ^{

        webView.frame = webViewFrame;
    };

    UIViewAnimationOptions options = 0;

    [UIView animateWithDuration:animationDuration
                          delay:0.0
                        options:options
                     animations:animationBlock
                     completion:NULL];
}

- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
    DDLogError(@"webView:didFailLoadWithError: %@", error);
}

- (void)webViewDidFinishLoad:(UIWebView *)sender
{
    NSString *scrollToBottom = @"window.scrollTo(document.body.scrollWidth, document.body.scrollHeight);";

    [sender stringByEvaluatingJavaScriptFromString:scrollToBottom];
}

- (void)logError:(NSString *)msg
{
    NSString *prefix = @"<font color=\"#B40404\">";
    NSString *suffix = @"</font><br/>";

    [log appendFormat:@"%@%@%@\n", prefix, msg, suffix];

    NSString *html = [NSString stringWithFormat:@"<html><body>\n%@\n</body></html>", log];
    [webView loadHTMLString:html baseURL:nil];
}

- (void)logInfo:(NSString *)msg
{
    NSString *prefix = @"<font color=\"#6A0888\">";
    NSString *suffix = @"</font><br/>";

    [log appendFormat:@"%@%@%@\n", prefix, msg, suffix];

    NSString *html = [NSString stringWithFormat:@"<html><body>\n%@\n</body></html>", log];
    [webView loadHTMLString:html baseURL:nil];
}

- (void)logMessage:(NSString *)msg
{
    NSString *prefix = @"<font color=\"#000000\">";
    NSString *suffix = @"</font><br/>";

    [log appendFormat:@"%@%@%@\n", prefix, msg, suffix];

    NSString *html = [NSString stringWithFormat:@"<html><body>%@</body></html>", log];
    [webView loadHTMLString:html baseURL:nil];
}

- (IBAction)startStop:(id)sender
{
    if (isRunning)
    {
        // STOP udp echo server

        [udpSocket close];

        [self logInfo:@"Stopped Udp Echo server"];
        isRunning = false;

        [portField setEnabled:YES];
        [startStopButton setTitle:@"Start" forState:UIControlStateNormal];
    }
    else
    {
        //*START udp echo server

        int port = [portField.text intValue];
        if (port < 0 || port > 65535)
        {
            portField.text = @"";
            port = 0;
        }

        NSError *error = nil;

        if (![udpSocket bindToPort:port error:&error])
        {
            [self logError:FORMAT(@"Error starting server (bind): %@", error)];
            return;
        }

        if (![udpSocket beginReceiving:&error])
        {
            [udpSocket close];

            [self logError:FORMAT(@"Error starting server (recv): %@", error)];
            return;
        }

        [self logInfo:FORMAT(@"Udp Echo server started on port %hu", [udpSocket localPort])];
        isRunning = YES;

        [portField setEnabled:NO];
        [startStopButton setTitle:@"Stop" forState:UIControlStateNormal];

    }
}
-(void)didReceivePublicIPandPort:(NSDictionary *) data{
    NSLog(@"Public IP=%@, public Port=%@, NAT is Symmetric: %@", [data objectForKey:publicIPKey],
          [data objectForKey:publicPortKey], [data objectForKey:isPortRandomization]);


    int portT=   [((NSNumber*)[data objectForKey:publicPortKey]) intValue];
    NSString* ipT=[data objectForKey:publicIPKey];
    [self logInfo:FORMAT(@"STUN says external IP: %@ PORT %d", ipT,portT)];

    [self logInfo:FORMAT(@"LOCAL IP: %@  LOCAL PORT %d", [udpSocket localHost],[udpSocket localPort])];
    isRunning =YES;
    [stunClient startSendIndicationMessage];
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
                                               fromAddress:(NSData *)address
                                         withFilterContext:(id)filterContext
{
    if (!isRunning) return;

    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    if (msg)
    {
        /* If you want to get a display friendly version of the IPv4 or IPv6 address, you could do this:

        NSString *host = nil;
        uint16_t port = 0;
        [GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];

        */

        [self logMessage:msg];
    }
    else
    {
        [self logError:@"Error converting received data into UTF-8 String"];
    }

    [udpSocket sendData:data toAddress:address withTimeout:-1 tag:0];
}
- (IBAction)startStun:(id)sender
{
     [stunClient requestPublicIPandPortWithUDPSocket:udpSocket delegate:self];
}
@end
  

我的所作所为:

  1. 启用客户端和服务器应用。

  2. 按下STUN按钮 - 哪个     启动` - (IBAction)startStun:(id)sender;

  3. 从ClientApp中的STUN服务器手动输入外部IP和端口。
  4. 发送。
  5. 也许是路由器问题?或者我不完全理解打孔方法?

0 个答案:

没有答案