上篇说到即时通讯编程选择哪种方式,笔者用socket编写了一个仿QQ聊天的通讯应用,顺便就用这个应用介绍一个socket的使用吧
socket与XMPP差别之处就是socket的客户端与服务器端都需要自己来搭建。XMPP 有现成的服务器跟客户端,编写测试起来就比较轻松,所以,XMPP也就成了即时通讯编程的主流了。不过介于个人兴趣,socket还是值得探究的。
好了,就说这么多。下面就是笔者的socket应用。
首先先介绍一下客户端这是客户端登陆界面
接下来,进行注册操作
点击注册--(向服务器端发送注册消息---下面代码会介绍)
客户端注册的用户信息保存在服务器端的数据库
输入刚刚注册的用户名,密码登陆
登陆页面代码 记得先导入 AsyncSocket 框架
#import <UIKit/UIKit.h>
#import "AsyncSocket.h"
typedef void(^isSuccessBlock)(BOOL);
@interface ViewController : UIViewController
{
__weak IBOutlet UIView *_contBgView; //登陆视图
__weak IBOutlet UITextField *_usernameField;
__weak IBOutlet UITextField *_pwdField;
__weak IBOutlet UITableView *_tableView;
__weak IBOutlet UIView *_sendView;
//信息文本
__weak IBOutlet UITextField *_message;
}
@property(nonatomic,strong)AsyncSocket *socket; //客户端通信对象
@property(nonatomic,copy)isSuccessBlock block; //登陆成功验证Block
//发送消息
- (IBAction)sendMessage:(UIButton *)sender;
//验证用户
- (IBAction)contentAction:(UIButton *)sender;
//注册用户
- (IBAction)registerAction:(UIButton *)sender;
@end
#import "ViewController.h"
#import "MyCell.h"
#import "registerViewController.h"
@interface ViewController ()<UITableViewDataSource,UITableViewDelegate,AsyncSocketDelegate>//设置通信代理
{
NSMutableArray *dataArr; //存放本客户端及其它客户端消息
NSString *username; //验证通过后的用户名
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.title=@"Cheep";
_contBgView.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:@"red.jpg"]];
dataArr=[NSMutableArray array];
_tableView.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:@"chat_bg_default.jpg"]];
_tableView.separatorStyle=UITableViewCellSeparatorStyleNone;
_tableView.rowHeight=64;
_sendView.backgroundColor=[UIColor colorWithPatternImage:[UIImage imageNamed:@"chat_bottom_textfield.png"]];
//ios socket第三方框架 AsyncSocket使用简介,连接,心跳,断线,数据发送与接收
/*
1. socket 连接
2. socket 断开连接与重连
3. socket 发送与接收数据
4. 简单使用说明
*/
if (_socket == nil) {
_socket=[[AsyncSocket alloc] initWithDelegate:self];
//设定好的服务器地址跟端口
[_socket connectToHost:@"192.168.7.19" onPort:6225 error:nil];
}
}
//验证用户
- (IBAction)contentAction:(UIButton *)sender {
/* key : value
doWhat: commit(验证标记)
username: _usernameField.text
...
*/
//将字符串转成字典样式包装发送
NSString *dataStr= [NSString stringWithFormat:@"{\"doWhat\":\"commit\",\"username\":\"%@\",\"password\":\"%@\"}",_usernameField.text,_pwdField.text];
NSData *data=[dataStr dataUsingEncoding:NSUTF8StringEncoding];
/*
data 向服务器发送的数据
-1 请求超时时间 -1为一直等待服务器回复
tag 是为了在回调方法中匹配发起调用的方法的,不会加在传输数据中
*/
[_socket writeData:data withTimeout:-1 tag:0];
//监听请求消息
[_socket readDataWithTimeout:-1 tag:0];
//服务器返回验证消息
_block=^(BOOL result)
{
if (!result) {
_usernameField.text=@"";
_pwdField.text=@"";
}else
{
//成功----记录下用户名
username=_usernameField.text;
_contBgView.hidden=YES; //登陆页面隐藏
//消息页面显示
_tableView.hidden=NO;
_sendView.hidden=NO;
}
};
}
//注册用户
- (IBAction)registerAction:(UIButton *)sender {
//注册页面控制器
registerViewController *Ctrl=[[registerViewController alloc] init];
[self presentViewController:Ctrl animated:YES completion:^{
;
}];
//block回调(拿到注册页面,用户名,密码)
Ctrl.myBlock=^(NSString *textNum,NSString *textPassWord)
{
// resiger - 注册标记
NSString *dataStr= [NSString stringWithFormat:@"{\"doWhat\":\"resiger\",\"username\":\"%@\",\"password\":\"%@\"}",textNum,textPassWord];
NSData *data=[dataStr dataUsingEncoding:NSUTF8StringEncoding];
//向服务器发送注册请求,并监听请求信息
[_socket writeData:data withTimeout:-1 tag:0];
[_socket readDataWithTimeout:-1 tag:0];
};
}
//向服务器发送消息
- (IBAction)sendMessage:(UIButton *)sender {
if(_message.text.length >0)
{
//显示在本客户端UI的消息
NSDictionary *dic=[NSDictionary dictionary];
dic=@{
@"socket":@"client",
@"username":username,
@"message":_message.text
};
[dataArr addObject:dic];
//插入数据到最后一个单元格
NSIndexPath *index=[NSIndexPath indexPathForRow:dataArr.count-1 inSection:0];
[_tableView insertRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationFade];
//滚动到最后一个单元格
[_tableView scrollToRowAtIndexPath:index atScrollPosition:UITableViewScrollPositionBottom animated:YES];
/*
(内容包括 :内容标记,本客户端用户名,是否是客户端,发送的内容)当然自己可以定制,只要在服务器端也做相应的接收就可以
*/
NSString *message= [NSString stringWithFormat:@"{\"doWhat\":\"message\",\"username\":\"%@\",\"socket\":\"client\",\"message\":\"%@\"}",username,_message.text];
NSData *data=[message dataUsingEncoding:NSUTF8StringEncoding];
//向服务器发送聊天消息
[_socket writeData:data withTimeout:-1 tag:0];
[_socket readDataWithTimeout:-1 tag:0];
_message.text=@"";
}
}
#pragma mark- UITableView dataSource delegate
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return dataArr.count;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *intenty = @"UITableViewCell";
MyCell*cell = [tableView dequeueReusableCellWithIdentifier:intenty];
if (cell == nil) {
cell=[[[NSBundle mainBundle] loadNibNamed:@"MyCell" owner:self options:nil] lastObject];
}
NSDictionary *message =dataArr[indexPath.row];
//将model交给视图去显示
cell.message=message;
return cell;
}
#pragma mark - AsyncSocketDelegate
//1.客户端连接服务器调用的协议方法
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
//监听服务端socket消息
[sock readDataWithTimeout:-1 tag:0];
}
//2.接受服务器发送过来的数据(读取数据)
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
//服务器处理客户端发送的消息,相应的返回客户端能识别的消息(比如,上面客户端向服务器发送字典消息,服务器也返回相应的字典消息)
NSDictionary *dic=[NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
// NSLog(@"%@",dic);
NSString *dowhat=[dic objectForKey:@"doWhat"]; //内容标记
NSString *type=[dic objectForKey:@"type"]; //处理结果
if ([dowhat isEqualToString:@"commit"]) //登陆验证返回消息
{
if ([type integerValue]==1) {
NSLog(@"验证成功");
_block(YES);
}else
{
NSLog(@"验证失败");
}
}else if ([dowhat isEqualToString:@"resiger"]) //注册返回消息
{
if ([type integerValue]==2) {
NSLog(@"注册成功");
}else
{
}
}else if([dowhat isEqualToString:@"message"]) //聊天消息返回的消息
{
//拿到其他客户端发送的消息,
NSString *message=[dic objectForKey:@"message"];
NSString *fromusername=[dic objectForKey:@"username"];
// NSString *socket=[dic objectForKey:@"socket"];
dic=@{
@"socket":@"server",
@"username":fromusername,
@"message":message
};
[dataArr addObject:dic];
//UI操作放在主线程上
dispatch_async(dispatch_get_main_queue(), ^{
//插入数据到最后一个单元格
NSIndexPath *index=[NSIndexPath indexPathForRow:dataArr.count-1 inSection:0];
[_tableView insertRowsAtIndexPaths:@[index] withRowAnimation:UITableViewRowAnimationFade];
//滚动到最后一个单元格
[_tableView scrollToRowAtIndexPath:index atScrollPosition:UITableViewScrollPositionBottom animated:YES];
});
}
//继续监听服务器发来的消息
[sock readDataWithTimeout:-1 tag:0];
}
@end
注册页面在视图返回登陆页面的时候记得使用Block回调即可
//调用--先判断
if (self.myBlock != nil) {
self.myBlock(_fieldNum.text,_fieldPassWord.text);
}
//布局子视图 需判断消息是否为自己发送
if ([what isEqualToString:@"client"]) {
_iconImage.frame = CGRectMake(Kw - 50, 10, 40, 40);
_bgImage.frame = CGRectMake(Kw-50-(size.width + 30)-10, 10, size.width + 30, size.height + 30);
_contText.frame = CGRectMake(Kw-50-size.width-30, 20, size.width, size.height);
_usernameLabel.frame=CGRectMake(Kw-70, 1, 100, 17);
}else if([what isEqualToString:@"server"])
{
_iconImage.frame = CGRectMake(10, 10, 40, 40);
_bgImage.frame = CGRectMake(60, 10, size.width + 30, size.height + 30);
_contText.frame = CGRectMake(80, 20, size.width, size.height);
_usernameLabel.frame=CGRectMake(52, 1, 100, 17);
}
单元格布局根据字典内的消息的出处,判断该怎么布局。
socket 客户端器就介绍到这里。