iOS开发-使用OC搭建自己的Socket 包括服务端和客服端
前言
- iOS开发中需要使用到Socket通信的地方,socket分为UDP和TCP,这次分享的是基于UDP是实现的socket。
开发须知
- 七层模型
- 计算机基础
- IP地址(主机名) 本地地址127.0.0.1 主机名localhost,每台电脑都有存在一个http://www.ip138.com
- 端口号
- 和进程关联起来的(IOS App)
- 有效端口号:0~65535
- 其中0~1024由系统使用或保留
- 传输协议
- 关于socket
- socket编程有两种模型:SOCK_DGRAM/SOCK_STREAM
- TCP:有连接/数据可靠/无边界/双工/C/S模型
- UDP:无连接/数据不可靠/有边界/双工 /对等模型
- TCP数据的无边界(STREAM)
- 需要建立连接
- 通过三次握手完成连接(connect(ack))
- 数据没有限制
- UDP(DGRAM)
- 不需要建立链接(所以速度快)
- 只管发送,不确认对方是接收(不可靠)
- 每个数据包的大小限制在64K内
- MSG_WAITALL:标记控制数据接收必须接受到指定字节长度才返回, 利用MSG_WAITALL接收指定长度的数据,达到数据分边界.
- TCP客户端处理
- 建立socket
- 连接服务器
- 发送数据
- 循环接收数据
- 关闭连接
- UDP 客户端处理
- 建立socket
- 发送数据SendTo
- 接收数据
- 接收数据 recvFrom
- 1.5.关闭连接
客服端
- 使用GCDAsyncSocket 库辅助
- chatC.h
#import <Foundation/Foundation.h>
@interface chatC : NSObject
- (void)buildClient;
- (void)sendData:(NSString*)content;
@end
#import "chatC.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
@implementation chatC
static int fd;
- (void)buildClient {
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1) {
NSLog(@"socket fail");
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8888);
int result = connect(fd, (struct sockaddr *)&addr, sizeof(addr));
if (result == -1) {
NSLog(@"connect fail");
}
[self sendData:@"123456"];
[NSThread detachNewThreadSelector:@selector(threadRecvData) toTarget:self withObject:nil];
}
- (void)threadRecvData {
char buf[32];
while (1) {
ssize_t result = recv(fd, buf, 32, MSG_WAITALL);
if (result <= 0) {
NSLog(@"recv fail!");
break;
}
buf[result] = 0;
NSLog(@"%s", buf);
}
close(fd);
}
- (void)sendData:(NSString*)content {
[NSThread detachNewThreadSelector:@selector(threadSendData:) toTarget:self withObject:content];
}
- (void)threadSendData:(NSString*)content {
if (content.length == 0) {
return;
}
const char* contC = [content UTF8String];
ssize_t result = send(fd, contC, strlen(contC), 0);
if (result < 0) {
NSLog(@"send fail");
}
}
@end
#import <Foundation/Foundation.h>
@interface chatcUDP : NSObject
- (void)buildClient;
- (void)sendData:(NSString*)content;
@end
#import "chatcUDP.h"
#import <sys/socket.h>
#import <netinet/in.h>
#import <arpa/inet.h>
static int fd;
static struct sockaddr_in addr;
@implementation chatcUDP
- (void)buildClient {
fd = socket(AF_INET, SOCK_DGRAM, 0);
if (fd == -1) {
NSLog(@"socket fail");
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(8888);
[self sendData:@"123456"];
[NSThread detachNewThreadSelector:@selector(threadRecvData) toTarget:self withObject:nil];
}
- (void)threadRecvData {
char buf[32];
while (1) {
socklen_t size = (socklen_t)sizeof(addr);
ssize_t result = recvfrom(fd, buf, 32, 0, (struct sockaddr *)&addr, &size);
if (result <= 0) {
NSLog(@"recv fail!");
break;
}
buf[result] = 0;
NSLog(@"%s", buf);
}
close(fd);
}
- (void)sendData:(NSString*)content {
[NSThread detachNewThreadSelector:@selector(threadSendData:) toTarget:self withObject:content];
}
- (void)threadSendData:(NSString*)content {
if (content.length == 0) {
return;
}
const char* contC = [content UTF8String];
ssize_t result = sendto(fd, contC, strlen(contC), 0, (struct sockaddr *)&addr, sizeof(addr));
}
@end
#import "ViewController.h"
#import "chatC.h"
#import "chatcUDP.h"
@interface ViewController () <UITextFieldDelegate>
{
chatC *_chatC;
chatcUDP *_chatcUdp;
}
@property(nonatomic, strong) UITextField *textField;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
[self.view addSubview:self.textField];
NSLog(@"==%d", getpid());
_chatcUdp = [chatcUDP new];
[_chatcUdp buildClient];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[_chatcUdp sendData:textField.text];
return YES;
}
- (UITextField *)textField {
if(_textField == nil) {
_textField = [[UITextField alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height/2, self.view.frame.size.width, 20)];
_textField.delegate = self;
}
return _textField;
}
@end
服务端
#import <Foundation/Foundation.h>
@interface ChatS : NSObject
- (int)buildServer;
@end
#import "ChatS.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/_select.h>
#include <sys/select.h>
static int serverfd;
static fd_set allfd;
static fd_set changefds;
static int maxfd = -1;
@implementation ChatS
- (int)buildServer{
serverfd = socket(AF_INET, SOCK_STREAM, 0);
if (serverfd == -1) {
NSLog(@"socket fail");
return 0;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("192.168.0.108");
addr.sin_port = htons(9999);
int isreused = 0;
setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, &isreused, sizeof(int));
int r = bind(serverfd, (struct sockaddr *)&addr, sizeof(addr));
if(r == -1){
printf("bind fail!\n");
return 0;
}else{
printf("bind success!\n");
}
r = listen(serverfd, 10);
if (r == -1) {
printf("listen fail\n");
return 0;
}
printf("listen success\n");
while (1) {
FD_ZERO(&changefds);
FD_SET(serverfd, &changefds);
maxfd = maxfd<serverfd?serverfd:maxfd;
for (int i = 0; i <= maxfd; i++) {
if (FD_ISSET(i, &allfd)) {
FD_SET(i, &changefds);
maxfd = maxfd<i?i:maxfd;
}
}
r = select(maxfd + 1, &changefds, 0, 0, 0);
if (FD_ISSET(serverfd, &changefds)) {
printf("有人来连接了\n");
int fd = accept(serverfd, 0, 0);
if(fd == -1){
printf("连接失败\n");
break;
}
maxfd = maxfd<fd?fd:maxfd;
FD_SET(fd, &allfd);
}
char buf[256];
for(int i = 0; i <= maxfd; i++){
if (FD_ISSET(i, &changefds) && FD_ISSET(i, &allfd)) {
r = (int)recv(i, buf, 255, 0);
if (r <= 0) {
printf("有人退出了\n");
FD_CLR(i, &allfd);
}
buf[r] = 0;
printf("来自客户端的数据:%d,%s\n", r, buf);
}
for (int j = 0; j <= maxfd; j++) {
if (FD_ISSET(j, &allfd)) {
r = (int)send(j, buf,strlen(buf), 0);
printf("send:%d\n", r);
}
}
}
}
close(serverfd);
return 0;
}
@end
两端测试
#import "ViewController.h"
#import "chatC.h"
#import "chatcUDP.h"
#import "ChatS.h"
@interface ViewController () <UITextFieldDelegate>
{
chatC *_chatC;
chatcUDP *_chatcUdp;
ChatS *chatS;
}
@property(nonatomic, strong) UITextField *textField;
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
[self.view addSubview:self.textField];
_chatC = [chatC new];
[_chatC buildClient];
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField{
[_chatC sendData:textField.text];
return YES;
}
- (UITextField *)textField {
if(_textField == nil) {
_textField = [[UITextField alloc]initWithFrame:CGRectMake(0, self.view.frame.size.height/2, self.view.frame.size.width, 20)];
_textField.delegate = self;
}
return _textField;
}
@end