ios Socket套接字编程
Server端: 用CFSocketCreate()创建socket; 再用CFSocketSetAddress()设置地址; 再用CFSocketCreateRunLoopSource()把socket添加到runloop; 在socket的回调方法中用CFStreamCreatePairWithSocket()创建绑定到socket的 CFReadStreamReff流,再将CFReadStreamRef流转换成NSInputStream类型,设置好delegate,添加到runloop,最后open; 在- (void)stream:(NSStream*)aStream handleEvent:(NSStreamEvent)eventCode方法中处理NSStreamEventHasBytesAvailable事件读取流中的数据。 Client端: 用CFSocketCreate()创建socket; 再用CFSocketConnectToAddress()连接到指定地址; 再用CFSocketCreateRunLoopSource()把socket添加到runloop; 在socket的回调方法中用CFStreamCreatePairWithSocket()创建绑定到socket的 CFWriteStreamRef流,再将CFReadStreamRef流转换成NSInputStream类型,设置好delegate,添加到runloop,最后open; 在- (void)stream:(NSStream*)aStream handleEvent:(NSStreamEvent)eventCode方法中处理NSStreamEventHasSpaceAvailable事件向流中写数据。 如果我跳过创建socket,直接使用CFStreamCreatePairWithSocketToHost创建CFWriteStreamRef流,就不会出问题,这是为什么?请高手指教。
Client:
-(void)CreateConnect:(NSString*)strAddress
{
CFSocketContext sockContext = {0, // 结构体的版本,必须为0
self,
NULL, // 一个定义在上面指针中的retain的回调, 可以为NULL
NULL,
NULL};
_socket = CFSocketCreate(kCFAllocatorDefault, // 为新对象分配内存,可以为nil
PF_INET, // 协议族,如果为0或者负数,则默认为PF_INET
SOCK_STREAM, // 套接字类型,如果协议族为PF_INET,则它会默认为SOCK_STREAM
IPPROTO_TCP, // 套接字协议,如果协议族是PF_INET且协议是0或者负数,它会默认为IPPROTO_TCP
kCFSocketConnectCallBack, // 触发回调函数的socket消息类型,具体见Callback Types
TCPClientConnectCallBack, // 上面情况下触发的回调函数
&sockContext // 一个持有CFSocket结构信息的对象,可以为nil
);
if(_socket != NULL)
{
struct sockaddr_in addr4; // IPV4
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8888);
addr4.sin_addr.s_addr = inet_addr([strAddress UTF8String]); // 把字符串的地址转换为机器可识别的网络地址
// 把sockaddr_in结构体中的地址转换为Data
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
CFSocketConnectToAddress(_socket, // 连接的socket
address, // CFDataRef类型的包含上面socket的远程地址的对象
-1 // 连接超时时间,如果为负,则不尝试连接,而是把连接放在后台进行,如果_socket消息类型为kCFSocketConnectCallBack,将会在连接成功或失败的时候在后台触发回调函数
);
CFRunLoopRef cRunRef = CFRunLoopGetCurrent(); // 获取当前线程的循环
// 创建一个循环,但并没有真正加如到循环中,需要调用CFRunLoopAddSource
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
CFRunLoopAddSource(cRunRef, // 运行循环
sourceRef, // 增加的运行循环源, 它会被retain一次
kCFRunLoopCommonModes // 增加的运行循环源的模式
);
CFRelease(sourceRef);
NSLog(@"connect ok");
}
}
// socket回调函数,同客户端
static void TCPClientConnectCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
ViewController *client = (ViewController *)info;
if (data != NULL)
{
NSLog(@"连接失败");
[client.TextView insertText:@"连接失败\n"];
return;
}
else
{
NSLog(@"连接成功");
[client.TextView insertText:@"连接成功\n"];
// 读取接收的数据
g_viewPage = client;
[client StartReadThread];
}
}
-(void)StartReadThread
{
NSThread *InitThread = [[NSThread alloc]initWithTarget:self selector:@selector(InitThreadFunc:) object:self];
[InitThread start];
}
-(void)InitThreadFunc:(id)sender
{
while (1) {
[self readStream];
}
}
// 读取接收的数据
-(void)readStream
{
char buffer[1024];
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *str = @"服务器发来数据:";
recv(CFSocketGetNative(_socket), buffer, sizeof(buffer), 0);
{
str = [str stringByAppendingString:[NSString stringWithUTF8String:buffer]];
}
NSLog(str);
//回界面显示信息
[self performSelectorOnMainThread:@selector(ShowMsg:) withObject:str waitUntilDone:NO];
[pool release];
}
-(void)ShowMsg:(id)sender
{
NSString *str = sender;
str = [str stringByAppendingString:@"\n"];
[self.TextView insertText:str];
}
// 发送数据
- (void)sendMessage {
NSLog(@"hello Server");
char *data = "hello Server";
send(CFSocketGetNative(_socket), data, strlen(data) + 1, 0);
}
- (IBAction)SendMessageTouch:(id)sender {
[self sendMessage];
}
- (IBAction)TouchConnectServer:(id)sender {
NSString *serverIp = self.textField.text;
[self CreateConnect:serverIp];
}
- (void)dealloc {
[_TextView release];
[_textField release];
[super dealloc];
}
CFWriteStreamRef outputStream;
Sever:
@implementation SocketServer
-(int)setupSocket
{
CFSocketContext sockContext = {0, // 结构体的版本,必须为0
self,
NULL, // 一个定义在上面指针中的retain的回调, 可以为NULL
NULL,
NULL};
_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, TCPServerAcceptCallBack, &sockContext);
if (NULL == _socket) {
NSLog(@"Cannot create socket!");
return 0;
}
int optval = 1;
setsockopt(CFSocketGetNative(_socket), SOL_SOCKET, SO_REUSEADDR, // 允许重用本地地址和端口
(void *)&optval, sizeof(optval));
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = htons(8888);
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
CFDataRef address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&addr4, sizeof(addr4));
if (kCFSocketSuccess != CFSocketSetAddress(_socket, address))
{
NSLog(@"Bind to address failed!");
if (_socket)
CFRelease(_socket);
_socket = NULL;
return 0;
}
CFRunLoopRef cfRunLoop = CFRunLoopGetCurrent();
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _socket, 0);
CFRunLoopAddSource(cfRunLoop, source, kCFRunLoopCommonModes);
CFRelease(source);
return 1;
}
-(void) SendMessage
{
char *str = "你好 Client";
uint8_t * uin8b = (uint8_t *)str;
if (outputStream != NULL)
{
CFWriteStreamWrite(outputStream, uin8b, strlen(str) + 1);
}
else {
NSLog(@"Cannot send data!");
}
}
// 开辟一个线程线程函数中
-(void) StartServer
{
int res = [self setupSocket];
if (!res) {
exit(1);
}
NSLog(@"运行当前线程的CFRunLoop对象");
CFRunLoopRun(); // 运行当前线程的CFRunLoop对象
}
-(void)ShowMsgOnMainPage:(NSString*)strMsg
{
[self.delegate ShowMsg:strMsg];
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// socket回调函数
static void TCPServerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
if (kCFSocketAcceptCallBack == type)
{
// 本地套接字句柄
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t nameLen = sizeof(name);
if (0 != getpeername(nativeSocketHandle, (struct sockaddr *)name, &nameLen)) {
NSLog(@"error");
exit(1);
}
CFReadStreamRef iStream;
CFWriteStreamRef oStream;
// 创建一个可读写的socket连接
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &iStream, &oStream);
if (iStream && oStream)
{
CFStreamClientContext streamContext = {0, info, NULL, NULL};
if (!CFReadStreamSetClient(iStream, kCFStreamEventHasBytesAvailable,readStream, &streamContext))
{
exit(1);
}
if (!CFWriteStreamSetClient(oStream, kCFStreamEventCanAcceptBytes, writeStream, &streamContext))
{
exit(1);
}
CFReadStreamScheduleWithRunLoop(iStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFWriteStreamScheduleWithRunLoop(oStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
CFReadStreamOpen(iStream);
CFWriteStreamOpen(oStream);
} else
{
close(nativeSocketHandle);
}
}
}
// 读取数据
void readStream(CFReadStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
{
UInt8 buff[255];
CFReadStreamRead(stream, buff, 255);
///根据delegate显示到主界面去
NSString *strMsg = [[NSString alloc]initWithFormat:@"客户端传来消息:%s",buff];
SocketServer *info = (SocketServer*)clientCallBackInfo;
[info ShowMsgOnMainPage:strMsg];
}
void writeStream (CFWriteStreamRef stream, CFStreamEventType eventType, void *clientCallBackInfo)
{
outputStream = stream;
}
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。