Shark Deng

Interactive Designer @Shanju Design in Shanghai

touched caffe / mxnet / python / wordpress / bootstrap / Arduino / Raspberry Pi / webgl / three.js / PIXI / 3dMax

Email

Github

OC – socket服务端和客户端简单通信

2019-11-18 22:29:55

ServerClient.h

#import <Foundation/Foundation.h>
#import "sys/socket.h"
#import "netinet/in.h"
#import "arpa/inet.h"


@interface SocketClient : NSObject

@property CFSocketRef socket;
@property BOOL isOnline;

-(void) create;
-(void) send; 
-(void) read;
@end

ServerClient.m

#import "SocketClient.h"

@implementation SocketClient


//MARK: CFSocket TCP
//CFSocketCreate()
//CFSocketConnectToAddress()
- (void) create {
    
    CFSocketContext ctx = {0, //version, must be 0
        (__bridge void *)(self), NULL, NULL, NULL};
    _socket = CFSocketCreate(kCFAllocatorDefault,
                             PF_INET, //windows AF_INET == PF_INET while in unix/linux, AF -> BSD, PF -> POSIX
                             SOCK_STREAM, //SOCK_DGRAM -> udp
                             IPPROTO_TCP,
                             kCFSocketConnectCallBack, //method type
                             TCPServerConnectCallBack,
                             &ctx);
    if (_socket != nil) {
        NSLog(@"\n\r socket create successfully");
        
        //1
        struct sockaddr_in addr4;
        memset(&addr4, 0, sizeof(addr4));
        addr4.sin_len = sizeof(addr4);
        addr4.sin_family = AF_INET;
        
        //ip and port
        addr4.sin_addr.s_addr = inet_addr([@"192.168.1.2" UTF8String]);
        addr4.sin_port = htons(9503);
        
        //turn sockaddr_in to CFDataRef
        CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *) &addr4, sizeof(addr4));
        
        
        //2
        CFSocketError result = CFSocketConnectToAddress(_socket, address, -1);
        //if 3rd param is negative, means backgroud connection
        //I set 3rd param 5 and it prompt connect failed while -1 works
        
        //4: enable callback
        CFRunLoopRef loop = CFRunLoopGetCurrent(); //get current thread loop
        CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
        CFRunLoopAddSource(loop, src, kCFRunLoopDefaultMode);
        CFRelease(src);
        CFRunLoopRun();
        
        
        
        //3: handle here or in callback func
        if (result == kCFSocketSuccess) {
            NSLog(@"\r\n socket connect successfully");
            
            _isOnline = YES;
            
            //start new thread to read data from server
            [NSThread detachNewThreadSelector:@selector(read) toTarget:self withObject:nil];
            
            
        } else {
            NSLog(@"\r\n socket connect failed");
        }
        
        
        
        
    } else {
        NSLog(@"\r\n socket create failed");
    }
}


static void TCPServerConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info) {
    //when select kCFSocketConnectCallBack, fail will return error pointer, others return NULL
    if (data != NULL) {
  
        
        NSLog(@"connect error");
        
        return;
    }
    
    //read data
    SocketClient *client = (__bridge SocketClient*) info;
    [client performSelectorInBackground:@selector(read) withObject:nil];
    
}


//recv()
- (void) read {
    char buf[2048];
    int hasRead;
    //NSAutoReleasePool is in run phase
    //@autoreleasepool is in build phase, more recommended
    @autoreleasepool {
        while ((hasRead = recv(CFSocketGetNative(_socket), buf, sizeof(buf), 0))) {
            NSLog(@"%d", hasRead);
            NSLog(@"data from server: %@", [[NSString alloc] initWithBytes:buf length:hasRead encoding:NSUTF8StringEncoding]);
        }
    }
    
}


//send()
- (void) send{
    if (_isOnline) {
        NSString *stringToSend = @"greeting from ios client";
        const char *data = [stringToSend UTF8String];
        send(CFSocketGetNative(_socket), data, strlen(data), 1);
    } else {
        NSLog(@"offline!");
    }
}



@end

ViewController.m(xcode项目执行)

- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar {
 
    NSLog(@"Start request..");
    
    SocketClient *client = [[SocketClient alloc]init];
    [client create];
    
    
}

服务端在terminal终端执行,可参考我之前博文:Swift/Oc在终端下编译

SocketServer.h


