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