socket(套接字)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。
建立Socket连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket,另一个运行于服务器端,称为ServerSocket。套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
Socket可以支持不同的传输层协议(TCP或UDP)是网络开发中不可或缺的一部分。
1、创建连接Socket(af,sockettype,protocol)
//1 创建socket
// 第一个参数 domain 协议族 指定IPV4
// 第二个参数 type socket类型 流socket 数据报socket
// 第三个参数 protocol 协议
// 返回值 如果创建成功返回1
int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
2、连接服务器Connect(sockid, destaddr, addrlen)
//2 连接服务器
// 第一个参数 socket描述符
// 第二个参数 结构体 ip地址和端口
// 第三个参数 结构体到长度 sizeof
// 返回值 成功0
struct sockaddr_in addr;
addr.sin_family = AF_INET;
//htons用来转换端口号为大尾数据
addr.sin_port = htons(12345);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
int result = connect(clientSocket, (const struct sockaddr *)&addr, sizeof(addr));
if (result != 0) {
NSLog(@"失败");
return ;
}
3、向服务器发送数据send(sockid, buff, bufflen)
//3 向服务器发送数据
// 第一个参数 socket描述符
// 第二个参数 发送到消息
// 第三个参数 发送到字符个数
// 第四个参数 是否阻塞
const char * msg = "hello world";
ssize_t sendCount = send(clientSocket, msg, strlen(msg), 0);
NSLog(@"发送到字节数 %zd ",sendCount);
4、接收服务器返回到数据recv(sockid, buffer, sizeof(buffer), 0)
//4 接收服务器返回到数据
// 第一个参数 socket描述符
// 第二个参数 接收数据的数组
// 第三个参数 发送到字符大小
// 第四个参数 是否阻塞
uint8_t buffer[1024];
NSMutableData *mData = [NSMutableData data];
//首次返回数据
ssize_t revCount = recv(clientSocket, buffer, sizeof(buffer), 0);
[mData appendBytes:buffer length:revCount];
//接收所有的数据
while (revCount != 0) {
revCount = recv(clientSocket, buffer, sizeof(buffer), 0);
NSLog(@"接收的字节数:%zd",revCount);
[mData appendBytes:buffer length:revCount];
}
//把字节数组转换为字符
NSString *revMsg = [[NSString alloc]initWithData:mData encoding:NSUTF8StringEncoding];
NSLog(@"接收到的消息:%@",revMsg);
5、关闭连接close(sockid)
// 5 关闭连接
close(clientSocket);
当让我们也可以把这些步骤进行封装整理,方便日后使用。
下面为一个请求百度的socket demo:
简单写了几个方法方便调用
#pragma mark - 连接服务器
- (BOOL)connectWithIp:(NSString *)ip port:(int)port
{
//1 创建socket
// 第一个参数 domain 协议族 指定IPV4
// 第二个参数 type socket类型 流socket 数据报socket
// 第三个参数 protocol 协议
// 返回值 如果创建成功返回1
int clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
self.clientSocket = clientSocket;
//2 连接服务器
// 第一个参数 socket描述符
// 第二个参数 结构体 ip地址和端口
// 第三个参数 结构体到长度 sizeof
// 返回值 成功0
struct sockaddr_in addr;
addr.sin_family = AF_INET;
//htons用来转换端口号为大尾数据
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.UTF8String);
int result = connect(clientSocket, (const struct sockaddr *)&addr, sizeof(addr));
if (result != 0) {
NSLog(@"失败");
return NO;
}
else{
NSLog(@"成功");
return YES;
}
}
#pragma mark - 发送/接收数据
- (NSString *)sendAndRev:(NSString *)msg
{
//3 向服务器发送数据
// 第一个参数 socket描述符
// 第二个参数 发送到消息
// 第三个参数 发送到字符个数
// 第四个参数 是否阻塞
const char * tmsg = msg.UTF8String;
ssize_t sendCount = send(self.clientSocket, tmsg ,strlen(tmsg), 0);
NSLog(@"发送到字节数 %zd ",sendCount);
//4 接收服务器返回到数据
// 第一个参数 socket描述符
// 第二个参数 接收数据的数组
// 第三个参数 发送到字符大小
// 第四个参数 是否阻塞
uint8_t buffer[1024];
NSMutableData *mData = [NSMutableData data];
//首次返回数据
ssize_t revCount = recv(self.clientSocket, buffer, sizeof(buffer), 0);
[mData appendBytes:buffer length:revCount];
//接收所有的数据
while (revCount != 0) {
revCount = recv(self.clientSocket, buffer, sizeof(buffer), 0);
NSLog(@"接收的字节数:%zd",revCount);
[mData appendBytes:buffer length:revCount];
}
//把字节数组转换为字符
NSString *revMsg = [[NSString alloc]initWithData:mData encoding:NSUTF8StringEncoding];
NSLog(@"接收到的消息:%@",revMsg);
return revMsg;
}
#pragma mark - 关闭连接
- (void)closeConnect{
// 5 关闭连接
close(self.clientSocket);
}
设置请求:
//连接服务器
BOOL isOK = [self connectWithIp:@"61.135.169.125" port:80];
if (!isOK) {
return;
}
NSString *request = @"GET / HTTP/1.1\r\n"
@"Host: www.baidu.com\r\n"
@"User-Agent: Mozilla/5.0 (iPad; CPU OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1\r\n"
@"Connection: close\r\n\r\n";
NSString *response = [self sendAndRev:request];
NSLog(@"响应头+内容=%@",response);
//关闭连接
close(self.clientSocket);
if (response.length>0) {
// 截取响应内容
NSRange range = [response rangeOfString:@"\r\n\r\n"];
NSString *html = [response substringFromIndex:range.length+range.location];
NSLog(@"响应内容=%@",html);
}
设置请求头时需要注意头部的参数格式
1、键-值间有空格隔开,每个参数独占一行,以’\r\n’进行换行
2、最后一个参数以’\r\n\r\n’进行结尾
3、User-Agent不要放在最后一个