详解网络通讯Socket、TCP、UDP

来源:互联网 发布:mac ndk r10d 下载 编辑:程序博客网 时间:2024/06/05 12:03

首先我们在用C语言建立Socket链接通讯前先简单介绍一下 网络通讯的三要素、网络模型、Socket、UDP、TCP:

网络通讯的三要素:

1、IP地址。网路中设备的标示。  

2、端口号。用于标示进程的逻辑地址,不同进程的标示,有效端口 0~65535。  

3、传输协议。例如TCP、UDP等等协议。


OSI网络模型:

从上到下:应用层(信息显示在app上) --> 表示层(对文件类型处理,例如文本,音频、视频等) --> 会话层(根据传输规定格式进行传输,才能事变输出数据,例如http有一个标准传输文本格式) --> 传输层 --> 网络层 --> 数据链路层 --> 物理层

简化版TCP/IP参考模型:应用层(应用层,表示层,回话层简写) --> 传输层(TCP/UDP) --> 网络互联层(IP) --> 网络接口层(数据链路层和物理层成的简写)


UDP:(用户数据报文协议)   ---   可以理解为发短信,只管发,但是不能确信保证对方是否已经收到。

要点:

a.只管发送,不确认对方是否收到。

b.将数据及源和目的封装成数据包,不需要确信是否已经连接成功。

c.每个数据包的大小限制在64K之内。

d.因为不需要确定是否已经连接成功,所以是不可靠的传输协议,但是由于不需要进行可靠的握手连接,发送速度比较快。

我们平时观看视频直播,玩的游戏(英雄联盟等)一般都是走UDP协议。传输效率高。有时候我们观看视频直播时候会偶尔发现,视频屏幕上会出现雪花般的小斑点,屏幕不清晰。玩游戏突然间屏幕出现卡顿现象,过一会儿画面上人物环境又变为另外一个场景。这就是典型的UDP协议丢包现象。


TCP:(传输控制协议) --- 可以理解为打电话,确认对方已接通电话,再进行通话交流。

要点:

a.建立连接,形成传输数据的通道。

b.通过三次握手完成连接,是可靠的协议,安全送达,保证数据正确性及数据顺序

c.在连接中进行大数据传输,数据大小不受限制,流模式形式进行传输

d.由于必须建立连接才能传输,建立连接需要开销较多时间,所以相比UDP传输协议效率会相对低些。

注意:TCP协议的三次握手:

    第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

    第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

    第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。


我们平时下载大文件一般都是走TCP协议。

套接字(Socket) ---  可以理解为   ip + 端口 == 一个socket

  套接字(Socket)是通信的基石,是支持TCP/IP协议的网络通信的基本操作单元。它是网络通信过程中端点的抽象表示,包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,远地进程的协议端口。

     应用层通过传输层进行数据通信时,服务器会遇到同时为多个应用程序进程提供并发服务的问题。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCPIP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。


建立Socket连接进行TCP协议传输:

  建立 Socket 连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket, 客服端的ip+端口。另一个运行于服务器端,称为ServerSocket,服务器端的ip+端口

  套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。

  服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。

  客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。

  连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。

在进行TCP协议传输数据的前提是建立Socket连接,创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCPUDP、自定义协议等)。


常用的Socket类型有两种:流式SocketSOCK_STREAM)和数据报式SocketSOCK_DGRAM)。流式是一种面向连接的 Socket,针对于面向连接的TCP服务应用;数据报式 Socket是一种无连接的 Socket,对应于无连接的UDP服务应用。


接着下面通过我们用C语言写一个使用TCP协议的进行数据传输:

ViewConroller导入头文件:

#import <sys/socket.h>

#import <netinet/in.h>

#import <arpa/inet.h>


申请一个客服端的 socket属性

@property(assign,nonatomic)int clientSocket;


- (void)viewDidLoad {

    [superviewDidLoad];

    [selfdoConnecting];

    NSString * request =@"哈哈哈";

    //发送消息

    [self sendAndRecv:request];

}


//MARK: 外部方法

//连接

- (void)doConnecting {

    [selfconnectToHost:@"112.80.248.74"port:80];

}



//MARK: SOCKET 演练

- (void)connectToHost:(NSString *)host port:(int)port{

    //1.创建Socket

    /**

     参数

     domain:    协议域,AF_INET -->IPV4

     type:      Socket 类型,SOCK_STREAM(TCP)/SOCK_DGRAM(报文 UDP)

     protocol: IPPROTO_TCP,如果传入0,会自动根据第二个参数,选中合适的协议

     返回值

     socket > 0 就是成功

     */


    self.clientSocket =socket(AF_INET,SOCK_STREAM,0);

    

    

    //2.连接到服务器

    /*

     参数

     1> 客户端socket

     2> 指向数据结构sockaddr的指针,其中包括目的端口和IP地址

     3> 结构体数据长度

     返回值

     0 成功/其他 错误代号

     */

    struct sockaddr_in serverAddr;

    serverAddr.sin_family =AF_INET;//IPV4(2 32)  IPV6(2 64)

    //端口

    serverAddr.sin_port = htons(port);

    //IP地址

    serverAddr.sin_addr.s_addr =inet_addr(host.UTF8String);

    //在c语言开发中,经常传递一个数据结构体的指针,的同时,需要你传入结构的长度

   int connResult = connect(self.clientSocket, (conststructsockaddr *)&serverAddr,sizeof(serverAddr));

    if (connResult == 0) {

        NSLog(@"连接成功");

    }else{

        NSLog(@"失败 %d",connResult);

        return;

    }

}



//发送和接受

-(NSString *)sendAndRecv:(NSString *)sendMsg{

    //3.发送数据到服务器

    /*

     参数

     1> 客户端socket

     2> 发送内容地址

     3> 发送内容长度

     4> 发送方式标志,一般为0

     返回值

     如果成功,则返回发送的字节数,失败则返回SOCKET_ERROR

     */

    ssize_t sendLen = send(self.clientSocket, sendMsg.UTF8String,strlen(sendMsg.UTF8String),0);

    NSLog(@"发送了 %ld 个字节 ",sendLen);

    //4.从服务器接受数据。    recv()函数的四个参数详解

    /*

     参数

     1> 客户端socket

     2> 接收内容缓冲区地址, 需要提前准备

     3> 接收内容缓存区长度

     4> 接收方式,0表示阻塞,必须等待服务器返回数据

     返回值

     如果成功,则返回读入的字节数,失败则返回SOCKET_ERROR

     

     */

    uint8_t buffer[1024];//把控件先准备好

    

    ssize_t  recvLen = recv(self.clientSocket, buffer,sizeof(buffer),0);

    NSLog(@"接受了 %ld 个字节",recvLen);

    

    //获取服务器返回的数据,从缓冲区中读取 recvLen 个字节!

    NSData * data = [NSDatadataWithBytes:bufferlength:recvLen];

    //转换成字符串

    NSString * str =  [[NSStringalloc]initWithData:dataencoding:NSUTF8StringEncoding];

    NSLog(@"----------- %@",str);

    return str;

}


//MARK: Socket聊天

//断开连接

-(void)disConnection{

    /**

     长连接:连上就一直聊!通常用于QQ,及时通讯,效率很高!(一对一!)

     短连接:通讯一次,马上断开,下次再次建立连接,效率低! (一对多!)

     */

    close(self.clientSocket);

}


以上代码大概建立链接Socket连接通过TCP协议传输数据。
步骤:
1.创建本地端的Socket

2.通过服务器的ip和端口连接到服务器

3.发送数据到服务器

4.从服务器接受数据。