2009-06-24 23 views
8

Ok, tôi gặp sự cố khi tạo ổ cắm bằng Objective-C. Nếu bạn sẽ xem xét mã của tôi dưới đây, thông qua trợ giúp với mã ví dụ và các nguồn khác, tôi đã có thể xây dựng một ổ cắm hoàn chỉnh mà tôi tin. Vấn đề là khi tôi biên dịch nó, nó xây dựng tốt (không có vấn đề cú pháp), nhưng không có ổ cắm được tạo ra. Như bạn sẽ nhận thấy tôi đã nhận xét ra rất nhiều thứ trong Server2.m và đã phân lập vấn đề ngay từ đầu khi tôi tạo cấu trúc cho listenSocket. Bằng cách này, nếu điều này giúp, nó là một phần của phía máy chủ của ứng dụng máy khách-khách hàng. Có ai biết tại sao tôi sẽ nhận được vấn đề này? Tất cả mọi thứ dường như làm việc tốt đẹp ngày hôm qua, và sáng nay tôi nghĩ rằng tôi sẽ có một cách tiếp cận khác nhau để xây dựng các ổ cắm, vì vậy tôi đã cố gắng này. Cảm ơn vì bất kì sự giúp đỡ!Sự cố khi tạo ổ cắm bằng CFSocket trong Objective-C (ứng dụng iPhone)

Server_TrialViewController.m

#include <CFNetwork/CFSocketStream.h> 
#import <UIKit/UIKit.h> 
#import "Server2.h" 
#import "Client_Test.h" 

@interface Server_TrialViewController : UIViewController { 
    IBOutlet UIButton *ServerButton; 
    IBOutlet UIButton *ClientButton; 
    IBOutlet UILabel *statusLabel; 
    Server2 *server; 
    Client_Test *client; 
} 

@property(nonatomic, retain) UILabel *statusLabel; 
@property(nonatomic, retain) Server2 *server; 
@property(nonatomic, retain) Client_Test *client; 

-(IBAction)serverButtonPressed; 
-(IBAction)clientButtonPressed; 
//-(void)sendMessageWithServer:(Server_Test *)SERVER AndClient:(Client_Test *)CLIENT; 

@end 

Server_TrialViewController.h

#import "Server_TrialViewController.h" 

@implementation Server_TrialViewController 
@synthesize statusLabel; 
@synthesize server; 
@synthesize client; 

-(IBAction)serverButtonPressed { 
    if ([server start]) { 
     [statusLabel setText:@"Success"]; 
    } 
    else { 
     if (server.status == NULL) { 
      [statusLabel setText: @"No Server: No statUpdate"]; 
     } 
     else { 
      [statusLabel setText: @"No Server: Found statUpdate"]; 
     } 
    }  
} 


-(IBAction)clientButtonPressed { 
    if ([client start]) { 
     [statusLabel setText:@"Client Started"]; 
    } 
    else { 
     [statusLabel setText:@"Client Not Started"]; 
    } 
} 


- (void)didReceiveMemoryWarning { 
    [super didReceiveMemoryWarning]; // Releases the view if it doesn't have a superview 
    // Release anything that's not essential, such as cached data 
} 


- (void)dealloc { 
    [super dealloc]; 
} 

@end 

Server2.h

#import <Foundation/Foundation.h> 
#import "Server2Delegate.h" 

@interface Server2 : NSObject 
{ 
    uint16_t port; 
    CFSocketRef listeningSocket; 
    id<Server2Delegate> delegate; 
    NSNetService* netService; 
    NSString *status; 
} 

// Initialize connection 
- (BOOL)start; 
- (void)stop; 

// Delegate receives various notifications about the state of our server 
@property(nonatomic,retain) id<Server2Delegate> delegate; 
@property(nonatomic, retain) NSString *status; 

@end 

Server2.m

#include <sys/socket.h> 
#include <netinet/in.h> 
#include <unistd.h> 
#include <CFNetwork/CFSocketStream.h> 

#import "Server2.h" 
#import "Connection2.h" 
#import "AppConfig2.h" 

// Declare some private properties and methods 
@interface Server2() 
@property(nonatomic,assign) uint16_t port; 
@property(nonatomic,retain) NSNetService* netService; 

-(BOOL)createServer; 
-(void)terminateServer; 

@end 

// Implementation of the Server interface 
@implementation Server2 

@synthesize delegate; 
@synthesize port, netService; 
@synthesize status; 

// Cleanup 
- (void)dealloc 
{ 
    self.netService = nil; 
    self.delegate = nil; 
    [super dealloc]; 
} 


// Create server and announce it 
- (BOOL)start 
{ 
    // Start the socket server 
    if (! [self createServer]) 
    { 
     status = @"Server Not Created"; 
     return FALSE; 
    } 
    status = @"Server Created"; 
    return TRUE; 
} 


// Close everything 
- (void)stop { 
    [self terminateServer]; 
} 

#pragma mark Callbacks 

// Handle new connections 
- (void)handleNewNativeSocket:(CFSocketNativeHandle)nativeSocketHandle { 
    Connection2* connection = [[[Connection2 alloc] initWithNativeSocketHandle:nativeSocketHandle] autorelease]; 

    // In case of errors, close native socket handle 
    if (connection == nil) { 
     close(nativeSocketHandle); 
     return; 
    } 

    // finish connecting 
    if (! [connection connect]) { 
     //status = @"Connection Not Made"; 
     [connection close]; 
     return; 
    } 

    //status = @"Connection Made"; 

    // Pass this on to our delegate 
    [delegate handleNewConnection:connection]; 
} 