#import <Foundation/Foundation.h>
#import <sys/socket.h>
#import <arpa/inet.h>

@interface SocketServer : NSObject

@property CFSocketRef server;

-(void) create :(NSString *)ip :(int) port;

@end

 


SocketServer.m

//
//  SocketServer.m
//  SRClient
//
//  Created by sj on 11/06/2018.
//  Copyright © 2018 Apple. All rights reserved.
//

#import "SocketServer.h"

@implementation SocketServer



-(void) create: (NSString *)ip :(int) port {
    _server = CFSocketCreate(kCFAllocatorDefault,
                             PF_INET,
                             SOCK_STREAM,
                             IPPROTO_TCP,
                             kCFSocketAcceptCallBack,
                             TCPServerAcceptCallBack,
                             nil);
    
    if (_server != nil) {
        NSLog(@"server create successfully \n");
        
        int opt = 1;
        setsockopt(CFSocketGetNative(_server), SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
        
        
        struct sockaddr_in addr4;
        memset(&addr4, 0, sizeof(addr4));
        addr4.sin_addr.s_addr = inet_addr([ip UTF8String]);
        addr4.sin_port = htons(port);
        addr4.sin_family = AF_INET;
        addr4.sin_len = sizeof(addr4);
        
        //turn ipv4 to address
        CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
        CFSocketError result = CFSocketSetAddress(_server, address);
        if (result == kCFSocketSuccess) {
            NSLog(@"server set successfully \n");
            
            //loop listening client
            CFRunLoopRef current = CFRunLoopGetCurrent();
            CFRunLoopSourceRef src = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _server, 0);
            CFRunLoopAddSource(current, src, kCFRunLoopDefaultMode);
            CFRelease(src);
            CFRunLoopRun();
            
            
        } else {
            NSLog(@"server set failed \n");
            
            if (_server) {
                CFRelease(_server);
            }
            _server = NULL;
            
            exit(0);
        }
        
    } else {
        NSLog(@"server create failed \n");
    }

}



void serverRead(CFReadStreamRef iStream, CFStreamEventType type, void *info) {
    UInt8 buff[2048];
    
    CFIndex hasRead = CFReadStreamRead(iStream, buff, 2048);
    
    if(hasRead > 0) {
        printf("接收到数据: %s\n", buff);
        
    }
    
}



void TCPServerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef  address, const void *data, void *info ){
    
    if(type == kCFSocketAcceptCallBack){
        
        CFSocketNativeHandle nh = *(CFSocketNativeHandle*)data;
        uint8_t name[1024];
        socklen_t nameLen = sizeof(name);
        
        // 获取对方Socket信息,还有getsocketname()函数则用于获取本程序所在Socket信息
        if(getpeername(nh, (struct sockaddr *)name, &nameLen) != 0){
            
            NSLog(@"error");
            exit(1);
            
        }
        
        // 获取连接信息
        struct sockaddr_in * addr_in = (struct sockaddr_in*) name;
        
        NSLog(@"%s: %d 连接进来了", inet_ntoa(addr_in->sin_addr), addr_in->sin_port);
        
        CFReadStreamRef iStream;
        CFWriteStreamRef oStream;
        
        // 创建一组可读/写的CFStream
        
        CFStreamCreatePairWithSocket(kCFAllocatorDefault , nh, &iStream, &oStream);
        
        if(iStream && oStream){
            
            // 打开输入流和输入流
            
            CFReadStreamOpen(iStream);
            CFWriteStreamOpen(oStream);
            
            CFStreamClientContext streamContext = {0, NULL, NULL, NULL};
            
            if(!CFReadStreamSetClient(iStream,
                                      kCFStreamEventHasBytesAvailable,
                                      serverRead,
                                      &streamContext)) {
                
                exit(1);
                
            }
            
            CFReadStreamScheduleWithRunLoop(iStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
            
            const char* str = "您好,您收到Mac服务器的新年祝福!\n";
            
            // 向客户端输出数据
            
            CFWriteStreamWrite(oStream, (UInt8 *)str, strlen(str) + 1);
            
        }
        
    }
    
}


int main(int argc, char * argv[]) {
    SocketServer *server = [[SocketServer alloc]init];
    [server create:@"192.168.1.2" :9503];
}



@end

 

 

 



——By SharkDeng
如果你喜欢我的文章,欢迎红包赞赏

Leave a Reply

Your email address will not be published. Required fields are marked *