トップ «前の日記(2015-01-16) 最新 次の日記(2015-02-15)» 編集

Cocoa練習帳

iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど

2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|

2015-01-28 [OSX][iOS]UDP

UDP通信のサンプルを作成したので、ざっと説明する。

ADCのサンプルコードUDPEchoを改造して作成した。

UDP通信する部分はクラスにしている。

@protocol UDPDelegate;
 
@interface UDP : NSObject
 
@property (nonatomic, weak,   readwrite) id<UDPDelegate>        delegate;
@property (nonatomic, assign, readonly, getter=isServer) BOOL   server;
@property (nonatomic, copy,   readonly ) NSString *             hostName;
@property (nonatomic, copy,   readonly ) NSData *               hostAddress;
@property (nonatomic, assign, readonly ) NSUInteger             port;
 
- (void)startServerOnPort:(NSUInteger)port;
- (void)startConnectedToHostName:(NSString *)hostName port:(NSUInteger)port;
- (void)sendData:(NSData *)data;
- (void)stop;
 
@end
 
@protocol UDPDelegate <NSObject>
@optional
- (void)udp:(UDP *)udp didReceiveData:(NSData *)data fromAddress:(NSData *)addr;
- (void)udp:(UDP *)udp didReceiveError:(NSError *)error;
- (void)udp:(UDP *)udp didSendData:(NSData *)data toAddress:(NSData *)addr;
- (void)udp:(UDP *)udp didFailToSendData:(NSData *)data toAddress:(NSData *)addr error:(NSError *)error;
- (void)udp:(UDP *)udp didStartWithAddress:(NSData *)address;
- (void)udp:(UDP *)udp didStopWithError:(NSError *)error;
@end
#import <sys/socket.h>
#import <netinet/in.h>
#import <fcntl.h>
#import <unistd.h>
#import <Foundation/Foundation.h>
 
#if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
#import <CFNetwork/CFNetwork.h>
#else
#import <oreServices/CoreServices.h>
#endif
 
#import "UDP.h"
 
@interface UDP () 
@property (nonatomic, copy,   readwrite) NSString *             hostName;
@property (nonatomic, copy,   readwrite) NSData *               hostAddress;
@property (nonatomic, assign, readwrite) NSUInteger             port;
 
- (void)stopHostResolution;
- (void)stopWithError:(NSError *)error;
- (void)stopWithStreamError:(CFStreamError)streamError;
@end
 
@implementation UDP {
    CFHostRef   _cfHost;
    CFSocketRef _cfSocket;
}
 
- (id)init
{
    NSLog(@"%s", __func__);
    self = [super init];
    if (self != nil) {
    }
    return self;
}
 
- (void)dealloc
{
    NSLog(@"%s", __func__);
    [self stop];
}
 
- (BOOL)isServer
{
    NSLog(@"%s", __func__);
    return self.hostName == nil;
}
 
- (void)sendData:(NSData *)data toAddress:(NSData *)addr
{
    NSLog(@"%s", __func__);
    int                     err;
    int                     sock;
    ssize_t                 bytesWritten;
    const struct sockaddr * addrPtr;
    socklen_t               addrLen;
    
    sock = CFSocketGetNative(self->_cfSocket);
    
    if (addr == nil) {
        addr = self.hostAddress;
        addrPtr = NULL;
        addrLen = 0;
    } else {
        addrPtr = [addr bytes];
        addrLen = (socklen_t) [addr length];
    }
    
    bytesWritten = sendto(sock, [data bytes], [data length], 0, addrPtr, addrLen);
    if (bytesWritten < 0) {
        err = errno;
    } else  if (bytesWritten == 0) {
        err = EPIPE;
    } else {
        err = 0;
    }
    
    if (err == 0) {
        if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didSendData:toAddress:)] ) {
            [self.delegate udp:self didSendData:data toAddress:addr];
        }
    } else {
        if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didFailToSendData:toAddress:error:)] ) {
            [self.delegate udp:self
             didFailToSendData:data
                     toAddress:addr
                         error:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]];
        }
    }
}
 