// This function will be used as a callback while creating our listening socket via 'CFSocketCreate' 
static void serverAcceptCallback(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) { 
    Server2 *server = (Server2*)info; 

    // We can only process "connection accepted" calls here 
    if (type != kCFSocketAcceptCallBack) { 
     return; 
    } 

    // for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle 
    CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle*)data; 

    [server handleNewNativeSocket:nativeSocketHandle]; 
} 


#pragma mark Sockets and streams 

- (BOOL)createServer 
{ 
    //// PART 1: Create a socket that can accept connections 

    // Socket context 
    // struct CFSocketContext { 
    // CFIndex version; 
    // void *info; 
    // CFAllocatorRetainCallBack retain; 
    // CFAllocatorReleaseCallBack release; 
    // CFAllocatorCopyDescriptionCallBack copyDescription; 
    // }; 
    CFSocketContext socketContext = {0, self, NULL, NULL, NULL}; 

    listeningSocket = CFSocketCreate(
            kCFAllocatorDefault, 
            PF_INET,  // The protocol family for the socket 
            SOCK_DGRAM, // The socket type to create 
            IPPROTO_UDP, // The protocol for the socket. TCP vs UDP. 
            0, //kCFSocketAcceptCallBack, // New connections will be automatically accepted and the callback is called with the data argument being a pointer to a CFSocketNativeHandle of the child socket. 
            NULL, //(CFSocketCallBack)&serverAcceptCallback, 
            &socketContext); 

    // Previous call might have failed 
    if (listeningSocket == NULL) { 
     status = @"listeningSocket Not Created"; 
     return FALSE; 
    } 
    else { 
     status = @"listeningSocket Created"; 
     return TRUE; 
    } 
} 
    /* 
    // getsockopt will return existing socket option value via this variable 
    int existingValue = 1; 

    // Make sure that same listening socket address gets reused after every connection 
    setsockopt(CFSocketGetNative(listeningSocket), 
       SOL_SOCKET, SO_REUSEADDR, (void *)&existingValue, 
       sizeof(existingValue)); 


    //// PART 2: Bind our socket to an endpoint. 
    // We will be listening on all available interfaces/addresses. 
    // Port will be assigned automatically by kernel. 
    struct sockaddr_in socketAddress; 
    memset(&socketAddress, 0, sizeof(socketAddress)); 
    socketAddress.sin_len = sizeof(socketAddress); 
    socketAddress.sin_family = AF_INET; // Address family (IPv4 vs IPv6) 
    socketAddress.sin_port = 0;   // Actual port will get assigned automatically by kernel 
    socketAddress.sin_addr.s_addr = htonl(INADDR_ANY); // We must use "network byte order" format (big-endian) for the value here 

    // Convert the endpoint data structure into something that CFSocket can use 
    NSData *socketAddressData = 
    [NSData dataWithBytes:&socketAddress length:sizeof(socketAddress)]; 

    // Bind our socket to the endpoint. Check if successful. 
    if (CFSocketSetAddress(listeningSocket, (CFDataRef)socketAddressData) != kCFSocketSuccess) { 
     // Cleanup 
     if (listeningSocket != NULL) { 
      status = @"Socket Not Binded"; 
      CFRelease(listeningSocket); 
      listeningSocket = NULL; 
     } 

     return FALSE; 
    } 
    status = @"Socket Binded"; 

    //// PART 3: Find out what port kernel assigned to our socket 
    // We need it to advertise our service via Bonjour 
    NSData *socketAddressActualData = [(NSData *)CFSocketCopyAddress(listeningSocket) autorelease]; 

    // Convert socket data into a usable structure 
    struct sockaddr_in socketAddressActual; 
    memcpy(&socketAddressActual, [socketAddressActualData bytes], 
      [socketAddressActualData length]); 

    self.port = ntohs(socketAddressActual.sin_port); 

    //// PART 4: Hook up our socket to the current run loop 
    CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent(); 
    CFRunLoopSourceRef runLoopSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket, 0); 
    CFRunLoopAddSource(currentRunLoop, runLoopSource, kCFRunLoopCommonModes); 
    CFRelease(runLoopSource); 

    return TRUE; 
} 
*/ 

- (void) terminateServer { 
    if (listeningSocket != nil) { 
     CFSocketInvalidate(listeningSocket); 
     CFRelease(listeningSocket); 
     listeningSocket = nil; 
    } 
} 


#pragma mark - 
#pragma mark NSNetService Delegate Method Implementations 

// Delegate method, called by NSNetService in case service publishing fails for whatever reason 
- (void)netService:(NSNetService*)sender didNotPublish:(NSDictionary*)errorDict { 
    if (sender != self.netService) { 
     return; 
    } 

    // Stop socket server 
    [self terminateServer]; 
} 

@end 

Trả lời

4

Đối với những người tìm kiếm thông tin về máy chủ CFSocket, đây là câu trả lời: Mã ở trên hoạt động tốt nếu bạn thay đổi "SOCK_DGRAM" thành "SOCK_STREAM".

+0

CFSocketStream bao gồm từ "luồng" để cho biết đó là các kết nối được định hướng luồng. Trong Apple, họ nghĩ điều này rõ ràng là có thể, nhưng rõ ràng là không. – uchuugaka

1

Bạn đã thử đặt kCFSocketAcceptCallBack thành một thứ khác 0?

Nếu bạn quan tâm đến lập trình socket trên Mac OS X hoặc iPhone, tôi khuyên bạn nên xem this example từ tài liệu của Apple.

Các vấn đề liên quan