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