iOS开发基础40-网络编程:HTTP 协议、GET/POST、NSURLConnection 与 NSURLSession
iOS 网络编程深度解析:HTTP 协议、GET/POST、NSURLConnection 与 NSURLSession
本文系统梳理 iOS 网络编程:网络基础概念、URL 结构、HTTP 协议特性、GET 与 POST 对比、HTTP 请求/响应结构、NSURLConnection(已废弃)与 NSURLSession(推荐)的使用,以及常见状态码和 HTTPS 基础。
一、网络基础
1. 为什么学习网络编程
移动互联网时代,几乎所有应用都需要与服务器交换数据实现实时更新:新闻资讯、视频播放、音乐播放、地图导航、电商购物、社交应用等。
2. 基本概念
| 概念 | 说明 |
|---|---|
| 客户端(Client) | 运行在用户设备上的应用(iOS/Android App) |
| 服务器(Server) | 提供服务和数据的远程计算机 |
| 请求(Request) | 客户端向服务器索取/提交数据 |
| 响应(Response) | 服务器处理请求后返回的数据 |
3. 服务器类型
- 远程服务器:上线后的正式服务器,供全体用户使用。
- 本地服务器:开发测试阶段使用,部署在本地或内网,速度快,便于调试。
二、URL 与协议
1. URL 定义
URL(Uniform Resource Locator,统一资源定位符)用于定位互联网上的唯一资源,客户端通过 URL 找到目标服务器。
2. URL 完整格式
协议://主机地址:端口号/路径?查询参数#片段
| 组成部分 | 说明 | 示例 |
|---|---|---|
| 协议 | 数据传输方式 | https |
| 主机地址 | 服务器 IP 或域名 | api.example.com |
| 端口号 | 服务端口(可省略,HTTP 默认 80,HTTPS 默认 443) | 443 |
| 路径 | 资源在服务器中的位置 | /v1/users |
| 查询参数 | 键值对参数,? 开头,& 分隔 |
?page=1&size=20 |
| 片段 | 锚点,# 开头,仅客户端使用 |
#section1 |
3. 常见协议
| 协议 | 说明 | 默认端口 |
|---|---|---|
| HTTP | 超文本传输协议,明文传输 | 80 |
| HTTPS | 安全 HTTP,SSL/TLS 加密传输 | 443 |
| FTP | 文件传输协议 | 21 |
| FILE | 本地文件访问 | - |
HTTP vs HTTPS:HTTPS 在 HTTP 基础上加入 SSL/TLS 加密层,数据传输加密、服务器身份验证(证书)、数据完整性校验,防止窃听和篡改。现代 App 应优先使用 HTTPS,iOS 9+ 的 ATS(App Transport Security)默认禁止明文 HTTP 请求。
三、HTTP 协议
1. HTTP 简介
HTTP(Hypertext Transfer Protocol,超文本传输协议)规定客户端与服务器之间的数据传输格式,是 Web 和移动端最常用的应用层协议。
2. HTTP 特点
- 简单快速:协议格式简单,通信速度快。
- 灵活:通过 Content-Type 允许传输各种类型数据(JSON、XML、图片、文件等)。
- 无状态(Stateless):服务器不保存客户端的状态信息,每次请求相互独立。如需保持状态(如登录态),通过 Cookie/Session/Token 实现。
- 连接模式:
- HTTP 0.9/1.0:非持续连接,每次请求建立一次 TCP 连接,响应后立即断开。
- HTTP 1.1:默认持续连接(Keep-Alive),一次 TCP 连接可处理多个请求,减少握手开销。
3. HTTP 通信过程
客户端 ──请求(Request)──▶ 服务器
客户端 ◀──响应(Response)── 服务器
一个完整的 HTTP 通信 = 一个请求 + 一个响应。
四、GET 与 POST
1. HTTP 请求方法
HTTP/1.1 定义了多种请求方法,最常用的是 GET 和 POST:
| 方法 | 说明 |
|---|---|
| GET | 获取资源(查询、读取) |
| POST | 提交数据(创建、登录) |
| PUT | 更新资源(全量替换) |
| DELETE | 删除资源 |
| PATCH | 部分更新 |
| HEAD | 获取响应头(无响应体) |
| OPTIONS | 查询服务器支持的方法 |
RESTful 风格中,GET 查询、POST 创建、PUT 更新、DELETE 删除,语义清晰。
2. GET 与 POST 对比
| 对比项 | GET | POST |
|---|---|---|
| 参数位置 | 附加在 URL 之后(?key=value) |
放在请求体(Body)中 |
| 数据可见性 | 参数在 URL 中可见 | 参数不可见(但并非加密,抓包仍可看到) |
| 数据量限制 | 受浏览器和服务器 URL 长度限制(非 HTTP 协议限制) | 通常无严格限制(服务器可配置上限) |
| 缓存 | 可被浏览器缓存 | 默认不缓存 |
| 书签/历史 | 可收藏为书签、保留在历史记录 | 不可 |
| 适用场景 | 查询、获取数据 | 提交、创建、敏感数据 |
关于 GET 参数长度限制:HTTP 协议本身没有规定 URL 长度上限,限制来自具体的浏览器和服务器实现。例如旧版 IE 限制约 2083 字符,Apache 默认限制约 8192 字符。因此"GET 参数不能超过 1KB"的说法不准确,实际限制因环境而异,但 GET 不适合传输大量数据。
3. GET 与 POST 选择
- 查询/获取数据 → GET
- 提交/创建数据 → POST
- 大量数据 → POST
- 敏感信息(密码、Token) → POST(配合 HTTPS 加密)
- RESTful 更新 → PUT,删除 → DELETE
五、iOS 网络请求方案
1. 苹果原生方案
| 方案 | 说明 | 状态 |
|---|---|---|
| NSURLConnection | 早期网络 API,同步/异步/代理 | iOS 9 起废弃 |
| NSURLSession | 功能强大,支持后台下载/上传、任务管理 | 推荐 |
| CFNetwork | 底层 C 语言网络接口 | 底层定制 |
2. 第三方框架
| 框架 | 说明 |
|---|---|
| AFNetworking | Objective-C 最常用网络库,基于 NSURLSession 封装 |
| Alamofire | AFNetworking 的 Swift 版本 |
| MKNetworkKit | 轻量网络库,使用者较少 |
3. 建议
新项目推荐使用 NSURLSession(苹果原生)或 AFNetworking/Alamofire(第三方封装,简化代码)。
六、HTTP 请求与响应结构
1. HTTP 请求
一个完整的 HTTP 请求包含:
请求行(方法 + 路径 + HTTP 版本)
请求头(Key: Value,多行)
空行
请求体(Body,POST/PUT 才有)
示例:
POST /login HTTP/1.1
Host: api.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 29
User-Agent: MyApp/1.0
username=test&pwd=password
常见请求头:
| 请求头 | 说明 |
|---|---|
| Host | 服务器域名 |
| Content-Type | 请求体数据类型(application/json、multipart/form-data 等) |
| Content-Length | 请求体长度 |
| User-Agent | 客户端标识 |
| Authorization | 认证信息(Token) |
| Accept | 客户端可接受的响应类型 |
| Cookie | 客户端携带的 Cookie |
2. HTTP 响应
一个完整的 HTTP 响应包含:
状态行(HTTP 版本 + 状态码 + 状态描述)
响应头(Key: Value,多行)
空行
响应体(Body,服务器返回的数据)
示例:
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 42
Server: nginx
{"code":0,"message":"success","data":{}}
常见响应头:
| 响应头 | 说明 |
|---|---|
| Content-Type | 响应体数据类型 |
| Content-Length | 响应体长度 |
| Server | 服务器类型 |
| Set-Cookie | 设置 Cookie |
| Location | 重定向目标 URL(3xx 状态码) |
| Cache-Control | 缓存控制 |
七、NSURLConnection(已废弃,了解)
NSURLConnection 是 iOS 早期的网络 API,iOS 9 起被 NSURLSession 取代,新项目不推荐使用。
1. 常用类
| 类 | 说明 |
|---|---|
| NSURL | 请求地址 |
| NSURLRequest | 请求对象(不可变) |
| NSMutableURLRequest | 可变请求对象 |
| NSURLConnection | 发送请求、建立连接、处理响应 |
2. 同步请求(阻塞当前线程)
NSURL *url = [NSURL URLWithString:@"http://example.com/data"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [NSURLConnection sendSynchronousRequest:request
returningResponse:&response
error:&error];
同步请求会阻塞当前线程,禁止在主线程调用,否则 UI 卡死。
3. 异步请求(Block 回调)
NSURL *url = [NSURL URLWithString:@"http://example.com/data"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
[NSURLConnection sendAsynchronousRequest:request
queue:[NSOperationQueue mainQueue]
completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
if (!connectionError && data) {
NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@", result);
}
}];
4. 代理方式
@interface ViewController () <NSURLConnectionDataDelegate>
@property (nonatomic, strong) NSMutableData *responseData;
@end
@implementation ViewController
- (void)startRequest {
NSURL *url = [NSURL URLWithString:@"http://example.com/data"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.responseData = [NSMutableData data];
[[NSURLConnection alloc] initWithRequest:request delegate:self];
}
// 接收到服务器响应
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
[self.responseData setLength:0];
}
// 接收到数据(可能调用多次,需拼接)
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.responseData appendData:data];
}
// 请求完成
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSString *result = [[NSString alloc] initWithData:self.responseData encoding:NSUTF8StringEncoding];
NSLog(@"%@", result);
}
// 请求出错
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
NSLog(@"错误: %@", error);
}
@end
5. NSMutableURLRequest 常用设置
NSURL *url = [NSURL URLWithString:@"http://example.com/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST"; // 请求方法
request.HTTPBody = [@"username=test&pwd=123" dataUsingEncoding:NSUTF8StringEncoding]; // 请求体
request.timeoutInterval = 15; // 超时时间(秒)
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; // 请求头
[request setValue:@"Bearer token123" forHTTPHeaderField:@"Authorization"];
6. GET 请求创建
NSString *urlStr = @"http://example.com/search?keyword=hello&page=1";
// URL 编码(处理中文、特殊字符)
// stringByAddingPercentEscapesUsingEncoding 已废弃,改用:
urlStr = [urlStr stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
NSURL *url = [NSURL URLWithString:urlStr];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
stringByAddingPercentEscapesUsingEncoding:在 iOS 9 已废弃,应使用stringByAddingPercentEncodingWithAllowedCharacters:。
八、NSURLSession(推荐)
NSURLSession 是 iOS 7+ 引入的现代网络 API,取代 NSURLConnection,支持后台下载/上传、任务管理、配置灵活。
1. 基本使用(Data Task)
// 获取共享 session(基本配置)
NSURLSession *session = [NSURLSession sharedSession];
NSURL *url = [NSURL URLWithString:@"https://api.example.com/data"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
// 创建数据任务
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
if (error) {
NSLog(@"请求失败: %@", error);
return;
}
// 解析响应数据(在子线程执行,更新 UI 需回主线程)
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"结果: %@", json);
});
}];
[task resume]; // 任务默认挂起,必须 resume 才开始
2. POST 请求(JSON)
NSURL *url = [NSURL URLWithString:@"https://api.example.com/login"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *params = @{@"username": @"test", @"pwd": @"123"};
request.HTTPBody = [NSJSONSerialization dataWithJSONObject:params options:0 error:nil];
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request
completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
// 处理响应
}];
[task resume];
3. 自定义 Session 配置
// 默认配置(类似 sharedSession,但可自定义)
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
config.timeoutIntervalForRequest = 15; // 请求超时
config.timeoutIntervalForResource = 30; // 资源超时
config.HTTPAdditionalHeaders = @{@"User-Agent": @"MyApp/1.0"}; // 额外请求头
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:self
delegateQueue:[NSOperationQueue mainQueue]];
4. 下载任务(Download Task)
NSURL *url = [NSURL URLWithString:@"https://example.com/file.zip"];
NSURLSessionDownloadTask *task = [[NSURLSession sharedSession] downloadTaskWithURL:url
completionHandler:^(NSURL *location, NSURLResponse *response, NSError *error) {
// location 是临时文件路径,需移动到目标位置
NSString *dest = [NSTemporaryDirectory() stringByAppendingPathComponent:@"file.zip"];
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:dest] error:nil];
}];
[task resume];
NSURLSession 任务(DataTask/DownloadTask/UploadTask)创建后默认挂起,必须调用
resume才开始执行。可调用suspend暂停、cancel取消。
九、HTTP 状态码
| 状态码 | 类别 | 说明 |
|---|---|---|
| 200 | 2xx 成功 | 请求成功 |
| 201 | 2xx 成功 | 已创建(资源创建成功) |
| 204 | 2xx 成功 | 无内容(成功但无响应体) |
| 301 | 3xx 重定向 | 永久重定向 |
| 302 | 3xx 重定向 | 临时重定向 |
| 304 | 3xx 重定向 | 未修改(使用缓存) |
| 400 | 4xx 客户端错误 | 请求参数错误 |
| 401 | 4xx 客户端错误 | 未授权(未登录/Token 失效) |
| 403 | 4xx 客户端错误 | 禁止访问(无权限) |
| 404 | 4xx 客户端错误 | 资源未找到 |
| 405 | 4xx 客户端错误 | 方法不允许 |
| 500 | 5xx 服务器错误 | 服务器内部错误 |
| 502 | 5xx 服务器错误 | 错误网关 |
| 503 | 5xx 服务器错误 | 服务不可用 |
| 504 | 5xx 服务器错误 | 网关超时 |
十、Swift 版本对照
NSURLSession(Swift)
// GET 请求
let url = URL(string: "https://api.example.com/data")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("请求失败: \(error)")
return
}
guard let data = data else { return }
do {
let json = try JSONSerialization.jsonObject(with: data)
DispatchQueue.main.async {
print("结果: \(json)")
}
} catch {
print("解析失败: \(error)")
}
}
task.resume() // 必须 resume
// POST 请求(JSON)
var request = URLRequest(url: URL(string: "https://api.example.com/login")!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: ["username": "test", "pwd": "123"])
URLSession.shared.dataTask(with: request) { data, _, error in
// 处理响应
}.resume()
// 下载任务
let downloadURL = URL(string: "https://example.com/file.zip")!
URLSession.shared.downloadTask(with: downloadURL) { location, _, error in
guard let location = location else { return }
let dest = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("file.zip")
try? FileManager.default.moveItem(at: location, to: dest)
}.resume()
URL 编码
let urlStr = "https://example.com/search?keyword=你好"
let encoded = urlStr.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let url = URL(string: encoded!)
十一、总结
- 网络基础:客户端发送请求、服务器返回响应;远程服务器用于生产,本地服务器用于开发测试。
- URL:格式为
协议://主机:端口/路径?参数#片段,HTTP 默认端口 80,HTTPS 默认 443;HTTPS 在 HTTP 基础上加密传输,iOS ATS 默认禁止明文 HTTP。 - HTTP 协议:简单快速、灵活、无状态;HTTP 1.1 默认持续连接(Keep-Alive);通信过程 = 请求 + 响应。
- GET vs POST:GET 参数在 URL(可见、受 URL 长度限制、可缓存),POST 参数在请求体(不可见、数据量大、不缓存);HTTP 协议本身无 URL 长度限制,限制来自浏览器/服务器;查询用 GET,提交用 POST,RESTful 中 PUT 更新、DELETE 删除。
- 请求/响应结构:请求 = 请求行 + 请求头 + 空行 + 请求体;响应 = 状态行 + 响应头 + 空行 + 响应体。
- NSURLConnection:iOS 早期 API,iOS 9 起废弃;支持同步(阻塞)、异步(Block)、代理三种方式;同步请求禁止在主线程调用。
- NSURLSession:iOS 7+ 推荐 API,取代 NSURLConnection;支持 DataTask(普通请求)、DownloadTask(下载)、UploadTask(上传)、后台会话;任务创建后默认挂起,必须
resume才执行;可自定义超时、请求头、代理。 - 状态码:2xx 成功、3xx 重定向、4xx 客户端错误、401 未授权、403 禁止、404 未找到、5xx 服务器错误。
- URL 编码:
stringByAddingPercentEscapesUsingEncoding:已废弃,改用stringByAddingPercentEncodingWithAllowedCharacters:(OC)/addingPercentEncoding(withAllowedCharacters:)(Swift)。

浙公网安备 33010602011771号