- (void)readData
{
    NSLog(@"%s", __func__);
    int                     err;
    int                     sock;
    struct sockaddr_storage addr;
    socklen_t               addrLen;
    uint8_t                 buffer[65536];
    ssize_t                 bytesRead;
    
    sock = CFSocketGetNative(self->_cfSocket);
    
    addrLen = sizeof(addr);
    bytesRead = recvfrom(sock, buffer, sizeof(buffer), 0, (struct sockaddr *) &addr, &addrLen);
    if (bytesRead < 0) {
        err = errno;
    } else if (bytesRead == 0) {
        err = EPIPE;
    } else {
        NSData *dataObj;
        NSData *addrObj;
        
        err = 0;
        
        dataObj = [NSData dataWithBytes:buffer length:(NSUInteger) bytesRead];
        addrObj = [NSData dataWithBytes:&addr  length:addrLen  ];
        
        if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didReceiveData:fromAddress:)] ) {
            [self.delegate udp:self didReceiveData:dataObj fromAddress:addrObj];
        }
        
        if (self.isServer) {
            [self sendData:dataObj toAddress:addrObj];
        }
    }
    
    if (err != 0) {
        if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didReceiveError:)] ) {
            [self.delegate udp:self
               didReceiveError:[NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil]];
        }
    }
}
 
static void SocketReadCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
    NSLog(@"%s", __func__);
    UDP *udp = (__bridge UDP *)info;
    [udp readData];
}
 
- (BOOL)setupSocketConnectedToAddress:(NSData *)address port:(NSUInteger)port error:(NSError **)errorPtr
{
    NSLog(@"%s", __func__);
    int                     err;
    int                     junk;
    int                     sock;
    const CFSocketContext   context = { 0, (__bridge void *)(self), NULL, NULL, NULL };
    CFRunLoopSourceRef      rls;
    
    err = 0;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) {
        err = errno;
    }
    
    if (err == 0) {
        struct sockaddr_in      addr;
        
        memset(&addr, 0, sizeof(addr));
        if (address == nil) {
            addr.sin_len         = sizeof(addr);
            addr.sin_family      = AF_INET;
            addr.sin_port        = htons(port);
            addr.sin_addr.s_addr = INADDR_ANY;
            err = bind(sock, (const struct sockaddr *) &addr, sizeof(addr));
        } else {
            if ([address length] > sizeof(addr)) {
                [address getBytes:&addr length:sizeof(addr)];
            } else {
                [address getBytes:&addr length:[address length]];
            }
            addr.sin_port = htons(port);
            err = connect(sock, (const struct sockaddr *) &addr, sizeof(addr));
        }
        if (err < 0) {
            err = errno;
        }
    }
    
    if (err == 0) {
        int flags;
        
        flags = fcntl(sock, F_GETFL);
        err = fcntl(sock, F_SETFL, flags | O_NONBLOCK);
        if (err < 0) {
            err = errno;
        }
    }
    
    if (err == 0) {
        self->_cfSocket = CFSocketCreateWithNative(NULL, sock, kCFSocketReadCallBack, SocketReadCallback, &context);
        
        sock = -1;
        
        rls = CFSocketCreateRunLoopSource(NULL, self->_cfSocket, 0);
        
        CFRunLoopAddSource(CFRunLoopGetCurrent(), rls, kCFRunLoopDefaultMode);
        
        CFRelease(rls);
    }
    
    if (sock != -1) {
        junk = close(sock);
    }
    if ( (self->_cfSocket == NULL) && (errorPtr != NULL) ) {
        *errorPtr = [NSError errorWithDomain:NSPOSIXErrorDomain code:err userInfo:nil];
    }
    
    return (err == 0);
}
 
- (void)startServerOnPort:(NSUInteger)port
{
    NSLog(@"%s", __func__);
    if (self.port == 0) {
        BOOL        success;
        NSError *   error;
        
        success = [self setupSocketConnectedToAddress:nil port:port error:&error];
        
        if (success) {
            self.port = port;
            
            if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didStartWithAddress:)] ) {
                CFDataRef   localAddress;
                
                localAddress = CFSocketCopyAddress(self->_cfSocket);
                
                [self.delegate udp:self didStartWithAddress:(__bridge NSData *) localAddress];
                
                CFRelease(localAddress);
            }
        } else {
            [self stopWithError:error];
        }
    }
}
 
