首先我们在用C语言建立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传输协议效率会相对低些。
第一次握手:建立连接时,客户端发送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地址,远地进程的协议端口。
应用层通过传输层进行数据通信时,服务器会遇到同时为多个应用程序进程提供并发服务的问题。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了套接字(Socket)接口。应用层可以和传输层通过Socket接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
建立Socket连接进行TCP协议传输:
建立 Socket 连接至少需要一对套接字,其中一个运行于客户端,称为ClientSocket, 客服端的ip+端口。另一个运行于服务器端,称为ServerSocket,服务器端的ip+端口。
套接字之间的连接过程分为三个步骤:服务器监听,客户端请求,连接确认。
服务器监听:服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态,等待客户端的连接请求。
客户端请求:指客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
连接确认:当服务器端套接字监听到或者说接收到客户端套接字的连接请求时,就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,双方就正式建立连接。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
在进行TCP协议传输数据的前提是建立Socket连接,创建Socket连接时,可以指定使用的传输层协议,Socket可以支持不同的传输层协议(TCP、UDP、自定义协议等)。
常用的Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_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, (conststruct sockaddr *)&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:buffer length: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.从服务器接受数据。