iOS - Socket 网络套接字
1、Socket
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个 Socket。Socket 又称 "套接字",应用程序通常通过 "套接字" 向网络发出请求或者应答网络请求。
Socket 的英文原义是 “孔” 或 “插座”。作为 BSD UNIX 的进程通信机制,取后一种意思。通常也称作 "套接字",用于描述 IP 地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在 Internet 上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个 Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket 正如其英文原意那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供 220 伏交流电,有的提供 110 伏交流电,有的则提供有线电视节目。客户软件将插头插到不同编号的插座,就可以得到不同的服务。
- Socket 就是为网络服务提供的一种机制。
- 在 Unix中,网络即是 Socket,并不局限在 TCP/UDP。
- Socket 可以用于自定义协议。
- 通信的两端都是 Socket。
- 网络通信其实就是 Socket 间的通信。
- 数据在两个 Socket 间通过 IO 传输。
Socket 开始是纯 C 语言的,是跨平台的。
1.1 网络
1、IP 地址(主机名):
网络中设备的唯一标示。不易记忆,可以用主机名(域名)。
1) IP V4:
- 0~255.0~255.0~255.0~255 ,共有 2\^8\^4 = 2\^32 = 42 亿。
2) 本地回环地址:
每台机器都有自己的本地回环地址,ip 为 127.0.0.1 ,主机名为 localhost。如果 127.0.0.1 ping 不通,则网卡不正常。
本地 hosts 文件修改,终端:
$ cd /etc
$ sudo vim hosts
$ 输入密码进入 hosts 文件编辑界面
$ 将光标移动到指定位置
- 英文输入模式下按 i 键进入编辑状态,
- 英文输入模式下按 esc 键进入命令状态,
- 在命令状态下输入 :wq 回车,保存退出 hosts 文件。
2、端口号:
- 用于标示进程的逻辑地址,不同进程的标示。
有效端口为 0 ~ 65535,其中 0 ~ 1024 由系统使用或者保留端口,开发中不要使用 1024 以下的端口。
1) Netcat 的使用:
- Netcat 是 Mac 终端下用于调试和检查网络的工具包,可用于创建 TCP/IP 连接。
- 终端:
$ nc -lk 12345
,开启监听,终端将始终监听本地计算机 12345 端口的数据。
3、传输协议(通讯的规则):
1) TCP:传输控制协议:
- 建立连接,形成传输数据的通道(建立连接的三次握手,断开连接的四次握手)。
- 在连接中进行大数据传输,数据大小不收限制。
- 通过三次握手完成连接,是可靠协议,数据安全送达。
- 必须建立连接,效率会稍低。
2) UDP:用户数据报协议:
- 只管发送,不确认对方是否接收到。
- 不需要建立连接,将数据及源和目的封装成数据包中,每个数据报的大小限制在 64K 之内。
- 因为无需连接,因此是不可靠协议。
- 不需要建立连接,速度快。
- 应用场景:多媒体教室/网络流媒体。
3) 常见网络协议:
应用层协议 端口 说明 HTTP 80 超文本传输协议 HTTPS 443 HTTP+SSL,HTTP 的安全版 FTP 20, 21, 990 文件传输 POP3 110 邮局协议 SMTP 25 简单邮件传输协议 telnet 23 远程终端协议
4、网络参考模型:
ISO 参考模型 TCP/IP 参考 说明 应用层 应用层 表示层 会话层 传输层 传输层 Socket 开发,TCP 协议,UDP 协议 网络层 网络互连层 路由器,IP 协议 数据链路层 网络接口层 交换机 物理层 网线
1.2 Socket 通讯示意图

