上篇說到即時通訊編程選擇哪種方式,筆者用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 客戶端器就介紹到這裏。