即時通訊: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 客戶端器就介紹到這裏。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章