网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。它不是一种协议,而是一种通信机制。Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。
根据连接启动的方式以及本地套接字要连接的目标,套接字之间的连接过程可以分为三个步骤:服务器监听,客户端请求,连接确认。
(1)服务器监听:是服务器端套接字并不定位具体的客户端套接字,而是处于等待连接的状态,实时监控网络状态。
(2)客户端请求:是指由客户端的套接字提出连接请求,要连接的目标是服务器端的套接字。为此,客户端的套接字必须首先描述它要连接的服务器的套接字,指出服务器端套接字的地址和端口号,然后就向服务器端套接字提出连接请求。
(3)连接确认:是指当服务器端套接字监听到或者说接收到客户端套接字的连接请求,它就响应客户端套接字的请求,建立一个新的线程,把服务器端套接字的描述发给客户端,一旦客户端确认了此描述,连接就建立好了。而服务器端套接字继续处于监听状态,继续接收其他客户端套接字的连接请求。
要想使用socket套接字完成两个ip的通信,在iOS中需要使用第三方库 AsyncSocket 还需要导入CFNetWork.frameWork
下面介绍第一种~~~socket在tcp的基础上通信。。 直接上代码 。。有详细注释。
@implementation ViewController {
/** 读取ip的文本输入框 */
IBOutlet UITextField *_ipField;
/** 发送消息的文本输入框 */
IBOutlet UITextField *_sendField;
/** 显示 */
IBOutlet UITextView *_textView;
/** 保存通讯连接的数组 */
NSMutableArray *_socketArray;
/** 客户端发送使用 */
AsyncSocket *_clientSocket;
/** 服务端接收使用 */
AsyncSocket *_serverSocket;
// 1. 当前想要发送数据的是客户端,接受数据的是服务端。
// 2. 服务端接收到数据,开始显示。端口号要保持一致才能进行通信。 当前需要初始化客户端和服务端。
}
- (void)viewDidLoad {
[super viewDidLoad];
/**
* Socket: 进程通信机制,是建立网络连接时使用的,在连接成功时,应用程序两端都会产生一个Socket实例
操作这个实例,用套接字的相关函数来完成通信过程。
*/
// 实例化这个连接数组,是用来保存每一次的连接~的~~~~
_socketArray = [[NSMutableArray alloc] init];
// 实例化’客户端‘的套接字,同时设置代理,我们需要通过其代理方法来做很多事情。!
_clientSocket = [[AsyncSocket alloc] initWithDelegate:self];
// 实例化‘服务端’的套接字,同时设置代理
_serverSocket = [[AsyncSocket alloc] initWithDelegate:self];
// 这是(服务端) 在对应端口上开始监听有没有客户端的连接吧!!
// 不建议用5000以前的端口,以防与计算机中已有的端口冲突
[_serverSocket acceptOnPort:9527 error:nil];
// 同一个设备 网卡的不同进程 都是同一个ip,通过这个端口 来区分
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self sendText];
}
- (void)sendText {
// 判断客户端是否已经和服务器 连接了,如果没有连接,发个毛线
if (_clientSocket.isConnected == NO) {
// 连接服务端,客户端要连接的端口必须和服务器监听的端口保持一致。要不然连不上啊~~~~~
/**
* 1. 参数1 网络IP (主机)
2. 端口号来区分到底是哪个进程
3. 超时时间,过了这个时间,服务器不鸟你了~
*/
[_clientSocket connectToHost:_ipField.text onPort:9527 withTimeout:30 error:nil];
}
// 发送数据
[_clientSocket writeData:[_sendField.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:30 tag:100];
// 已经连接上了,服务端你就随时的进行监听吧~~~~~~
}
#pragma mark - AsyncSocket '代理方法'
/**
* 将要建立连接,
*/
- (BOOL)onSocketWillConnect:(AsyncSocket *)sock {
NSLog(@"将要建立连接,是否允许");
return YES;
}
/**
* 请求也连接成功,数据服务器也收到了,等着接受服务器的返回信息吧~~~~
*/
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {
[sock readDataWithTimeout:-1 tag:100];
NSLog(@"已经连接到了服务器 ip:%@,端口号:%d",host,port);
}
/**
* 服务器收到了新的客户端的请求(监听) 服务器一直在监听
*/
- (void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket {
// 数组 保存这个连接吧,我下次还要做事情
[_socketArray addObject:newSocket];
// 保存完了 还有事情 继续监听 (-1) 就是 一直在监听
[newSocket readDataWithTimeout:-1 tag:100];
}
/**
* 服务器真的就收到了客户端发来的数据,不是假的。
*/
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSLog(@"我已经读取到了TCP传输过来的数据");
// 我得将它转化为可见的字符串啊~~
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// 服务器将接收到了的数据 转化为可见的字符串 还有将主机名 显示到 界面上。~~
_textView.text = [NSString stringWithFormat:@"%@%@:%@\n",_textView.text,[sock connectedHost],dataString];
// [sock connectedHost] 这是用来返回ip地址滴
// 读取完了别闲着~~~~继续做数据的监听
[sock readDataWithTimeout:-1 tag:100];
}
/**
* 将要断开连接,看看有没有错误
*/
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err {
NSLog(@"将要断开连接");
if (err) {
NSLog(@"error:%@",err);
}
}
/**
* 已经断开连接了~~~断开连接
*/
- (void)onSocketDidDisconnect:(AsyncSocket *)sock {
NSLog(@"已经断开了和%@的连接",sock);
}
/**
* 已经写入数据到服务器了~~~
*
*/
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag {
NSLog(@"已经写入数据到服务器");
}
第二种,socket在UDP基础上实现通信。这个就比较简单一点,因为不要建立连接。直接传数据。
@implementation ViewController {
/** 读取ip的文本输入框 */
IBOutlet UITextField *_ipField;
/** 发送消息的文本输入框 */
IBOutlet UITextField *_sendField;
/** 显示 */
IBOutlet UITextView *_textView;
/** 客户端发送使用 */
AsyncUdpSocket *_clientSocket;
/** 服务端接收使用 */
AsyncUdpSocket *_serverSocket;
}
- (void)viewDidLoad {
[super viewDidLoad];
// 实例化客户端套接字
_clientSocket = [[AsyncUdpSocket alloc] init];
// 绑定端口
[_clientSocket bindToPort:8764 error:nil];
// 服务端,启动异步服务,绑定端口,等待数据的接收
_serverSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
// 你的端口,别人向你发送数据,需要通过这个端口号!
[_serverSocket bindToPort:8765 error:nil];
[_serverSocket receiveWithTimeout:-1 tag:101];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
[self sendText];
}
- (void)sendText {
// 客户端发送数据~~le ~ UDP的数据 无需建立连接 直接开发~~~
[_clientSocket sendData:[_sendField.text dataUsingEncoding:NSUTF8StringEncoding] toHost:[_ipField text] port:8764 withTimeout:30 tag:101];
_textView.text = [NSString stringWithFormat:@"%@我说:%@\n",_textView.text,_sendField.text];
// 将发送文本框内容置空吧~~~
_sendField.text = @"";
}
#pragma mark - AsyncUdpSocket ‘代理方法’
/**
* 接收到了数据
*/
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock
didReceiveData:(NSData *)data
withTag:(long)tag
fromHost:(NSString *)host
port:(UInt16)port {
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
_textView.text = [NSString stringWithFormat:@"%@%@:%@\n",_textView.text,host,dataString];
// 接受完数据之后,socket继续接收数据 根本停不下来~
[sock receiveWithTimeout:-1 tag:101];
NSLog(@"从ip:%@ 端口%d 收到数据",host,port);
// 允许接收
return YES;
}
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag {
NSLog(@"发送了数据调用");
}