1、BSDSocket
一套 unix 系统下的 socket API(纯 C)。
iOS 系统基于 unix,所以支持底层的 BSD Socket,在 Xcode 中可以直接使用。
2、基本使用
2.1 Client 客户端
-
TCP 客户端
-
包含头文件
#import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import <ifaddrs.h>
-
创建 Socket
// client Socket @property (nonatomic, assign) int clientSocket; self.clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (self.clientSocket > 0) { NSLog(@"Socket 创建成功 %d", self.clientSocket); } else { NSLog(@"Socket 创建失败"); }
-
连接到服务器
// 创建服务器地址结构体 struct sockaddr_in serverAddress; bzero(&serverAddress, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; // 协议族 serverAddress.sin_port = htons(12345); // 端口 端口数据高位在前低位在后 12345 => 3039 => 3930 serverAddress.sin_addr.s_addr = inet_addr("192.168.88.100"); // inet_addr 函数可以把 ip 地址转换成一个整数 int isConnected = connect(self.clientSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)); if (isConnected) { NSLog(@"连接失败 %d", isConnected); } else { NSLog(@"连接成功"); }
-
发送数据
NSString *message = @"你好"; ssize_t sendLen = send(self.clientSocket, message.UTF8String, strlen(message.UTF8String), 0); if (sendLen > 0) { NSLog(@"发送成功,发送 %ld 个字节数据", sendLen); } else { NSLog(@"发送失败 %zd", sendLen); }
-
接收数据
// 接收字符串的数组,typedef unsigned char uint8_t; uint8_t buffer[1024]; ssize_t recvLen = recv(self.clientSocket, buffer, sizeof(buffer), 0); if (recvLen > 0) { // 按照服务器返回的长度,从 buffer 中,读取二进制数据,建立 NSData 对象 NSData *data = [NSData dataWithBytes:buffer length:recvLen]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"接收成功,接收到 %ld 个字节数据\n 接收到:%@", recvLen, str); } else { NSLog(@"接收失败 %zd", recvLen); }
-
关闭连接
// 数据传递完毕之后,关闭连接 int isCloseed = close(self.clientSocket); NSLog(@"%d", isCloseed);
-
2.2 Server 服务端
-
TCP 服务端
-
包含头文件
#import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import <ifaddrs.h>
-
创建 Socket
// server Socket @property (nonatomic, assign) int serverSocket; // client Socket @property (nonatomic, assign) int clientSocket; self.serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (self.serverSocket > 0) { NSLog(@"Socket 创建成功 %d", self.serverSocket); } else { NSLog(@"Socket 创建失败"); }
-
开始监听
// 创建服务器地址结构体 struct sockaddr_in serverAddress; bzero(&serverAddress, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(12345); serverAddress.sin_addr.s_addr = inet_addr("192.168.88.100"); int isBind = bind(self.serverSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)); if (isBind) { NSLog(@"绑定失败 %d", isBind); return; } else { NSLog(@"绑定成功"); } int isListen = listen(self.serverSocket, 10); if (isListen) { NSLog(@"监听失败 %d", isListen); return; } else { NSLog(@"监听成功"); } // clientSocket struct sockaddr_in clientAddress; bzero(&clientAddress, sizeof(clientAddress)); socklen_t len = sizeof(clientAddress); self.clientSocket = accept(self.serverSocket, (struct sockaddr *)&clientAddress, &len); if (self.clientSocket) { NSLog(@"接收成功 %d,client IP:%zd", self.clientSocket, clientAddress); } else { NSLog(@"接收失败"); }
-
发送数据
NSString *message = @"你好"; // 使用 clientSocket ssize_t sendLen = send(self.clientSocket, message.UTF8String, strlen(message.UTF8String), 0); if (sendLen > 0) { NSLog(@"发送成功,发送 %ld 个字节数据", sendLen); } else { NSLog(@"发送失败 %zd", sendLen); }
-
接收数据
// 接收字符串的数组,typedef unsigned char uint8_t; uint8_t buffer[1024]; // 使用 clientSocket ssize_t recvLen = recv(self.clientSocket, buffer, sizeof(buffer), 0); if (recvLen > 0) { // 按照服务器返回的长度,从 buffer 中,读取二进制数据,建立 NSData 对象 NSData *data = [NSData dataWithBytes:buffer length:recvLen]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"接收成功,接收到 %ld 个字节数据\n 接收到:%@", recvLen, str); } else { NSLog(@"接收失败 %zd", recvLen); }
-
关闭连接
// 数据传递完毕之后,关闭连接 int isCloseed = close(self.serverSocket); NSLog(@"%d", isCloseed);
-
3、网络访问
-
1、请求:
-
1)请求行
GET / HTTP/1.1
- 方法 GET
- 路径 /
协议 HTTP 1.1
-
2)请求头
- Host: localhost 主机
- User-Agent: 告诉服务器客户端的类型
- Accept: 告诉服务器客户端支持的格式
- Accept-Language: 告诉服务器客户端的语言
- Accept-Encoding: 告诉服务器客户端支持的压缩格式
-
-
2、响应:
-
1)状态行
-
HTTP/1.1 200 OK
- 协议 HTTP 1.1
- 状态码:
- 200 成功
- 404 页面没找到
- 301 内容没变化,用在缓存
-
-
2)响应头(主要在开发下载应用的时候使用的)
- Date: Tue, 24 Mar 2015 01:52:25 GMT 访问日期
- Server: Apache/2.4.9 (Unix) 访问服务器的类型
- Content-Location: index.html.en 访问的文件名
- Content-Length: 45 访问文件的大小
- Content-Type: text/html 访问文件的类型
-
3)数据实体
<html><body><h1>It works!</h1></body></html>
访问服务器最需的,相当于 NSURLConnection 异步方法回调中的 data。
-
-
TCP 客户端
// 包含头文件 #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import <ifaddrs.h> @property (nonatomic, assign) int clientSocket; - (void)socketHttpRequest { // 连接到服务器,80 端口 apache 中就是 http 的协议 if (![self connection:@"127.0.0.1" port:80]) { return; } // 创建请求 NSString *requestStr = @"GET / HTTP/1.1\n" "Host: localhost\n" "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:36.0) Gecko/20100101 Firefox/36.0\n" "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\n" "Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3\n" // \n 拼接换行,内容由 firefox 浏览器的网络控制面板的原始请求头信息中复制 "Accept-Encoding: gzip, deflate\n" // 删除最后三行,最后一行加两个 \n\n "Connection: keep-alive\n\n"; // 发送接收请求,发送给 web 服务器的请求需要遵守 http 协议 NSString *recvStr = [self sendAndRecv:requestStr]; NSLog(@"%@", recvStr); } /// 创建连接 - (BOOL)connection:(NSString *)hostText port:(int)port { // 创建 Socket self.clientSocket = socket(AF_INET, SOCK_STREAM, 0); // 连接到服务器 struct sockaddr_in serverAddress; bzero(&serverAddress, sizeof(serverAddress)); serverAddress.sin_family = AF_INET; serverAddress.sin_port = htons(port); serverAddress.sin_addr.s_addr = inet_addr(hostText.UTF8String); int isConnected = connect(self.clientSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)); return (isConnected == 0); } /// 发送和接收字符串 - (NSString *)sendAndRecv:(NSString *)message { // 发送数据 send(self.clientSocket, message.UTF8String, strlen(message.UTF8String), 0); // 接收数据 uint8_t buffer[1024]; ssize_t recvLen = recv(self.clientSocket, buffer, sizeof(buffer), 0); NSData *data = [NSData dataWithBytes:buffer length:recvLen]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; return str; }
4、客户端使用
-
TCP 客户端 & UDP
// 包含头文件 #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import <ifaddrs.h> typedef NS_ENUM (NSInteger,ServerType) { ServerTypeTCP, ServerTypeUDP }; @property (weak, nonatomic) IBOutlet UITextField *desIPTF; @property (weak, nonatomic) IBOutlet UITextField *desPortTF; @property (weak, nonatomic) IBOutlet UITextField *locIPTF; @property (weak, nonatomic) IBOutlet UITextField *locPortTF; @property (weak, nonatomic) IBOutlet UITextView *recvTextView; @property (weak, nonatomic) IBOutlet UITextField *sendTF; @property (nonatomic, assign) int clientSockfd; @property (nonatomic, assign) int udpSockfd; @property (nonatomic, assign) ServerType serType; - (void)viewDidLoad { [super viewDidLoad]; NSString *ip_addr = [self getIPAddress]; _locIPTF.text = ip_addr; } #pragma mark - 创建 TCP 连接 - (IBAction)connectTCPServer:(id)sender { [NSThread detachNewThreadSelector:@selector(connectTCPSer) toTarget:self withObject:nil]; } - (void)connectTCPSer { self.serType = ServerTypeTCP; BOOL success; int err; // 创建 socket self.clientSockfd = socket(AF_INET, SOCK_STREAM, 0); success = (self.clientSockfd != -1); NSString *logStr = nil; if (success == NO) { logStr = @"创建 socket 失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"创建 socket 成功...\n"; [self showMessage:logStr]; // 服务器地址 struct sockaddr_in ser_addr; memset(&ser_addr, 0, sizeof(ser_addr)); ser_addr.sin_len = sizeof(ser_addr); ser_addr.sin_family = AF_INET; ser_addr.sin_port = htons(_desPortTF.text.intValue); ser_addr.sin_addr.s_addr = inet_addr(_desIPTF.text.UTF8String); // 连接 err = connect(self.clientSockfd, (struct sockaddr *)&ser_addr, sizeof(ser_addr)); success = (err == 0); } if (success == NO) { logStr = @"socket 连接失败...\n"; [self showMessage:logStr]; return; } else { logStr = [NSString stringWithFormat:@"socket 连接 %@ 成功...\n", _desIPTF.text]; [self showMessage:logStr]; char buf[1024]; do { // 接收数据 ssize_t recvLen = recv(self.clientSockfd, buf, sizeof(buf), 0); if (recvLen > 0) { logStr = [NSString stringWithFormat:@"recv:%@\n", [NSString stringWithUTF8String:buf]]; [self showMessage:logStr]; } } while (strcmp(buf, "exit") != 0); } } #pragma mark - 创建 UDP 连接 - (IBAction)connectUDP:(id)sender { [NSThread detachNewThreadSelector:@selector(connectUDP) toTarget:self withObject:nil]; } - (void)connectUDP { self.serType = ServerTypeUDP; BOOL success; int err; // 创建 Socket self.udpSockfd = socket(AF_INET, SOCK_DGRAM, 0); success = (self.udpSockfd != -1); NSString *logStr = nil; if (success == NO) { logStr = @"创建 socket 失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"创建 socket 成功...\n"; [self showMessage:logStr]; // 本地地址 struct sockaddr_in loc_addr; bzero(&loc_addr, sizeof(loc_addr)); loc_addr.sin_port = htons(_locPortTF.text.intValue); loc_addr.sin_addr.s_addr = inet_addr(_locIPTF.text.UTF8String); // 绑定 err = bind(self.udpSockfd, (const struct sockaddr *)&loc_addr, sizeof(loc_addr)); success = (err == 0); } if (success == NO) { logStr = @"socket 绑定失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"socket 绑定成功...\n"; [self showMessage:logStr]; // 目标地址 struct sockaddr_in des_addr; bzero(&des_addr, sizeof(des_addr)); des_addr.sin_family = AF_INET; des_addr.sin_port = htons(_desPortTF.text.intValue); des_addr.sin_addr.s_addr = inet_addr(_desIPTF.text.UTF8String); char buf[256]; bzero(buf, sizeof(buf)); while(1) { // 接收数据 socklen_t des_addr_len = sizeof(des_addr); ssize_t recvLen = recvfrom(self.udpSockfd, buf, sizeof(buf), 0, (struct sockaddr*)&des_addr, &des_addr_len); if (recvLen > 0) { logStr = [NSString stringWithFormat:@"recv:%@\n", [NSString stringWithUTF8String:buf]]; [self showMessage:logStr]; } } } } #pragma mark - 发送数据 - (IBAction)btnClick:(id)sender { if (_sendTF.text.length == 0) { return; } else { ssize_t sendLen; if (self.serType == ServerTypeTCP) { // 发送数据 sendLen = send(self.clientSockfd, _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0); } else if (self.serType == ServerTypeUDP) { // 目标地址 struct sockaddr_in des_addr; bzero(&des_addr, sizeof(des_addr)); des_addr.sin_family = AF_INET; des_addr.sin_port = htons(_desPortTF.text.intValue); des_addr.sin_addr.s_addr = inet_addr(_desIPTF.text.UTF8String); // 发送数据 sendLen = sendto(self.udpSockfd, _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0, (struct sockaddr *)&des_addr, sizeof(des_addr)); } if (sendLen > 0) { NSString *logStr = [NSString stringWithFormat:@"send:%@\n", _sendTF.text]; [self showMessage:logStr]; } } } #pragma mark - 获取本地 IP 地址 - (NSString *)getIPAddress { NSString *address = @"error"; struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; // retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { // Loop through linked list of interfaces temp_addr = interfaces; while (temp_addr != NULL) { if (temp_addr->ifa_addr->sa_family == AF_INET) { // Check if interface is en0 which is the wifi connection on the iPhone if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { // Get NSString from C String address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; } } temp_addr = temp_addr->ifa_next; } } // Free memory freeifaddrs(interfaces); return address; } // 显示信息 - (void)showMessage:(NSString *)msg { dispatch_async(dispatch_get_main_queue(), ^{ _recvTextView.text = [_recvTextView.text stringByAppendingString:msg]; }); }
5、服务端使用
-
TCP 服务端 & UDP
// 包含头文件 #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> #import <ifaddrs.h> typedef NS_ENUM (NSInteger,ServerType) { ServerTypeTCP, ServerTypeUDP }; @property (weak, nonatomic) IBOutlet UITextField *desIPTF; @property (weak, nonatomic) IBOutlet UITextField *desPortTF; @property (weak, nonatomic) IBOutlet UITextField *locIPTF; @property (weak, nonatomic) IBOutlet UITextField *locPortTF; @property (weak, nonatomic) IBOutlet UITextView *recvTextView; @property (weak, nonatomic) IBOutlet UITextField *sendTF; @property (nonatomic, assign) int serverSockfd; @property (nonatomic, assign) int clientSockfd; @property (nonatomic, assign) int udpSockfd; @property (nonatomic, assign) ServerType serType; - (void)viewDidLoad { [super viewDidLoad]; NSString *ip_addr = [self getIPAddress]; _locIPTF.text = ip_addr; } #pragma mark - 创建 TCP 监听 - (IBAction)createdTCPServer:(id)sender { _desIPTF.enabled = NO; _desPortTF.enabled = NO; _locPortTF.enabled = NO; [NSThread detachNewThreadSelector:@selector(createdTCPSer) toTarget:self withObject:nil]; } - (void)createdTCPSer { self.serType = ServerTypeTCP; BOOL success; int err; // 创建 socket self.serverSockfd = socket(AF_INET, SOCK_STREAM, 0); success = (self.serverSockfd != -1); NSString *logStr = nil; if (success == NO) { logStr = @"创建 socket 失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"创建 socket 成功...\n"; [self showMessage:logStr]; // 本地地址 struct sockaddr_in loc_addr; memset(&loc_addr, 0, sizeof(loc_addr)); loc_addr.sin_family = AF_INET; loc_addr.sin_port = htons(_locPortTF.text.intValue); loc_addr.sin_addr.s_addr = inet_addr(_locIPTF.text.UTF8String); // 绑定 err = bind(self.serverSockfd, (const struct sockaddr *)&loc_addr, sizeof(loc_addr)); success = (err == 0); } if (success == NO) { logStr = @"socket 绑定失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"socket 绑定成功...\n"; [self showMessage:logStr]; // 监听 err = listen(self.serverSockfd, 5); // 5:最大连接个数 success = (err == 0); } if (success == NO) { logStr = @"socket 监听失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"socket 监听成功...\n"; [self showMessage:logStr]; while (true) { // 循环 // 连接的客户端的地址 struct sockaddr_in cli_addr; socklen_t cli_addr_Len = sizeof(cli_addr); // 接受客户端的请求,并建立连接 self.clientSockfd = accept(self.serverSockfd, (struct sockaddr *)&cli_addr, &cli_addr_Len); success = (self.clientSockfd != -1); if (success) { char *ip_addr = inet_ntoa(cli_addr.sin_addr); int ip_port = ntohs(cli_addr.sin_port); dispatch_async(dispatch_get_main_queue(), ^{ _desIPTF.text = [NSString stringWithFormat:@"%s", ip_addr]; _desPortTF.text = [NSString stringWithFormat:@"%d", ip_port]; }); logStr = [NSString stringWithFormat:@"已连接:%s,port:%d\n", ip_addr, ip_port]; [self showMessage:logStr]; char buf[1024]; do { // 返回读取的字节数 ssize_t recvLen = recv(self.clientSockfd, buf, sizeof(buf), 0); if (recvLen > 0) { logStr = [NSString stringWithFormat:@"recv:%@\n", [NSString stringWithUTF8String:buf]]; [self showMessage:logStr]; } } while (strcmp(buf, "exit") != 0); } } } } #pragma mark - 创建 UDP 连接 - (IBAction)connectUDP:(id)sender { [NSThread detachNewThreadSelector:@selector(connectUDP) toTarget:self withObject:nil]; } - (void)connectUDP { self.serType = ServerTypeUDP; BOOL success; int err; // 创建 Socket self.udpSockfd = socket(AF_INET, SOCK_DGRAM, 0); success = (self.udpSockfd != -1); NSString *logStr = nil; if (success == NO) { logStr = @"创建 socket 失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"创建 socket 成功...\n"; [self showMessage:logStr]; // 本地地址 struct sockaddr_in loc_addr; bzero(&loc_addr, sizeof(loc_addr)); loc_addr.sin_port = htons(_locPortTF.text.intValue); loc_addr.sin_addr.s_addr = inet_addr(_locIPTF.text.UTF8String); // 绑定 err = bind(self.udpSockfd, (const struct sockaddr *)&loc_addr, sizeof(loc_addr)); success = (err == 0); } if (success == NO) { logStr = @"socket 绑定失败...\n"; [self showMessage:logStr]; return; } else { logStr = @"socket 绑定成功...\n"; [self showMessage:logStr]; // 目标地址 struct sockaddr_in des_addr; bzero(&des_addr, sizeof(des_addr)); des_addr.sin_family = AF_INET; des_addr.sin_port = htons(_desPortTF.text.intValue); des_addr.sin_addr.s_addr = inet_addr(_desIPTF.text.UTF8String); char buf[256]; bzero(buf, sizeof(buf)); while(1) { // 接收数据 socklen_t des_addr_len = sizeof(des_addr); ssize_t recvLen = recvfrom(self.udpSockfd, buf, sizeof(buf), 0, (struct sockaddr*)&des_addr, &des_addr_len); if (recvLen > 0) { logStr = [NSString stringWithFormat:@"recv:%@\n", [NSString stringWithUTF8String:buf]]; [self showMessage:logStr]; } } } } #pragma mark - 发送数据 - (IBAction)btnClick:(id)sender { if (_sendTF.text.length == 0) { return; } else { ssize_t sendLen; if (self.serType == ServerTypeTCP) { // 发送数据 sendLen = send(self.clientSockfd, _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0); } else if (self.serType == ServerTypeUDP) { // 目标地址 struct sockaddr_in des_addr; bzero(&des_addr, sizeof(des_addr)); des_addr.sin_family = AF_INET; des_addr.sin_port = htons(_desPortTF.text.intValue); des_addr.sin_addr.s_addr = inet_addr(_desIPTF.text.UTF8String); // 发送数据 sendLen = sendto(self.udpSockfd, _sendTF.text.UTF8String, strlen(_sendTF.text.UTF8String), 0, (struct sockaddr *)&des_addr, sizeof(des_addr)); } if (sendLen > 0) { NSString *logStr = [NSString stringWithFormat:@"send:%@\n", _sendTF.text]; [self showMessage:logStr]; } } } #pragma mark - 获取本地 IP 地址 - (NSString *)getIPAddress { NSString *address = @"error"; struct ifaddrs *interfaces = NULL; struct ifaddrs *temp_addr = NULL; int success = 0; // retrieve the current interfaces - returns 0 on success success = getifaddrs(&interfaces); if (success == 0) { // Loop through linked list of interfaces temp_addr = interfaces; while (temp_addr != NULL) { if (temp_addr->ifa_addr->sa_family == AF_INET) { // Check if interface is en0 which is the wifi connection on the iPhone if ([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:@"en0"]) { // Get NSString from C String address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)]; } } temp_addr = temp_addr->ifa_next; } } // Free memory freeifaddrs(interfaces); return address; } // 显示信息 - (void)showMessage:(NSString *)msg { dispatch_async(dispatch_get_main_queue(), ^{ _recvTextView.text = [_recvTextView.text stringByAppendingString:msg]; }); }