Socket 网络编程

1.TCP/IP、UDP

TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
UDP(User Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
下面这张图,表明了这些协议的关系,如图TCP/IP协议族包括运输层、网络层、链路层:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KaSkEsnM-1649328223880)(http://img-storage.qiniudn.com/15-6-2/33737213.jpg)]

2.Socket

在图1中,我们没有看到Socket的影子,那么它到底在哪里呢?还是用图来说话,一目了然。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ANQi1lJI-1649328223880)(http://img-storage.qiniudn.com/15-6-2/58505525.jpg)]

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

3.使用socket

前人已经给我们做了好多的事了,网络间的通信也就简单了许多,但毕竟还是有挺多工作要做的。以前听到Socket编程,觉得它是比较高深的编程知识,但是只要弄清Socket编程的工作原理,神秘的面纱也就揭开了。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AP9l1TbP-1649328223881)(http://img-storage.qiniudn.com/15-6-2/64757972.jpg)]

一个生活中的场景。你要打电话给一个朋友,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。生活中的场景就解释了这工作原理,也许TCP/IP协议族就是诞生于生活中,这也不一定。

4.代码示例

    #import "ViewController.h"
    #import <sys/socket.h>
    #import <netinet/in.h>
    #import <arpa/inet.h>

    @interface ViewController ()

    // 客户端 socket
    @property (nonatomic, assign) int clientSocket;

    @property (nonatomic, weak) IBOutlet UIWebView *myWebView;

    @end

    @implementation ViewController
    /**
     ======= 请求头 =======
     1> 请求行
        GET / HTTP/1.1

        GET        表示从服务器"拿"数据
        /          访问服务器的根目录
        HTTP/1.1   HTTP的协议以及版本

     2> 请求头
        Host:              要访问的主机
        User-Agent:        用户代理,告诉服务器客户端的类型
        Accept:            告诉服务器客户端支持的数据格式
        Accept-Language:   告诉服务器客户端支持的语言
        Accept-Encoding:   告诉服务器客户端支持的解压缩类型

     HTTP请求本身的格式,是字符串格式的,最末一行,以 

 结束,表示所有请求的内容发送完毕,服务器可以处理!

     提示:key 值是 HTTP 协议定义的,不能随意更改!

    ==========响应 ===========
     1> 状态行

        HTTP/1.1 200 OK

     HTTP/1.1   HTTP的协议以及版本
        200     状态码(404)
        2XX     成功
        4XX     客户端错误
        5XX     服务器错误

     2> 响应头,服务器告诉客户端的一些信息
     提示:响应的信息,通常在开发“下载”功能时,才会使用,平时用不着

     Date:      响应日期
     Server:    服务器的信息

     // 文件长度
     Content-Length: 24

     3> 数据实体,从服务器返回的二进制数据!
     println("hello world!");

     */
    - (void)viewDidLoad
    {
        [super viewDidLoad];

        // http 端口是 80
        if ([self connectToHost:@"61.135.169.125" port:80])
        {
            NSLog(@"连接成功");
        }

        // 发送消息给 web 服务器
        // 建立请求字符串
        NSString *request = @"GET / HTTP/1.1
"
        "Host: www.baidu.com
"
        "User-Agent: iPhone AppleWebKit

";

        // 对结果的处理
        NSString *result = [self sendAndRecv:request];
        // 找 
 的出现的位置
        NSRange range = [result rangeOfString:@"

"];

        // 判断是否找到
        if (range.location == NSNotFound)
        {
            NSLog(@"html 错误");
            return;
        }

        NSString *html = [result substringFromIndex:range.location];

        [self.myWebView loadHTMLString:html baseURL:[NSURL URLWithString:@"http://www.baidu.com"]];
    }

    - (BOOL)connectToHost:(NSString *)host port:(int)port
    {
        // 1. socket
        /**
         参数

         domain:    协议域,AF_INET
         type:      Socket 类型,SOCK_STREAM(流 TCP)/SOCK_DGRAM(报文,提示:
                    在有些公司的程序员给服务器发送数据,会说:发送报文)
         protocol:  IPPROTO_TCP,提示:如果输入0,会根据第二个参数,自动选择协议

         返回值
         socket     如果>0,就是正确的
         */
        self.clientSocket = socket(AF_INET, SOCK_STREAM, 0);

        // 2. 连接到另外一台计算机
        /**
         参数
         1> 客户端socket
         2> 指向数据结构sockaddr的指针,其中包括目的端口和IP地址
            C 语言中没有对象,实现都是通过结构体来实现的
         3> 结构体数据长度
         返回值
            0 成功/其他 错误代号,很多C语言的程序都会如此设计,原因:成功只有一个,失败会有很多种!
            C 语言中,通常是非零即真
         */

        struct sockaddr_in serverAddress;

        // 1> 地址 inet_addr 可以将 ip 地址转换成整数
        // 提示:在网络上的使用的很多数据,需要做字节翻转
        serverAddress.sin_addr.s_addr = inet_addr(host.UTF8String);

        // 2> 端口 htons 可以将端口转换成整数
        // 端口号同样要做字节翻转
        serverAddress.sin_port = htons(port);

        // 3> 协议
        serverAddress.sin_family = AF_INET;

        // 在 C 语言中,通常传递结构体的指针同时,会传递结构体的尺寸
        int result = connect(self.clientSocket, (const struct sockaddr *)&serverAddress, sizeof(serverAddress));

        return (result == 0);
    }

    /**
     *  发送&接收消息
     */
    - (NSString *)sendAndRecv:(NSString *)msg {
        // 3. 发送数据给服务器
        /**
         参数
         1> 客户端socket
         2> 发送内容地址
         3> 发送内容长度
         4> 发送方式标志,一般为0
         返回值
         如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR

         提示:在很多C语言框架中,会将基本数据类型进行封装,使用的时候,便于后续框架的调整
         */

        // 在 UTF8 编码中,一个中文对应 3 个字节
        ssize_t sendLen = send(self.clientSocket, msg.UTF8String, strlen(msg.UTF8String), 0);

        // 4. 从服务器接收数据

        uint8_t buffer[1024];

        // 1> 定义一个可变的二进制数据,方便拼劲
        NSMutableData *dataM = [NSMutableData data];

        // 服务器返回的回车计数
        int count = 0;

        while (count == 0)
        {
            // 2> 接收数据
            ssize_t recvLen = recv(self.clientSocket, buffer, sizeof(buffer), 0);
            [dataM appendBytes:buffer length:recvLen];

            // 通过跟踪,

 需要继续等待,
 表示传输结束
            // 取末尾一个字节
            uint8_t lastByte = buffer[recvLen - 1];

            // 取倒数第三个字节
            uint8_t preLastByte = buffer[recvLen - 3];

            if (lastByte == '
' && preLastByte != '
') {
                count++;
            }
        }

        return [[NSString alloc] initWithData:dataM encoding:NSUTF8StringEncoding];
    }

    /**
     *  断开连接
     */
    - (void)disconnect {
        // 5. 关闭连接
        close(self.clientSocket);
    }

@end

5. 延伸阅读

1.Linux socket 编程:
http://www.ibm.com/developerworks/cn/education/linux/l-sock/l-sock.html
2. 网络编程学习笔记一:Socket编程:
http://blog.csdn.net/gneveek/article/details/8699198

)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值