前奏:socket两套库,GCDAsyncSocket,GCDAsyncUdpSocket和AsyncSocket,AsyncUdpSocket。
前者支持ARC。什么TCP,UDP基础知识就不在这里啰嗦了。
*** pods导入方式:pod 'CocoaAsyncSocket' ***
iOS-Socket-UDP篇
一进入正题先上(AsyncUdpSocket)非ARC的版本1:客户端点对点
#import "AsyncUdpSocket.h" //导入头文件
AsyncUdpSocketDelegate //加入需要遵循的协议代理
// 收发套接字对象
AsyncUdpSocket * _sendSocket;//套接字发送对象
AsyncUdpSocket * _recvSocket;//套接字接收对象
收发套接字对象实例化
- (void)initSocket
{
_sendSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
// 绑定一个端口 发数据
[_sendSocket bindToPort:0x1234 error:nil];
_recvSocket = [[AsyncUdpSocket alloc] initWithDelegate:self];
[_recvSocket bindToPort:0x4321 error:nil];
// 0x1234端口做数据发送 0x4321端口接受数据
// 开始监听
[_recvSocket receiveWithTimeout:-1 tag:200];
// 将聊天内容展示在表格上 自己说的显示在左边 别人说的现在在右边 聊天内容越长Cell高度越高
}
指定目标端口向其发送数据
- (IBAction)sendMsg:(UIButton *)sender {
// 发送消息
// 发送消息对应的二进制 目标主机 目标主机对应的端口号 超时时间 消息的标签
// 消息必须发送到接受端绑定的端口之上才能收到数据
[_sendSocket sendData:[inputField.text dataUsingEncoding:NSUTF8StringEncoding] toHost:destIPField.text port:0x4321 withTimeout:60 tag:100];
}
AsyncUdpSocket相关代理方法
- (void)onUdpSocket:(AsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
NSLog(@"消息发送成功回调");
}
// 成功接受到数据回调
- (BOOL)onUdpSocket:(AsyncUdpSocket *)sock didReceiveData:(NSData *)data withTag:(long)tag fromHost:(NSString *)host port:(UInt16)port
{
// data发送内容的二进制 tag消息标签 host发送消息的主机IP port发送方对应的端口
NSString * content = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"收到目标IP%@端口%x说:%@",host,port,content);
// _recvSocket对象接受完数据后 必须继续监听 否则不能收到数据
// -1 表示一直等 直到有数据到来
[_recvSocket receiveWithTimeout:-1 tag:200];
return YES;
}
点对点基本操作就这么简单,实际通信开发中需要的也许远远不止如此了,这里纯抽出的是点对点点对点关键代码没有贴UI相关。操作效果截图如下:
二.ARC的版本1:客户端点对点
#define recvPort 2346 //接收socket端口
#define sendPort 6432 //发送socket端口
@import CocoaAsyncSocket;//导入头文件
#*** pods导入方式:pod 'CocoaAsyncSocket' ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理协议
GCDAsyncUdpSocket *_sendSocket;//发送socket
GCDAsyncUdpSocket *_recvSocket;//接收socket
@property (strong, nonatomic) UITextField *ipText;//发送目标的IP地址
- (void)viewDidLoad {
[super viewDidLoad];
[self initSocket];
_submitText=[[UITextField alloc] initWithFrame:CGRectMake(10, 70+64, 300, 30)];
_submitText.delegate=self;
_submitText.borderStyle = UITextBorderStyleRoundedRect;
_submitText.text = @"192.168.1.21";//这里是我测试的发送目标的IP地址
[self.view addSubview:_submitText];
}
-(void)initSocket
{
//收发socket实例化
_recvSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[_recvSocket bindToPort:recvPort error:nil];
//在这里接收的套接字需要开启一个接收消息的监听如下:
if (![_recvSocket beginReceiving:nil]) {
[_recvSocket close];
NSLog(@"Error starting server");
return;
}
_sendSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[_sendSocket bindToPort:sendPort error:nil];
}
-(void)buttonClicked:(UIButton*)button
{
if (button.tag==100){
[self connectUDPTest];
}else if(button.tag==101){
[self sendUDPTouched];
}
}
- (void)connectUDPTest{
}
- (void)sendUDPTouched
{
//向目标IP发送消息
NSString *sengStr = [NSString stringWithFormat:@"%@-发来消息",_ipText.text];
[_sendSocket sendData:[sengStr dataUsingEncoding:NSUTF8StringEncoding] toHost:_ipText.text port:recvPort withTimeout:-1 tag:1];
NSLog(@"localPort = %hu", [_sendSocket localPort]);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
NSLog(@"Message didReceiveData :%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}
操作效果截图如下:
二.ARC的版本2:客户端对服务器
1.新建两个工程一个客户端,一个模拟服务器。首先上服务器
#define PORT 8008 //服务器端口设置
@import CocoaAsyncSocket;//导入头文件
#*** pods导入方式:pod 'CocoaAsyncSocket' ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理协议
GCDAsyncUdpSocket *serverSocket;//服务器socket
2.服务器socket实例化 在PORT端口监听数据
serverSocket=[[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[serverSocket bindToPort:PORT error:nil];
if (![serverSocket beginReceiving:nil]) {
[serverSocket close];
NSLog(@"Error starting server");
return;
}
3.GCDAsyncUdpSocket相关代理方法
// 网络连接成功后 自动回调
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address
{
NSLog(@"已连接到用户:ip:%@",[[NSString alloc]initWithData:address encoding:NSUTF8StringEncoding]);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
NSString *datastr=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
// 将数据回写给发送数据的用户
NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
[sock sendData:[[NSString stringWithFormat:@"服务器收到客户端消息返回%@",datastr] dataUsingEncoding:NSUTF8StringEncoding] toAddress:address withTimeout:-1 tag:300];
}
4客户端相关
#define HOST @"192.168.1.108" //服务器地址
#define PORT 8008 //服务器端口
@import CocoaAsyncSocket;//导入头文件
#*** pods导入方式:pod 'CocoaAsyncSocket' ***#
GCDAsyncUdpSocketDelegate 需要遵循的代理协议
GCDAsyncUdpSocket *_udpSocket;//客户端socket
@property (strong, nonatomic) UITextField *ipText;//发送目标的IP地址
GCDAsyncUdpSocket.h
#define HOST @"192.168.1.108"
#define PORT 8008
#import
#import "GCDAsyncUdpSocket.h"
@interface UDPServerCommunicationViewController : UIViewController
{
GCDAsyncUdpSocket *_udpSocket;
}
@property (strong, nonatomic) UITextField *ipText;
@end
GCDAsyncUdpSocket.m
#import "UDPServerCommunicationViewController.h"
@interface UDPServerCommunicationViewController ()
@end
@implementation UDPServerCommunicationViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
if (!_udpSocket)
{
_udpSocket=nil;
}
_udpSocket = [[GCDAsyncUdpSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSArray *nameArray=[NSArray arrayWithObjects:@"连接",@"发送",nil];
for (int i=0;i<[nameArray count];i++) {
UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:[nameArray objectAtIndex:i] forState:UIControlStateNormal];
[button setFrame:CGRectMake(10+50*i, 30+64, 50, 30)];
button.tag=100+i;
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
_ipText=[[UITextField alloc] initWithFrame:CGRectMake(10, 70+64, 300, 30)];
_ipText.borderStyle = UITextBorderStyleRoundedRect;
_ipText.text = HOST;
[self.view addSubview:_ipText];
}
-(void)buttonClicked:(UIButton*)button
{
if (button.tag==100){
[self connectUDPTest];
}else if(button.tag==101){
[self sendUDPTouched];
}
}
-(void)connectUDPTest
{
NSError *error = nil;
if(![_udpSocket connectedHost]){
if (![_udpSocket connectToHost:HOST onPort:PORT error:&error]) {
if (error==nil){
NSLog(@"连接成功");
}else{
NSLog(@"连接失败:%@",error);
}
}else{
NSLog(@"已经连接");
}
}else{
NSLog(@"已经连接:%@",[_udpSocket connectedHost]);
}
}
//客户端发送本机IP地址给服务器
- (void)sendUDPTouched{
[_udpSocket sendData:[_ipText.text dataUsingEncoding:NSUTF8StringEncoding] withTimeout:-1 tag:1];
// [_udpSocket sendData:[_ipText.text dataUsingEncoding:NSUTF8StringEncoding] toHost:HOST port:PORT withTimeout:-1 tag:1];
NSLog(@"Udp Echo server started on port %hu", [_udpSocket localPort]);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didConnectToAddress:(NSData *)address
{
NSError *error = nil;
NSLog(@"Message didConnectToAddress: %@",[[NSString alloc]initWithData:address encoding:NSUTF8StringEncoding]);
[_udpSocket beginReceiving:&error];
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didNotConnect:(NSError *)error
{
NSLog(@"Message didNotConnect: %@",error);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didNotSendDataWithTag:(long)tag dueToError:(NSError *)error
{
NSLog(@"Message didNotSendDataWithTag: %@",error);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data fromAddress:(NSData *)address withFilterContext:(id)filterContext
{
NSLog(@"Message didReceiveData :%@", [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding]);
}
-(void)udpSocket:(GCDAsyncUdpSocket *)sock didSendDataWithTag:(long)tag
{
NSLog(@"Message 发送成功");
}
@end
操作效果截图如下:
iOS-Socket-TCP篇
TCP篇这里把服务器模拟和客户端点对点放在一起
图解方式为:
一.(GCDAsyncSocket)ARC的版本
1.服务器
#import
@import CocoaAsyncSocket;
@interface ServerViewController : UIViewController
{
GCDAsyncSocket *serverSocket;
UITableView *clientTableView;
NSMutableArray *socketArray;
}
@end
#import "ServerViewController.h"
@interface ServerViewController ()
@end
@implementation ServerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
clientTableView=[[UITableView alloc] initWithFrame:CGRectMake(0,100+64,Screen_Width,Screen_Height-164) style:UITableViewStylePlain];
clientTableView.delegate=self;
clientTableView.dataSource=self;
[self.view addSubview:clientTableView];
// 存储连接到服务器的socket的数组
socketArray =[[NSMutableArray alloc] initWithCapacity:0];
// 服务器socket实例化 在0x1234端口监听数据
serverSocket=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[serverSocket acceptOnPort:0x1234 error:nil];
}
// 有新的socket向服务器链接自动回调
-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
[socketArray addObject:newSocket];
[clientTableView reloadData];
// 如果下面的方法不写 只能接收一次socket链接
[newSocket readDataWithTimeout:-1 tag:100];
}
// 网络连接成功后 自动回调
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(@"已连接到用户:ip:%@",host);
}
// 接收到了数据 自动回调 sock客户端
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"收到%@发来的消息:%@",[sock connectedHost],message);
// 相当与向服务起索取 在线用户数据
// 将连上服务器的所有ip地址 组成一个字符串 将字符串回写到客户端
if ([message isEqualToString:@"GetClientList"]) {
NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
int i=0;
// 每一个客户端连接服务器成功后 socketArray保存客户端的套接字
// [newSocket connectedHost] 获取套接字对应的IP地址
for (GCDAsyncSocket *newSocket in socketArray) {
// 以字符串形式分割ip地址 192..,192...,
if (i!=0) {
[clientList appendFormat:@",%@",[newSocket connectedHost]];
}
else{
[clientList appendFormat:@"%@",[newSocket connectedHost]];
}
i++;
}
// 将服务端所有的ip连接成一个字符串对象
NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
// 将在线的所有用户 以字符串的形式一次性发给客户端
// 哪个客户端发起数据请求sock就表示谁
[sock writeData:newData withTimeout:-1 tag:300];
}
else{
// 将数据回写给发送数据的用户
NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
[sock writeData:data withTimeout:-1 tag:300];
}
// 继续读取socket数据
[sock readDataWithTimeout:-1 tag:200];
}
/*重连
实现代理方法
-(void)onSocketDidDisconnect:(GCDAsyncSocket *)sock
{
NSLog(@"sorry the connect is failure %ld",sock.userData);
if (sock.userData == SocketOfflineByServer) {
// 服务器掉线,重连
[self socketConnectHost];
}
else if (sock.userData == SocketOfflineByUser) {
// 如果由用户断开,不进行重连
return;
}
}*/
// 连接断开时 服务器自动回调
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock
{
[socketArray removeObject:sock];
[clientTableView reloadData];
}
// 向用户发出的消息 自动回调
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
NSLog(@"向用户%@发出消息",[sock connectedHost]);
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [socketArray count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellName=@"CellName";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
}
GCDAsyncSocket *socket=[socketArray objectAtIndex:indexPath.row];
// 根据连接服务端套接字socket对象 获取对应的客户端IP地址
cell.textLabel.text=[NSString stringWithFormat:@"用户:%@",[socket connectedHost]];
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
2.客户端
#import
@import CocoaAsyncSocket;
@interface ClientViewController : UIViewController
{
GCDAsyncSocket *serverSocket;//服务器socket
GCDAsyncSocket *listenSocket;//客户端自身socket(接收消息)
GCDAsyncSocket *clientSocket;//客户端发送socket(发送消息)
UITableView *clientTableView;
NSMutableArray *clientArray;
UITextField* messageTextField;
NSString *ipCopy;
}
#import "ServerViewController.h"
#import "ClientViewController.h"
@interface ServerViewController ()
@end
@implementation ServerViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
clientTableView=[[UITableView alloc] initWithFrame:CGRectMake(0,100+64,Screen_Width,Screen_Height-164) style:UITableViewStylePlain];
clientTableView.delegate=self;
clientTableView.dataSource=self;
[self.view addSubview:clientTableView];
socketArray =[[NSMutableArray alloc] initWithCapacity:0];
// 服务器socket实例化 在0x1234端口监听数据
serverSocket=[[GCDAsyncSocket alloc]initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
[serverSocket acceptOnPort:PORT error:nil];
}
// 有新的socket向服务器链接自动回调
-(void)socket:(GCDAsyncSocket *)sock didAcceptNewSocket:(GCDAsyncSocket *)newSocket
{
[socketArray addObject:newSocket];
[clientTableView reloadData];
// 如果下面的方法不写 只能接收一次socket链接
[newSocket readDataWithTimeout:-1 tag:100];
}
// 网络连接成功后 自动回调
-(void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(@"已连接到用户:ip:%@",host);
}
// 接收到了数据 自动回调 sock客户端
-(void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"收到%@发来的消息:%@",[sock connectedHost],message);
// 相当与向服务起索取 在线用户数据
// 将连上服务器的所有ip地址 组成一个字符串 将字符串回写到客户端
if ([message isEqualToString:@"GetClientList"]) {
NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
int i=0;
// 每一个客户端连接服务器成功后 socketArray保存客户端的套接字
// [newSocket connectedHost] 获取套接字对应的IP地址
for (GCDAsyncSocket *newSocket in socketArray) {
// 以字符串形式分割ip地址 192..,192...,
if (i!=0) {
[clientList appendFormat:@",%@",[newSocket connectedHost]];
}
else{
[clientList appendFormat:@"%@",[newSocket connectedHost]];
}
i++;
}
// 将服务端所有的ip连接成一个字符串对象
NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
// 将在线的所有用户 以字符串的形式一次性发给客户端
// 哪个客户端发起数据请求sock就表示谁
[sock writeData:newData withTimeout:-1 tag:300];
}
else{
// 将数据回写给发送数据的用户
NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
[sock writeData:data withTimeout:-1 tag:300];
}
// 继续读取socket数据
[sock readDataWithTimeout:-1 tag:200];
}
/*重连
实现代理方法
-(void)onSocketDidDisconnect:(GCDAsyncSocket *)sock
{
NSLog(@"sorry the connect is failure %ld",sock.userData);
if (sock.userData == SocketOfflineByServer) {
// 服务器掉线,重连
[self socketConnectHost];
}
else if (sock.userData == SocketOfflineByUser) {
// 如果由用户断开,不进行重连
return;
}
}*/
// 连接断开时 服务器自动回调
-(void)socketDidDisconnect:(GCDAsyncSocket *)sock
{
[socketArray removeObject:sock];
[clientTableView reloadData];
}
// 向用户发出的消息 自动回调
-(void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
{
NSLog(@"向用户%@发出消息",[sock connectedHost]);
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [socketArray count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellName=@"CellName";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
}
GCDAsyncSocket *socket=[socketArray objectAtIndex:indexPath.row];
// 根据连接服务端套接字socket对象 获取对应的客户端IP地址
cell.textLabel.text=[NSString stringWithFormat:@"用户:%@",[socket connectedHost]];
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
操作效果截图如下:
二.(AsyncSocket)非ARC的版本
(AsyncSocket)非ARC的版本1.服务器
#import
#import "AsyncSocket.h"
@interface ServerViewController : UIViewController
{
AsyncSocket *serverSocket;
UITableView *clientTableView;
NSMutableArray *clientArray;
NSMutableArray *socketArray;
}
@end
#import "ServerViewController.h"
@interface ServerViewController ()
@end
@implementation ServerViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
clientTableView=[[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain];
clientTableView.delegate=self;
clientTableView.dataSource=self;
[self.view addSubview:clientTableView];
clientArray=[[NSMutableArray alloc] initWithCapacity:0];
socketArray =[[NSMutableArray alloc] initWithCapacity:0];
// 服务器socket实例化 在0x1234端口监听数据
serverSocket=[[AsyncSocket alloc] init];
serverSocket.delegate=self;
[serverSocket acceptOnPort:0x1234 error:nil];
}
// 有新的socket向服务器链接自动回调
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
[socketArray addObject:newSocket];
[clientTableView reloadData];
// 如果下面的方法不写 只能接收一次socket链接
[newSocket readDataWithTimeout:-1 tag:100];
}
// 网络连接成功后 自动回调
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
NSLog(@"已连接到用户:ip:%@",host);
}
// 接收到了数据 自动回调 sock客户端
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
NSString *message=[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"收到%@发来的消息:%@",[sock connectedHost],message);
// 相当与向服务起索取 在线用户数据
// 将连上服务器的所有ip地址 组成一个字符串 将字符串回写到客户端
if ([message isEqualToString:@"GetClientList"]) {
NSMutableString *clientList=[[NSMutableString alloc] initWithCapacity:0];
int i=0;
for (AsyncSocket *newSocket in socketArray) {
// 以字符串形式分割ip地址 192..,192...,
if (i!=0) {
[clientList appendFormat:@",%@",[newSocket connectedHost]];
}
else{
[clientList appendFormat:@"%@",[newSocket connectedHost]];
}
i++;
}
// 将服务端所有的ip连接成一个字符串对象
NSData *newData=[clientList dataUsingEncoding:NSUTF8StringEncoding];
// 将在线的所有用户 以字符串的形式一次性发给客户端
// 哪个客户端发起数据请求sock就表示谁
[sock writeData:newData withTimeout:-1 tag:300];
}
else{
// 将数据回写给发送数据的用户
NSLog(@"debug >>>> %@",[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
[sock writeData:data withTimeout:-1 tag:300];
}
// 继续读取socket数据
[sock readDataWithTimeout:-1 tag:200];
}
// 连接断开时 服务器自动回调
-(void)onSocketDidDisconnect:(AsyncSocket *)sock
{
[socketArray removeObject:sock];
[clientTableView reloadData];
}
// 向用户发出的消息 自动回调
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
NSLog(@"向用户%@发出消息",[sock connectedHost]);
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [socketArray count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellName=@"CellName";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
}
AsyncSocket *socket=[socketArray objectAtIndex:indexPath.row];
// 根据连接服务端套接字socket对象 获取对应的客户端IP地址
cell.textLabel.text=[NSString stringWithFormat:@"用户:%@",[socket connectedHost]];
return cell;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
1.(AsyncSocket)非ARC的版本客户端
#import
#import "AsyncSocket.h"
@interface ClientViewController : UIViewController
{
AsyncSocket *serverSocket;
AsyncSocket *listenSocket;
AsyncSocket *clientSocket;
UITableView *clientTableView;
NSMutableArray *clientArray;
UITextField* messageTextField;
}
@end
#import "ClientViewController.h"
@interface ClientViewController ()
@end
@implementation ClientViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
// A(监听收对方发来的数据) server(连接服务器) B(对方)
// 服务器socket
serverSocket =[[AsyncSocket alloc] init];
serverSocket.delegate=self;
// 本身
listenSocket=[[AsyncSocket alloc] initWithDelegate:self];
[listenSocket acceptOnPort:0x1235 error:nil];
// 点对点通讯时 对方的socket
clientSocket=[[AsyncSocket alloc] initWithDelegate:self];
NSArray *nameArray=[NSArray arrayWithObjects:@"连接",@"断开",@"在线用户", nil];
for (int i=0;i<[nameArray count];i++) {
UIButton *button=[UIButton buttonWithType:UIButtonTypeRoundedRect];
[button setTitle:[nameArray objectAtIndex:i] forState:UIControlStateNormal];
[button setFrame:CGRectMake(10+50*i, 30, 50, 30)];
button.tag=100+i;
[button addTarget:self action:@selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:button];
}
clientTableView=[[UITableView alloc] initWithFrame:CGRectMake(0, 80, 320,380) style:UITableViewStylePlain];
clientTableView.delegate=self;
clientTableView.dataSource=self;
[self.view addSubview:clientTableView];
clientArray=[[NSMutableArray alloc] initWithCapacity:0];
messageTextField=[[UITextField alloc] initWithFrame:CGRectMake(10, 50, 300, 30)];
messageTextField.delegate=self;
messageTextField.borderStyle = UITextBorderStyleRoundedRect;
[self.view addSubview:messageTextField];
}
-(BOOL)textFieldShouldReturn:(UITextField *)textField
{
[textField resignFirstResponder];
return YES;
}
// 发数据 结束编辑 就是键盘隐藏的时候 自动调用
-(void)textFieldDidEndEditing:(UITextField *)textField
{
if (clientSocket && [clientSocket isConnected]) {
NSData *data=[textField.text dataUsingEncoding:NSUTF8StringEncoding];
// 向socket写数据
[clientSocket writeData:data withTimeout:-1 tag:100];
//[serverSocket writeData:data withTimeout:-1 tag:200];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [clientArray count];
}
-(UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellName=@"CellName";
UITableViewCell *cell=[tableView dequeueReusableCellWithIdentifier:cellName];
if (cell==nil) {
cell=[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:cellName];
}
cell.textLabel.text=[clientArray objectAtIndex:indexPath.row];
return cell;
}
-(void)buttonClicked:(UIButton*)button
{
if (button.tag==100)
{
// 连接服务器
if (![serverSocket isConnected])
{
//[serverSocket disconnect];
// 指定 ip 指定端口 发起一个TCP连接
[serverSocket connectToHost:@"192.168.1.108" onPort:0x1234 error:nil];
// 向服务器写数据
// [serverSocket writeData:<#(NSData *)#> withTimeout:<#(NSTimeInterval)#> tag:<#(long)#>]
}
else
{
NSLog(@"已经和服务器连接");
}
}
// 断开与服务器连接
else if(button.tag==101)
{
// 断开TCP连接
[serverSocket disconnect];
}
// 获取在线用户
else if(button.tag==102)
{
NSString *message=@"GetClientList";
NSData *data=[message dataUsingEncoding:NSUTF8StringEncoding];
// 向服务器获取在线用户信息
// 向服务端写字符串 GetClientList
[serverSocket writeData:data withTimeout:-1 tag:400];
}
}
// 接收到了一个新的socket连接 自动回调
// 接收到了新的连接 那么释放老的连接
-(void)onSocket:(AsyncSocket *)sock didAcceptNewSocket:(AsyncSocket *)newSocket
{
if (sock==listenSocket) {
NSLog(@"收到用户%@的连接请求",[newSocket connectedHost]);
if (clientSocket && [clientSocket isConnected]) {
[clientSocket disconnect];
[clientSocket release];
}
// 保存发起连接的客户端socket
clientSocket=[newSocket retain];
}
}
// 写数据成功 自动回调
-(void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag
{
// 获取用户列表
if (sock==serverSocket) {
NSLog(@"向服务器%@发送消息成功",[sock connectedHost]);
}
// 客户端与客户端通讯
else if(sock==clientSocket){
NSLog(@"向客户%@发送消息成功",[sock connectedHost]);
}
// 继续监听
[sock readDataWithTimeout:-1 tag:500];
}
// 成功连接后自动回调
-(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
[sock readDataWithTimeout:-1 tag:200];
NSLog(@"已经连接到服务器:%@",host);
}
// 客户端接收到了数据
-(void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
// 如果时服务器给的消息 必然是在线用户的消息
if (sock==serverSocket) {
NSString *message=[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
// 将字符串以","分割 到数组中
// 分割出IP地址
NSArray *array=[message componentsSeparatedByString:@","];
[clientArray removeAllObjects];
[clientArray addObjectsFromArray:array];
[clientTableView reloadData];
NSLog(@"在线用户列表:%@",clientArray);
}
// 点对点通讯
else{
NSString *message=[[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] autorelease];
NSLog(@"收到客户%@的消息:%@",[sock connectedHost],message);
}
// 继续监听
[sock readDataWithTimeout:-1 tag:100];
}
// 点击一行时 向该行对应的ip地址发起连接
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *ip=[clientArray objectAtIndex:indexPath.row];
if (clientSocket ) {
// 如果之前连接上了一个好友 那么先断开
if ([clientSocket isConnected]) {
[clientSocket disconnect];
}
// 向TableViewCell上指定ip发起TCP连接
[clientSocket connectToHost:ip onPort:0x1235 error:nil];
// clientSocket已经指向了好友 如果需要和好友发消息
NSData *data=[@"didSelectRowAtIndexPath" dataUsingEncoding:NSUTF8StringEncoding];
[clientSocket writeData:data withTimeout:-1 tag:100];
}
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end
操作效果截图如下:和ARC一样
1.GitHub地址:GCD ARC版本 UDP&TCP demo
https://github.com/Yjunjie/Socket-GCD-TCP-UDPdemo
2.GitHub地址:非ARC版本 UDP&TCP demo
https://github.com/Yjunjie/Socket-TCP-UDPdemo