1.3 Socket 连接过程
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
1) 服务器监听:
- 是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
2) 客户端请求:
- 是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
3) 连接确认:
- 是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
1.4 Socket 常用函数
1) 创建:
函数原型: int socket(int domain, int type, int protocol); 参数说明: domain:协议域,又称协议族(family)。常用的协议族有 AF_INET(ipv4)、 AF_INET6(ipv6)、 AF_LOCAL(或称 AF_UNIX,Unix 域 Socket)、 AF_ROUTE 等。 协议族决定了 socket 的地址类型,在通信中必须采用对应的地址,如 AF_INET 决定了要用 ipv4 地址 (32 位的)与端口号(16 位的)的组合、AF_UNIX 决定了要用一个绝对路径名作为地址。 type:指定 Socket 类型。常用的 socket 类型有 SOCK_STREAM(流式/TCP)、 SOCK_DGRAM(数据报式/UDP)、 SOCK_RAW、 SOCK_PACKET、 SOCK_SEQPACKET 等。 流式 Socket(SOCK_STREAM)是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用。数据报式 Socket(SOCK_DGRAM)是一种无连接的 Socket,对应于无连接的 UDP 服务应用。 protocol:指定协议。常用协议有 IPPROTO_TCP(TCP 传输协议)、 IPPROTO_UDP(UDP 传输协议)、 IPPROTO_STCP(STCP 传输协议)、 IPPROTO_TIPC(TIPC 传输协议)等, 注意:1. type 和 protocol 不可以随意组合,如 SOCK_STREAM 不可以跟 IPPROTO_UDP 组合。当第三个参数为 0 时, 会自动选择第二个参数类型对应的默认协议。 2. Windows Socket 下 protocol 参数中不存在 IPPROTO_STCP。 返回值: 如果调用成功就返回新创建的套接字的描述符,如果失败就返回 INVALID_SOCKET(Linux 下失败返回 -1)。套接字描 述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应 关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找 到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓 冲里。
2) 绑定:
函数原型: int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len); 参数说明: socket:是一个套接字描述符。 address:是一个 sockaddr 结构指针,该结构中包含了要结合的地址和端口号。 address_len:确定 address 缓冲区的长度。 返回值: 如果函数执行成功,返回值为 0,否则为 SOCKET_ERROR。
3) 接收:
函数原型: int recv(SOCKET socket, char FAR* buf, int len, int flags); 参数说明: socket:一个标识已连接套接口的描述字。 buf:用于接收数据的缓冲区。 len:缓冲区长度。 flags:指定调用方式。取值:MSG_PEEK 查看当前数据,数据将被复制到缓冲区中,但并不从输入队列中删除; MSG_OOB 指示接收到 out-of-band 数据(即需要优先处理的数据)。 返回值: 若无错误发生,recv() 返回读入的字节数。如果连接已中止,返回 0。否则的话,返回 SOCKET_ERROR 错误,应用程序 可通过 WSAGetLastError() 获取相应错误代码。 函数原型: ssize_t recvfrom(int sockfd, void buf, int len, unsigned int flags, struct socketaddr* from, socket_t* fromlen); 参数说明: sockfd:标识一个已连接套接口的描述字。 buf:接收数据缓冲区。 len:缓冲区长度。 flags:调用操作方式。是以下一个或者多个标志的组合体,可通过 or 操作连在一起: (1)MSG_DONTWAIT:操作不会被阻塞; (2)MSG_ERRQUEUE:指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传 递进来,使用者应该提供足够大的缓冲区。导致错误的原封包通过 msg_iovec 作为一般的数据 来传递。导致错误的数据报原目标地址作为 msg_name 被提供。错误以 sock_extended_err 结构形态被使用。 (3)MSG_PEEK:指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据。 (4)MSG_TRUNC:返回封包的实际长度,即使它比所提供的缓冲区更长, 只对 packet 套接字有效。 (5)MSG_WAITALL:要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者 下次被接收的数据类型不同,仍会返回少于请求量的数据。 (6)MSG_EOR:指示记录的结束,返回的数据完成一个记录。 (7)MSG_CTRUNC:指明由于缓冲区空间不足,一些控制数据已被丢弃。 (8)MSG_OOB:指示接收到 out-of-band 数据(即需要优先处理的数据)。 (9)MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据。 from:(可选)指针,指向装有源地址的缓冲区。 fromlen:(可选)指针,指向from缓冲区长度值。
4) 发送:
函数原型: int sendto(SOCKET s, const char FAR* buf, int size, int flags, const struct sockaddr FAR* to, int tolen); 参数说明: s:套接字 buf:待发送数据的缓冲区 size:缓冲区长度 flags:调用方式标志位, 一般为 0, 改变 Flags,将会改变 Sendto 发送的形式 addr:(可选)指针,指向目的套接字的地址 tolen:addr 所指地址的长度 返回值: 如果成功,则返回发送的字节数,失败则返回 SOCKET_ERROR。
5) 接收连接请求:
函数原型: int accept(int fd, struct socketaddr* addr, socklen_t* len); 参数说明: fd:套接字描述符。 addr:返回连接着的地址 len:接收返回地址的缓冲区长度 返回值: 成功返回客户端的文件描述符,失败返回 -1。
2、Socket 的基本使用
Objective-C
包含头文件
#import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h>
创建 Socket
int socket(int, int, int); 参数: int domain :协议域,AF_INET(IPV4 的网络开发) int type :Socket 类型,SOCK_STREAM(TCP),SOCK_DGRAM(UDP 报文) int protocol:IPPROTO_TCP 协议,如果输入 0,可以根据第二个参数,自动选择协议 返回值: int :如果 > 0 就表示成功 int clientSocket = socket(AF_INET, SOCK_STREAM, 0); if (clientSocket > 0) { NSLog(@"Socket 创建成功 %d", clientSocket); } else { NSLog(@"Socket 创建失败"); }
连接到服务器
int connect(int, const struct sockaddr *, socklen_t); 参数: int :客户端 socket const struct sockaddr * :指向数据结构 sockaddr 的指针,其中包括目的端口和 IP 地址,服务器的 "结构体" 地址,C 语言没有对象 socklen_t :结构体数据长度 返回值: int :0 成功,其他 错误代号 // 创建服务器地址结构体,internet style struct sockaddr_in serverAddress; // IPV4 - 协议 serverAddress.sin_family = AF_INET; // inet_addr 函数可以把 ip 地址转换成一个整数 serverAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); // 端口 端口数据高位在前低位在后 12345 => 3039 => 3930 serverAddress.sin_port = htons(12345); int isConnected = connect(clientSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress)); if (isConnected) { NSLog(@"连接失败 %d", isConnected); } else { NSLog(@"连接成功"); }
发送数据
ssize_t send(int, const void *, size_t, int); 参数: int :客户端 socket const void * :发送内容地址 void * == id,message.UTF8String 将字符串转换成 UTF8 的 ASCII 码,一个汉字需要 3 个字节 size_t :发送内容长度,是字节的个数,strlen 计算所有字节的长度 int :发送方式标志,一般为 0 返回值: ssize_t :如果成功,则返回发送的字节数,失败则返回 SOCKET_ERROR NSString *message = @"约吗?"; ssize_t sendLen = send(clientSocket, message.UTF8String, strlen(message.UTF8String), 0); NSLog(@"发送 %ld 个字节数据", sendLen);
接收数据
ssize_t recv(int, void *, size_t, int); 参数: int :客户端 socket void * :接收内容的地址 size_t :接收内容的长度 int :接收数据的标记,0 是阻塞式,一直等待服务器的数据 返回值: ssize_t :接收到的数据长度 // 接收字符串的数组,typedef unsigned char uint8_t; uint8_t buffer[1024]; ssize_t recvLen = recv(clientSocket, buffer, sizeof(buffer), 0); NSLog(@"接收到 %ld 个字节数据", recvLen); // 按照服务器返回的长度,从 buffer 中,读取二进制数据,建立 NSData 对象 NSData *data = [NSData dataWithBytes:buffer length:recvLen]; NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"接收到:%@", str);
关闭连接
int close(int) __DARWIN_ALIAS_C(close); 参数: int :客户端 socket 返回值: int :0 成功关闭连接 // 数据传递完毕之后,关闭连接 int isCloseed = close(clientSocket); NSLog(@"%d", isCloseed);
3、Socket 聊天
Objective-C
// 包含头文件 #import <sys/socket.h> #import <netinet/in.h> #import <arpa/inet.h> @property (weak, nonatomic) IBOutlet UITextField *hostText; @property (weak, nonatomic) IBOutlet UITextField *portText; @property (weak, nonatomic) IBOutlet UITextField *messageText; @property (weak, nonatomic) IBOutlet UILabel *recvLabel; @property (weak, nonatomic) IBOutlet UIButton *sendBtn; /// client Socket @property (nonatomic, assign) int clientSocket; - (IBAction)conn:(UIButton *)sender { if (sender.isSelected) { // 断开连接 [self disConnection]; sender.selected = NO; [sender setTitle:@"连接" forState:UIControlStateNormal]; self.sendBtn.enabled = NO; } else { // 创建连接 if ([self connection:self.hostText.text port:self.portText.text.intValue]) { self.recvLabel.text = @"连接成功"; sender.selected = YES; [sender setTitle:@"断开" forState:UIControlStateSelected]; self.sendBtn.enabled = YES; } else { self.recvLabel.text = @"连接失败"; } } } - (IBAction)send:(UIButton *)sender { // 发送数据 self.recvLabel.text = [self sendAndRecv:self.messageText.text]; } /// 创建连接 - (BOOL)connection:(NSString *)hostText port:(int)port { // 创建 Socket self.clientSocket = socket(AF_INET, SOCK_STREAM, 0); // 连接到服务器 struct sockaddr_in serverAddress; serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = inet_addr(hostText.UTF8String); serverAddress.sin_port = htons(port); 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; } /// 断开连接 - (void)disConnection { close(self.clientSocket); }
4、Socket 网络访问
请求:
1、请求行
GET / HTTP/1.1
- 方法 GET
- 路径 /
协议 HTTP 1.1
2、请求头
- Host: localhost 主机
- User-Agent: 告诉服务器客户端的类型
- Accept: 告诉服务器客户端支持的格式
- Accept-Language: 告诉服务器客户端的语言
- Accept-Encoding: 告诉服务器客户端支持的压缩格式
响应:
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。
Objective-C
- (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; serverAddress.sin_family = AF_INET; serverAddress.sin_addr.s_addr = inet_addr(hostText.UTF8String); serverAddress.sin_port = htons(port); 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; }
iOS - Socket 网络套接字的更多相关文章
- sanic官方文档解析之Custom Protocols(自定义协议)和Socket(网络套接字)
1,Custom Protocol:自定义协议 温馨提示:自定义协议是一个高级用法,大多数的读者不需要用到此功能 通过特殊的自定义协议,你可以改变sanic的协议,自定义协议需要继承子类asyncio ...
- Pyhont 网络编程【第一篇】初始Socket网络套接字
一.什么是socket: Socket 别名 “网络套接字”,指网络通信链句柄 其实就是一堆网络信息(ip+端口) 建立起的链接称之为socket,Socket的英文原义是“孔”或“插座”,用来实现不 ...
- 什么是网络套接字(Socket)?
什么是网络套接字(Socket)?一时还真不好回答,而且网络上也有各种解释,莫衷一是.下文将以本人所查阅到的资料来说明一下什么是Socket. Socket定义 Socket在维基百科的定义: A n ...
- Java Socket:飞鸽传书的网络套接字
在古代,由于通信不便利,一些聪明的人就利用鸽子会飞且飞得比较快.会辨认方向的优点,对其进行了驯化,用来进行消息的传递——也就是所谓的“飞鸽传书”.而在 Java 中,网络套接字(Socket)扮演了同 ...
- Linux Socket 原始套接字编程
对于linux网络编程来说,可以简单的分为标准套接字编程和原始套接字编程,标准套接字主要就是应用层数据的传输,原始套接字则是可以获得不止是应用层的其他层不同协议的数据.与标准套接字相区别的主要是要开发 ...
- C++网络套接字编程TCP和UDP实例
原文地址:C++网络套接字编程TCP和UDP实例作者:xiaojiangjiang 1. 创建一个简单的SOCKET编程流程如下 面向有连接的套接字编程 服务器: 1) 创建套接字(so ...
- Socket称"套接字"
Socket又称"套接字",应用程序通常通过"套接字"向网络发出请求或者应答网络请求. 二.利用Socket建立网络连接的步骤 建立Socket连接至少需要一对 ...
- Win2 Socket(套接字)相关 API
Socket(套接字) 作者信息 肖进 单位:南京中萃食品有限公司 资讯部 邮箱:xiaoj@njb.swirebev.com 电话:025-58642091 与socket有关的一些函数介绍 1.读 ...
- python 全栈开发,Day33(tcp协议和udp协议,互联网协议与osi模型,socket概念,套接字(socket)初使用)
先来回顾一下昨天的内容 网络编程开发架构 B/S C/S架构网卡 mac地址网段 ip地址 : 表示了一台电脑在网络中的位置 子网掩码 : ip和子网掩码按位与得到网段 网关ip : 内置在路由器中的 ...
随机推荐
- iOS抓包Charles 操作
今天就来看一下Mac上如何进行抓包,之前有一篇文章介绍了使用Fidder进行抓包 http://blog.csdn.net/jiangwei0910410003/article/details/198 ...
- php中urlencode与rawurlencode的区别有那些呢
urlencode 函数: 返回字符串,此字符串中除了 -_. 之外的所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+).此编码与 WWW 表单 POST 数据的编码 ...
- oracle-odu小试牛刀--恢复drop表的数据
现在进入oracle12c时代:普遍用的oracle版本为10g以上.在oracle10g之后提供了一个回收的机制.所以恢复drop表的数据以及表很容易.当然需要打开回收机制以及是归档模式. ...
- java程序 输入10个数字并求和
课程作业: 模仿JavaAppArguments.java示例,编写编写一个程序,此程序从命令行接受多个数字,求和之后输出结果. 设计思想: 先从命令行读出数字,然后计算各个数字之和.求出结果. 流程 ...
- 关闭用miniUI打开的窗口
miniUI打开的窗口用window.close关闭无效, 应该用window.CloseOwnerWindow();
- android应用程序中获取view 的位置
1. 相对位置: getLeft() , getRight(), getTop(), getBottom() 在Android中可以把left相当于X轴值, top相当于Y轴值, 通过这两个值Andr ...
- js笔记---拖动元素
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...
- json返回日期格式化的解决
function jsonDateFormat(jsonDate) {//json日期格式转换为正常格式 try { var date = new Date(parseInt(jsonDate.rep ...
- 下载服务器端的图片和下载excel
#region 下载 /// <summary> /// 下载资源 /// </summary> public void Download() { SaveFileDialog ...
- ResultSetMetaData和DatabaseMetaData实现数据库中属性,属性值,属性所赋值的获取等
----------------------------------------------有些类下面代码中有; public class Test1 { TestDAO t=new TestDAO( ...