iOS开发——轻松学习Socket
<h3 id="socket是什么">Socket是什么?</h3><p>要了解什么是 <code>Socket</code>,首先需要了解 <code>TCP/IP、UDP</code>! 📚</p>
<blockquote>
<p><code>TCP/IP(Transmission Control Protocol/Internet Protocol)</code>即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。 <code>UDP(User Data Protocol,用户数据报协议)</code>是与TCP相对应的协议。它是属于<code>TCP/IP</code>协议族中的一种。</p>
</blockquote>
<p><img src="https://upload-images.jianshu.io/upload_images/16555213-0a195cabffbffac7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"></p>
<p><strong><code>TCP/IP协议族</code>包括运输层、网络层、链路层。现在你知道TCP/IP与UDP的关系了吧。</strong></p>
<p>如果大家对于 <code>七层协议模型</code> 不是非常了解的话,你可以,沉下心来看看下面👇🏻 这张图:</p>
<blockquote>
<p><strong>作为一个开发者,有一个学习的氛围跟一个交流圈子特别重要,这是一个我的iOS交流群:834688868,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长!</strong></p>
</blockquote>
<p>如果你正在面试,或者正准备跳槽,不妨看看我精心总结的面试资料: BAT 大厂最新面试题+答案合集(持续更新中) 来获取一份详细的大厂面试资料 为你的跳槽加薪多一份保障</p>
<p><img src="https://upload-images.jianshu.io/upload_images/16555213-2007a27a97b95dc0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"></p>
<p>那么<code>TCP/IP、UDP</code>和<code>socket</code>又有什么关系呢???</p>
<blockquote>
<p><code>Socket</code>是<code>应用层</code>与<code>TCP/IP协议族</code>通信的<code>中间软件抽象层</code>,它是一组<code>接口</code>。在设计模式中,<code>Socket</code>其实就是一个<code>门面模式</code>,它把复杂的<code>TCP/IP协议族</code>隐藏在<code>Socket</code>接口后面,对用户来说,一组简单的接口就是全部,让<code>Socket</code>去组织数据,以符合指定的协议。</p>
</blockquote>
<p><img src="https://upload-images.jianshu.io/upload_images/16555213-fea1bd2320b67562.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"></p>
<h3 id="socket怎么用">Socket怎么用</h3>
<p>这里其实还有很多网络相关的知识需要科普,但是篇幅有限,如果靓仔靓女的你对于这一块有点陌生,你可以买一些网络相关的书籍</p>
<ul>
<li><code>《TCP/IP详解 卷1:协议》</code></li>
<li><code>《图解HTTP》</code></li>
<li><code>《Unix网络编程》</code></li>
<li><code>HTTPS权威指南</code></li>
</ul>
<blockquote>
<p>Socket 作为一套接口,那么是怎么用的呢? 下面一图胜前言: 👇</p>
</blockquote>
<p><img src="https://upload-images.jianshu.io/upload_images/16555213-96d8f37c2a7c6447.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240"></p>
<p><code>Socket</code> 传输的特点:</p>
<ul>
<li>1: 传输数据为<code>字节级</code>,<code>传输数据可自定义</code>,数据量<code>小</code>(对于手机应用讲:费用低)</li>
<li>2: <code>传输数据时间短,性能高</code></li>
<li>3: 适合于客户端和服务器端之间信息实时交互</li>
<li>4: 可以加密,数据安全性强</li>
</ul>
<p>正因为这些优势,常被用来做即时通讯重要媒介</p>
<p>[图片上传失败...(image-8db8b3-1634195139896)]</p>
<p>上图就是通过 <code>socket</code> 在客户端和终端做的来回通讯</p>
<p><strong>socket使用代码实现:</strong></p>
<h4 id="1-创建socket">1: 创建<code>socket</code></h4>
<pre><code>int socketID = socket(AF_INET, SOCK_STREAM, 0);
self.clinenId= socketID;
if (socketID == -1) {
NSLog(@"创建socket 失败");
return;
}
复制代码
</code></pre>
<ul>
<li><code>domain</code>:协议域,又<code>称协议族(family)</code>。常用的协议族有<code>AF_INET</code>、<code>AF_INET6</code>、<code>AF_LOCAL</code>(或称<code>AF_UNIX</code>,<code>Unix</code>域<code>Socket</code>)、<code>AF_ROUTE</code>等。协议族决定了<code>socket</code>的地址类型,在通信中必须采用对应的地址,如<code>AF_INET</code>决定了要用<code>ipv4</code>地址(32位的)与端口号(16位的)的组合、<code>AF_UNIX</code>决定了要用一个绝对路径名作为地址。</li>
<li><code>type</code>:指定Socket类型。常用的socket类型有<code>SOCK_STREAM</code>、<code>SOCK_DGRAM</code>、<code>SOCK_RAW</code>、<code>SOCK_PACKET</code>、<code>SOCK_SEQPACKET</code>等。流式<code>Socket</code>(<code>SOCK_STREAM</code>)是一种面向连接的<code>Socket</code>,针对于面向连接的<code>TCP</code>服务应用。数据报式<code>Socket(SOCK_DGRAM)</code>是一种无连接的<code>Socket</code>,对应于无连接的UDP服务应用。</li>
<li><code>protocol</code>:指定协议。常用协议有<code>IPPROTO_TCP</code>、<code>IPPROTO_UDP</code>、<code>IPPROTO_STCP</code>、<code>IPPROTO_TIPC</code>等,分别对应<code>TCP</code>传输协议、<code>UDP传输协议</code>、<code>STCP</code>传输协议、<code>TIPC</code>传输协议。</li>
</ul>
<blockquote>
<p>注意:1.<code>type</code>和<code>protocol</code>不可以随意组合,如<code>SOCK_STREAM</code>不可以跟<code>IPPROTO_UDP</code>组合。当第三个参数为<code>0</code>时,会自动选择第二个参数类型对应的默认协议。</p>
</blockquote>
<ul>
<li><code>返回值</code>:如果调用成功就返回新创建的套接字的描述符,如果失败就返回<code>INVALID_SOCKET</code>(<code>Linux</code>下失败返回<code>-1</code>)</li>
</ul>
<h4 id="2-建立连接">2: 建立连接</h4>
<pre><code>int result = connect(socketID, (const struct sockaddr *)&socketAddr, sizeof(socketAddr));
if (result != 0) {
NSLog(@"链接失败");
return;
}
NSLog(@"链接成功");
复制代码
</code></pre>
<ul>
<li><code>参数一</code>:套接字描述符</li>
<li><code>参数二</code>:指向数据结构<code>sockaddr</code>的指针,其中包括目的端口和IP地址</li>
<li><code>参数三</code>:参数二<code>sockaddr</code>的长度,可以通过<code>sizeof(struct sockaddr)</code>获得</li>
<li><code>返回值</code>: 成功则返回<code>0</code>,失败返回<code>非0</code>,错误码<code>GetLastError()</code>。</li>
</ul>
<pre><code>struct sockaddr_in socketAddr;
socketAddr.sin_family = AF_INET;
socketAddr.sin_port = SocketPort;
struct in_addr socketIn_addr;
socketIn_addr.s_addr= SocketIP;
socketAddr.sin_addr = socketIn_addr;
复制代码
</code></pre>
<ul>
<li><code>__uint8_t sin_len</code>; 假如没有这个成员,其所占的一个字节被并入到<code>sin_family</code>成员中</li>
<li><code>sa_family_t sin_family</code>; 一般来说<code>AF_INET</code>(地址族)<code>PF_INET</code>(协议族)</li>
<li><code>in_port_t sin_port</code>; // 端口</li>
<li><code>struct in_addr sin_addr</code>; // <code>ip</code></li>
<li><code>char sin_zero</code>; 没有实际意义,只是为了 跟<code>SOCKADDR</code>结构在内存中对齐</li>
</ul>
<h4 id="3-发送消息">3: 发送消息</h4>
<pre><code>if (self.sendMsgContent_tf.text.length == 0) {
return;
}
const char *msg = self.sendMsgContent_tf.text.UTF8String;
ssize_t sendLen = send(self.clinenId, msg, strlen(msg), 0);
NSLog(@"发送 %ld 字节",sendLen);
;
self.sendMsgContent_tf.text = @"";
复制代码
</code></pre>
<ul>
<li><code>s</code>:一个用于标识已连接套接口的描述字。</li>
<li><code>buf</code>:包含待发送数据的缓冲区。</li>
<li><code>len</code>:缓冲区中数据的长度。</li>
<li><code>flags</code>:调用执行方式。</li>
<li><code>返回值</code>: 如果成功,则返回发送的字节数,失败则返回<code>SOCKET_ERROR</code>.一个中文对应 <code>3 个字节</code>!<code>UTF8</code> 编码!</li>
</ul>
<h4 id="4-接受消息">4: 接受消息</h4>
<pre><code>while (1) {
uint8_t buffer;
ssize_t recvLen = recv(self.clinenId, buffer, sizeof(buffer), 0);
if (recvLen == 0) {
NSLog(@"接收到了0个字节");
continue;
}
// buffer -> data -> string
NSData *data = ;
NSString *str= [ initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"%@---%@",,str);
dispatch_async(dispatch_get_main_queue(), ^{
;
self.sendMsgContent_tf.text = @"";
});
}
复制代码
</code></pre>
<ul>
<li><code>参数一</code>: 客户端<code>socket</code></li>
<li><code>参数二</code>: 接收内容缓冲区地址</li>
<li><code>参数三</code>: 接收内容缓存区长度</li>
<li><code>参数四</code>: 接收方式,<code>0表示阻塞</code>,必须等待服务器返回数据</li>
<li><code>返回值</code>:如果成功,则返回读入的字节数,失败则返回<code>SOCKET_ERROR</code></li>
</ul>
<h3 id="socket-总结">Socket 总结</h3>
<p><code>Socket</code> 的使用还是非常简单的! 对于iOS开发有点吃力的原因是在于:</p>
<ul>
<li>都是C函数</li>
<li>函数参数多并且陌生</li>
<li>网络这一块知识的盲区</li>
</ul>
<p>链接:https://juejin.cn/post/7018054170741571591<br>
来源:稀土掘金</p><br><br>
来源:https://www.cnblogs.com/iOSer1122/p/15406916.html
頁:
[1]