// // ESPUDPSocketServer.m // EspTouchDemo // // Created by 白 桦 on 4/13/15. // Copyright (c) 2015 白 桦. All rights reserved. // #import "ESPUDPSocketServer.h" #include #include #include #include #include "ESPTouchTask.h" @interface ESPUDPSocketServer () @property(nonatomic,assign) int _sck_fd; @property(nonatomic,assign) int _port; // it is used to check whether the socket is closed already to prevent close more than once. // especially, when you close the socket second time, it is created just now, it will crash. // // // suppose fd1 = 4, fd1 belong to obj1 // e.g. int fd1 = socket(AF_INET,SOCK_DRAM,0); // close(fd1); // // // suppose fd2 = 4 as well, fd2 belong to obj2 // int fd2 = socket(AF_INET,SOCK_DRAM,0); // // // obj1's dealloc() is called by system, so // close(fd1); // // // Amazing!!! at the moment, fd2 is close by others // @property(nonatomic,assign) volatile bool _isClosed; // it is used to lock the close method @property(nonatomic,strong) volatile NSLock *_lock; @end @implementation ESPUDPSocketServer - (id) initWithPort: (int) port AndSocketTimeout: (int) socketTimeout { self = [super init]; if (self) { // create local self._lock = [[NSLock alloc]init]; // create socket self._isClosed = NO; self._sck_fd = socket(AF_INET,SOCK_DGRAM,0); if (DEBUG_ON) { NSLog(@"##########################server init(): _sck_fd=%d", self._sck_fd); } if (self._sck_fd < 0) { if (DEBUG_ON) { perror("server: _skd_fd init() fail\n"); } return nil; } // init socket params struct sockaddr_in server_addr; socklen_t addr_len; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); server_addr.sin_addr.s_addr = INADDR_ANY; addr_len = sizeof(server_addr); // set broadcast const int opt = 1; if (setsockopt(self._sck_fd,SOL_SOCKET,SO_BROADCAST,(char *)&opt, sizeof(opt)) < 0) { if (DEBUG_ON) { perror("server init(): setsockopt SO_BROADCAST fail\n"); } [self close]; return nil; } // set timeout [self setSocketTimeout:socketTimeout]; // bind if (bind(self._sck_fd, (struct sockaddr*)&server_addr, addr_len) < 0) { if (DEBUG_ON) { perror("server init(): bind fail\n"); } [self close]; return nil; } } return self; } // make sure the socket will be closed sometime - (void)dealloc { if (DEBUG_ON) { NSLog(@"###################server dealloc()"); } [self close]; } - (void) close { [self._lock lock]; if (!self._isClosed) { if (DEBUG_ON) { NSLog(@"###################server close() fd=%d",self._sck_fd); } close(self._sck_fd); self._isClosed = true; } [self._lock unlock]; } - (void) interrupt { [self close]; } - (void) setSocketTimeout: (int) timeout { struct timeval tv; tv.tv_sec = timeout/1000; tv.tv_usec = timeout%1000*1000; if (setsockopt(self._sck_fd,SOL_SOCKET,SO_RCVTIMEO,(char *)&tv, sizeof(tv)) < 0) { if (DEBUG_ON) { perror("server: setsockopt SO_RCVTIMEO fail\n"); } } } - (Byte) receiveOneByte { ssize_t recNumber = recv(self._sck_fd, _buffer, BUFFER_SIZE, 0); if (recNumber > 0) { return _buffer[0]; } else if(recNumber == 0) { if (DEBUG_ON) { perror("server: receiveOneByte socket is closed by the other\n"); } } else { if (DEBUG_ON) { perror("server: receiveOneByte fail\n"); } } return UINT8_MAX; } - (NSData *) receiveSpecLenBytes: (int)len { ssize_t recNumber = recv(self._sck_fd, _buffer, BUFFER_SIZE, 0); if (recNumber==len) { NSData *data = [[NSData alloc]initWithBytes:_buffer length:recNumber]; return data; } else if(recNumber==0) { if (DEBUG_ON) { perror("server: receiveOneByte socket is closed by the other\n"); } } else if(recNumber<0) { if (DEBUG_ON) { perror("server: receiveOneByte fail\n"); } } else { // receive rubbish message, just ignore it } return nil; } @end