- (void)hostResolutionDone
{
    NSLog(@"%s", __func__);
    NSError *           error;
    Boolean             resolved;
    NSArray *           resolvedAddresses;
    
    error = nil;
    
    resolvedAddresses = (__bridge NSArray *) CFHostGetAddressing(self->_cfHost, &resolved);
    if ( resolved && (resolvedAddresses != nil) ) {
        for (NSData * address in resolvedAddresses) {
            BOOL                    success;
            const struct sockaddr * addrPtr;
            NSUInteger              addrLen;
            
            addrPtr = (const struct sockaddr *) [address bytes];
            addrLen = [address length];
            
            success = NO;
            if (
                (addrPtr->sa_family == AF_INET)
                ) {
                success = [self setupSocketConnectedToAddress:address port:self.port error:&error];
                if (success) {
                    CFDataRef   hostAddress;
                    
                    hostAddress = CFSocketCopyPeerAddress(self->_cfSocket);
                    
                    self.hostAddress = (__bridge NSData *) hostAddress;
                    
                    CFRelease(hostAddress);
                }
            }
            if (success) {
                break;
            }
        }
    }
    
    if ( (self.hostAddress == nil) && (error == nil) ) {
        error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorHostNotFound userInfo:nil];
    }
    
    if (error == nil) {
        [self stopHostResolution];
        
        if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didStartWithAddress:)] ) {
            [self.delegate udp:self didStartWithAddress:self.hostAddress];
        }
    } else {
        [self stopWithError:error];
    }
}
 
static void HostResolveCallback(CFHostRef theHost, CFHostInfoType typeInfo, const CFStreamError *error, void *info)
{
    NSLog(@"%s", __func__);
    UDP *udp;
    
    udp = (__bridge UDP *)info;
    
    if ( (error != NULL) && (error->domain != 0) ) {
        [udp stopWithStreamError:*error];
    } else {
        [udp hostResolutionDone];
    }
}
 
- (void)startConnectedToHostName:(NSString *)hostName port:(NSUInteger)port
{
    NSLog(@"%s", __func__);
    if (self.port == 0) {
        Boolean             success;
        CFHostClientContext context = {0, (__bridge void *)(self), NULL, NULL, NULL};
        CFStreamError       streamError;
        
        self->_cfHost = CFHostCreateWithName(NULL, (__bridge CFStringRef) hostName);
        
        CFHostSetClient(self->_cfHost, HostResolveCallback, &context);
        
        CFHostScheduleWithRunLoop(self->_cfHost, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        
        success = CFHostStartInfoResolution(self->_cfHost, kCFHostAddresses, &streamError);
        if (success) {
            self.hostName = hostName;
            self.port = port;
        } else {
            [self stopWithStreamError:streamError];
        }
    }
}
 
- (void)sendData:(NSData *)data
{
    NSLog(@"%s", __func__);
    if (self.isServer || (self.hostAddress == nil) ) {
    } else {
        [self sendData:data toAddress:nil];
    }
}
 
- (void)stopHostResolution
{
    NSLog(@"%s", __func__);
    if (self->_cfHost != NULL) {
        CFHostSetClient(self->_cfHost, NULL, NULL);
        CFHostCancelInfoResolution(self->_cfHost, kCFHostAddresses);
        CFHostUnscheduleFromRunLoop(self->_cfHost, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
        CFRelease(self->_cfHost);
        self->_cfHost = NULL;
    }
}
 
- (void)stop
{
    NSLog(@"%s", __func__);
    self.hostName = nil;
    self.hostAddress = nil;
    self.port = 0;
    [self stopHostResolution];
    if (self->_cfSocket != NULL) {
        CFSocketInvalidate(self->_cfSocket);
        CFRelease(self->_cfSocket);
        self->_cfSocket = NULL;
    }
}
 
- (void)noop
{
    NSLog(@"%s", __func__);
}
 
- (void)stopWithError:(NSError *)error
{
    NSLog(@"%s", __func__);
    [self stop];
    if ( (self.delegate != nil) && [self.delegate respondsToSelector:@selector(udp:didStopWithError:)] ) {
        [self performSelector:@selector(noop) withObject:nil afterDelay:0.0];
        [self.delegate udp:self didStopWithError:error];
    }
}
 
- (void)stopWithStreamError:(CFStreamError)streamError
{
    NSLog(@"%s", __func__);
    NSDictionary *  userInfo;
    NSError *       error;
    
    if (streamError.domain == kCFStreamErrorDomainNetDB) {
        userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                    [NSNumber numberWithInteger:streamError.error], kCFGetAddrInfoFailureKey,
                    nil
                    ];
    } else {
        userInfo = nil;
    }
    error = [NSError errorWithDomain:(NSString *)kCFErrorDomainCFNetwork code:kCFHostErrorUnknown userInfo:userInfo];
    
    [self stopWithError:error];
}
 
@end

そして、ViewControllerから呼び出す。

#import <UIKit/UIKit.h>
 
@interface ViewController : UIViewController
 
@property (weak, nonatomic) IBOutlet UITextField *inputTextField;
@property (weak, nonatomic) IBOutlet UIButton *sendButton;
@property (weak, nonatomic) IBOutlet UILabel *outputLabel;
 
- (IBAction)send:(id)sender;
 
@end
#import "ViewController.h"
#import "UDP.h"
 
@interface ViewController () <UDPDelegate>
 
@property UDP *server;
@property UDP *client;
 
- (void)runServerOnPort:(NSUInteger)port;
- (void)runClientWithHost:(NSString *)host port:(NSUInteger)port;
@end
 
@implementation ViewController
 
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self runServerOnPort:3054];
    [self runClientWithHost:@"localhost" port:3054];
}
 
- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}
 
- (IBAction)send:(id)sender
{
    NSLog(@"%s", __func__);
    NSData *data = [[NSString stringWithString:self.inputTextField.text] dataUsingEncoding:NSUTF8StringEncoding];
    [self.client sendData:data];
}
 
- (void)runServerOnPort:(NSUInteger)port
{
    NSLog(@"%s", __func__);
    self.server = [[UDP alloc] init];
    self.server.delegate = self;
    [self.server startServerOnPort:port];
}
 
- (void)runClientWithHost:(NSString *)host port:(NSUInteger)port
{
    NSLog(@"%s", __func__);
    self.client = [[UDP alloc] init];
    self.client.delegate = self;
    [self.client startConnectedToHostName:host port:port];
}
 
- (void)udp:(UDP *)udp didReceiveData:(NSData *)data fromAddress:(NSData *)addr
{
    NSLog(@"%s data(%@)", __func__, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
    NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    self.outputLabel.text = msg;
}
 
- (void)udp:(UDP *)udp didReceiveError:(NSError *)error
{
    NSLog(@"%s", __func__);
    self.outputLabel.text = [error description];
}
 
- (void)udp:(UDP *)udp didSendData:(NSData *)data toAddress:(NSData *)addr
{
    NSLog(@"%s data(%@)", __func__, [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
}
 
- (void)udp:(UDP *)udp didFailToSendData:(NSData *)data toAddress:(NSData *)addr error:(NSError *)error
{
    NSLog(@"%s", __func__);
    NSLog(@"failed with error: %@", [error description]);
}
 
- (void)udp:(UDP *)udp didStartWithAddress:(NSData *)address
{
    NSLog(@"%s", __func__);
}
 
- (void)udp:(UDP *)udp didStopWithError:(NSError *)error
{
    NSLog(@"%s", __func__);
    NSLog(@"failed with error: %@", [error description]);
}
 
@end

_ ソースコード

GitHubからどうぞ。
https://github.com/murakami/workbook/tree/master/ios/UDP - GitHub

_ 【Cocoa練習帳】

http://www.bitz.co.jp/weblog/
http://ameblo.jp/bitz/(ミラー・サイト)

トップ «前の日記(2015-01-16) 最新 次の日記(2015-02-15)» 編集