聊天功能了解

作者: Kevin_wzx | 来源:发表于2017-04-24 10:21 被阅读70次

即时通讯

屏幕快照 2017-04-24 上午10.43.21.png

1.XMPP(http://xmpp.org

1.概念

XMPP是一种基于标准通用标记语言的子集XML的协议,它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能

补充:


屏幕快照 2017-04-24 上午11.09.01.png
  • XMPP是一个基于个Socket通过的网络协议,目的是为了保存长连接,以实现即时通讯功能
  • XMPP的客户端是使用一个XMPPFramework框架实现
  • XMPP的服务器是使用Openfire,一个开源的服务器
  • 客户端获取到服务器发送过来的好友消息,客户端需要对XML进行解析,使用的解析框架的KissXML框架,而不是NSXMLParser/GDataXML
  • XMPP是一个即时通讯的协议,它规范了用于即时通信在网络上数据传输格式的,比如登录,获取好友列表等等的格式。XMPP在网络传输的数据是XML格式。比如登录:把用户名和密码放在xml的标签中,传输到服务器
屏幕快照 2017-04-24 上午11.11.29.png

2.XMPP的使用

讲解以下内容:
1.导入XMPP框架
2.登录 & 注销
3.注册
4.用户信息
5.好友
6.消息
7.文件传送(图片,音频)

1.导入XMPP框架

1.1 下载XMPPFramework框架
GitHub: XMPPFramework

1.2导入依赖框架

屏幕快照 2017-04-24 上午9.50.19.png
1.3导入一下文件夹 屏幕快照 2017-04-24 上午9.52.17.png

**1.4导入XMPP扩展框架 **

屏幕快照 2017-04-24 上午9.52.48.png

2.登录&注销

2.1 实现用户登录的步骤如下:

1. 实例化XMPPStream并设置代理,同时添加代理到工作队列      

2. 使用JID连接至服务器,默认端口为5222,JID字符串中需要包含服务器的域名     

3. 在完成连接的代理方法中验证用户密码,连接完成后XMPPStream的isConnect属性为YES     

4. 在验证代理方法中判断用户是否登录成功        

5. 上线或者下线成功后,向服务器发送Presence数据,以更新用户在服务器的状态

2.2 各部分的实现代码如下:

  • 初始化 XMPPStream 并设置代理:
-(void)setupXMPPStream{

   _xmppStream = [[XMPPStream alloc] init];

   // 设置代理
   [_xmppStream addDelegate:self delegateQueue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
}
  • 连接到服务器
-(void)connectToHost{
   NSLog(@"开始连接到服务器");
   if (!_xmppStream) {
       [self setupXMPPStream];
   }

   // 设置登录用户JID
   //resource 标识用户登录的客户端 iphone android

   XMPPJID *myJID = [XMPPJID jidWithUser:@"aaa" domain:@"bourne-mbp.local" resource:@"iphone" ];
   _xmppStream.myJID = myJID;

   // 设置服务器域名
   _xmppStream.hostName = @"bourne-mbp.local";//不仅可以是域名,还可是IP地址

   // 设置端口 如果服务器端口是5222,可以省略
   _xmppStream.hostPort = 5222;

   // 连接
   NSError *err = nil;
   if(![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&err]){
       NSLog(@"%@",err);
   }

}
  • 连接成功后发送密码验证
-(void)sendPwdToHost{
   NSLog(@"再发送密码授权");
   NSError *err = nil;
   [_xmppStream authenticateWithPassword:@"123456" error:&err];
   if (err) {
       NSLog(@"%@",err);
   }
}
  • 授权成功后,发送 在线 消息
#pragma mark  授权成功后,发送"在线" 消息
-(void)sendOnlineToHost{

   NSLog(@"发送 在线 消息");
   XMPPPresence *presence = [XMPPPresence presence];
   NSLog(@"%@",presence);

   [_xmppStream sendElement:presence];
}

2.3 需要实现的几个代理方法

#pragma mark 与主机连接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender{
    NSLog(@"与主机连接成功");

    // 主机连接成功后,发送密码进行授权
    [self sendPwdToHost];
}

#pragma mark  与主机断开连接
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error{
    // 如果有错误,代表连接失败
    NSLog(@"与主机断开连接 %@",error);
}

#pragma mark 授权成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender{
    NSLog(@"授权成功");

    [self sendOnlineToHost];
}

#pragma mark 授权失败
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error{
    NSLog(@"授权失败 %@",error);
}

2.4 注销登录

  • 发送离线信息
  • 断开连接
-(void)logout{
    // 1." 发送 `离线` 消息"
    XMPPPresence *offline = [XMPPPresence presenceWithType:@"unavailable"];
    [_xmppStream sendElement:offline];

    // 2. 与服务器断开连接
    [_xmppStream disconnect];
}

3.注册

  • 与登录一样,首先发送帐号建立连接
  • 连接成功后,发送注册的密码
  • 注册成功后,框架会通知代理

实现以下代理方法

- (void)xmppStreamDidRegister:(XMPPStream *)sender {
    NSLog(@"注册成功");

    if (_resultBlock) {
        _resultBlock(BWXMPPLoginResultSuccessed);
    }
}

- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
    if (_resultBlock) {
        _resultBlock(BWXMPPLoginResultFailure);
    }
}

4 .用户信息

XMPP是面向模块的,每一个大的动能都属于某一个模块,需要使用时,就在头文件中将其暴露出来(原本是被注释了的)

1.工作原理:

添加用户信息模块之后,XMPPFramework框架会自动从服务器获取用户信息,并使用CoreData保存到本地的数据库中,使用XMPPvCardTempModule可以访问数据

2.在XMPPFramework.h中将以下的头文件前面的注释去掉:

// 电子名片模块
#import "XMPPvCardTempModule.h"
#import "XMPPvCardCoreDataStorage.h"

// 头像模块
#import "XMPPvCardAvatarModule.h"

3.初始化模块

//添加电子名片模块
_vCardStorage = [XMPPvCardCoreDataStorage sharedInstance];
_vCard = [[XMPPvCardTempModule alloc] initWithvCardStorage:_vCardStorage];

//激活
[_vCard activate:_xmppStream];

//添加头像模块
_avatar = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_vCard];
[_avatar activate:_xmppStream];

4.应用

//xmpp提供了一个方法,直接获取个人信息
XMPPvCardTemp *myVCard =[WCXMPPTool sharedWCXMPPTool].vCard.myvCardTemp;

// 设置头像
if(myVCard.photo){
 self.haedView.image = [UIImage imageWithData:myVCard.photo];
}

// 设置昵称
self.nicknameLabel.text = myVCard.nickname;

5.好友

与用户信息模块相似,添加相应的好友花名册模块即可。

1.头文件

// 花名册模块
#import "XMPPRoster.h"
#import "XMPPRosterCoreDataStorage.h"

2.初始化

// 添加花名册模块【获取好友列表】
_rosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
_roster = [[XMPPRoster alloc] initWithRosterStorage:_rosterStorage];
[_roster activate:_xmppStream];

3.应用

//使用CoreData获取数据
// 1.上下文【关联到数据库XMPPRoster.sqlite】
NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].rosterStorage.mainThreadManagedObjectContext;

// 2.FetchRequest【查哪张表】
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPUserCoreDataStorageObject"];

// 3.设置过滤和排序
// 过滤当前登录用户的好友
NSString *jid = [WCUserInfo sharedWCUserInfo].jid;
NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@",jid];
request.predicate = pre;

//排序
NSSortDescriptor *sort = [NSSortDescriptor sortDescriptorWithKey:@"displayName" ascending:YES];
request.sortDescriptors = @[sort];

// 4.执行请求获取数据
_resultsContrl = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

_resultsContrl.delegate = self;

NSError *err = nil;
[_resultsContrl performFetch:&err];
if (err) {
 WCLog(@"%@",err);
}

注意:使用NSFetchedResultsController并设置代理,如果数据库的内容发生了变化,这个类会自动通知代理,就可以设置界面的数据,做到实时更新。

6.消息

1.头文件

  • 注意:这几个头文件没在XMPPFramework.h文件中,需要自己添加
// 消息模块
#import "XMPPMessageArchiving.h"
#import "XMPPMessageArchivingCoreDataStorage.h"

2.初始化

// 添加聊天模块
_msgStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];
_msgArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_msgStorage];
[_msgArchiving activate:_xmppStream];

3.应用

// 上下文
NSManagedObjectContext *context = [WCXMPPTool sharedWCXMPPTool].msgStorage.mainThreadManagedObjectContext;
// 请求对象
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];

// 过滤、排序
// 1.当前登录用户的JID的消息
// 2.好友的Jid的消息
NSPredicate *pre = [NSPredicate predicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",[WCUserInfo sharedWCUserInfo].jid,self.friendJid.bare];
NSLog(@"%@",pre);
request.predicate = pre;

// 时间升序
NSSortDescriptor *timeSort = [NSSortDescriptor sortDescriptorWithKey:@"timestamp" ascending:YES];
request.sortDescriptors = @[timeSort];

// 查询
_resultsContr = [[NSFetchedResultsController alloc] initWithFetchRequest:request managedObjectContext:context sectionNameKeyPath:nil cacheName:nil];

NSError *err = nil;
// 代理
_resultsContr.delegate = self;

[_resultsContr performFetch:&err];

NSLog(@"%@",_resultsContr.fetchedObjects);
if (err) {
  WCLog(@"%@",err);
}

7.文件传送(图片、音频)

1.原理分析

  • 使用base64将文件转化为字符串,然后再通过XMPPFramework传输。
  • 先将文件上传到服务器,再将文件网址通过XMPPFramework转输给好友,好友收到后再自行下载文件。

2.难点解析

  • 需要给XMPPFramework的``数据体添加一个信息类型字段。
XMPPMessage *msg = [XMPPMessage messageWithType:@"chat" to:self.friendJid];

//text 纯文本
//image 图片
[msg addAttributeWithName:@"bodyType" stringValue:bodyType];

// 设置内容
[msg addBody:text];
NSLog(@"%@",msg);
[[WCXMPPTool sharedWCXMPPTool].xmppStream sendElement:msg];
  • 根据消息类型解析消息
XMPPMessageArchiving_Message_CoreDataObject *msg = _resultsContr.fetchedObjects[indexPath.row];

// 判断是图片还是纯文本
NSString *chatType = [msg.message attributeStringValueForName:@"bodyType"];
if ([chatType isEqualToString:@"image"]) {
    //下图片显示
    [cell.imageView sd_setImageWithURL:[NSURL URLWithString:msg.body] placeholderImage:[UIImage imageNamed:@"DefaultProfileHead_qq"]];
    cell.textLabel.text = nil;
} else if ([chatType isEqualToString:@"text"]){

    //显示消息
    if ([msg.outgoing boolValue]) {//自己发
        cell.textLabel.text = msg.body;
    }else{//别人发的
        cell.textLabel.text = msg.body;
    }

    cell.imageView.image = nil;
}
屏幕快照 2017-04-24 下午2.06.01.png

2.环信

1.概念

屏幕快照 2017-04-24 上午11.13.17.png 1429890-b745aac54b315572.png 屏幕快照 2017-04-24 下午2.04.35.png

2.集成环信的准备工作

3.有关推送证书的内容

  • 3.1 创建推送证书的步骤:

step1. 打开[苹果开发者网站:https://developer.apple.com/cn/]

1400051-faf55bf12d2e19b6.jpg

step2. 从Member Center进入Certificates, Identifiers & Profiles

1400051-31c93383be646f4f.png

step3. 选择要制作的推送证书

1400051-c8fe1f3ce910ce87.png
对于开发环境(sandbox)的推送证书, 请选择 Apple Push Notification service SSL (Sandbox)
对于生产环境(production)的推送证书, 请选择 Apple Push Notification service SSL (Production)

step4. 选择对应的APP ID (环信示例使用ChatDemoUI, 所以此处选择com.easemob.enterprise.demo.ui)

1400051-1801b88869b66e3e.png

step5. 根据Certificate Assistant的提示, 创建Certificate Request

1400051-5f6dee6e50845490.png

step6. 上传上一步中创建的Certificate Request文件

1400051-64a6679b846fd796.png

step7. 上传完毕后, 推送证书就被正确生成了, 之后我们下载下来这个证书, 并双击导入系统

1400051-b659eee552ef787a.png
  • 3.2 上传推送证书的步骤:

step1. 打开Application –> Utilities –> Keychain Access应用, 我们会看到有刚刚我们制作好的推送证书

1400051-e960ed3425d6f41e.jpg

step2. 选中证书对应的私钥(或者展开后选中证书), 点右键, 选择导出, 并设定密码(本步导出的证书使用的电脑务必与制作证书时step5中使用的是一台电脑。)

1400051-395417e48955a3c3.png

step3. 登陆环信管理后台

1400051-6be2e2ca31cac67a.png

step4. 输入了正确的账号后, 选择对应的APP(环信示例为ChatDemoUI, 点击ChatDemoUI)

1400051-d7c978e572f3e16f.png

step5. 填写的证书名称

这个名称是个有意义的名字, 对推送直接相关, 稍后会在源码的修改里继续用到这个名字. 上传之前导出的P12文件, 密码则为此P12文件的密码, 证书类型请根据具体情况选择
(创建的是Apple Push Notification service SSL Sandbox请选择开发环境; Apple Push Notification service SSL Production请选择生产环境)

step6. 上传

1400051-cc45658d4f29fda5.png
请注意正确选择是生产环境还是测试环境的证书(我选的是开发环境,如果报错就选择生产环境)

4.集成环信SDK

集成SDK有两种方法,一种是用cocoaPods直接下载到自己的项目,一种是从官网下载SDK然后自己导入,不管哪种都需要导入第三方依赖库。

1.导入SDK
将下载好的SDK文件夹(EaseMobSDK)拖入到项目中,并勾选上Destination

1400051-d5c3c3971cbee487.jpg
2.设置工程属性
2.1. 向Build Phases → Link Binary With Libraries 中添加依赖库 1400051-671cb36841abd039.jpg 屏幕快照 2017-04-24 上午11.49.28.png

2.2. 向Build Settings → Linking → Other Linker Flags 中 添加-ObjC(注意大小写)

1400051-4788a01e68fe0fe8.jpg

2.3. 如果项目中使用-ObjC有冲突,可以添加-force_load来解决。
格式为: -force_load[空格]EaseMobSDK/lib/libEaseMobClientSDKLite.a(静态库的路径)(导入SDK过后会自动添加,如果没有就需要手动添加了,路径在EaseMobSDK-->lib中)。

  • step1. 先添加一个-force_load
1400051-65e75dc406cb51f6.jpg

step2. 将静态库拖动到上一步添加的-force_load下面

1400051-9cec842a3ad861f7.jpg

step3. 最终效果

1400051-f9eed763d745d60f.jpg
3.编译工程(根据情况修改所报的错误)

以上步骤进行完后,编译工程,如果没有报错,恭喜你,集成sdk成功,可以进行下一步了。
我们集成聊天功能的时候很多东西其实都不用我们动手,例如聊天页面等,我们可以直接从demo中拖过来,如果有不如意的地方,可以根据自己的喜好适当修改,下面我就说一下需要从环信3.0中搜索下面文件直接导入:

1400051-763d46c3a84279be.jpg

编译过后会报错如下图:

1400051-3df1c7f853dce967.jpg

这是因为没有导入EaseUI头文件导致的,在这里我们可以创建一个PCH文件(注意配置路径在

1400051-370c8dba936fc0f4.jpg


在pch文件中导入EaseUI.h头文件,编译成功。但是有可能出现报错这里介绍两种常见的报错
第一种:

1400051-e6c41c9155cf8ca4.jpg
这种报错解决办法是在环信3.0的demo中导入FixFopen.c文件即可,如果仍然报错那么在自己的pch文件中加入如下代码即可: 1400051-19e357f0a244e679.jpg

第二种:
属于第三方库的冲突报错,这种的话可以将环信中的第三方删除,其中需要注意的两个第三方库分别是:
1). EMSDWebImage,这是环信自己加了前缀,删除这个第三方库过后要在相应的代码中删除EM前缀删除;
2). MJRefresh,它使用的是老版本的,在新版本中一些老的方法已经删除,所以只能用EaseUI中的MJRefresh,后期环信应该会更新的。

5.使用环信

1.初始化应用,有两个方法

/*
*registerSDKWithAppKey:区别app的标识,开发者注册及管理后台apnsCertName:iOS中推送证书名称。制作与上传推送证书
*/

//环信的初始化
//    [[EaseMob sharedInstance] registerSDKWithAppKey:@"MG#MGChat"
ap sCertName:@””];

//环信的初始化
并隐藏日志输出 
[[EaseMob sharedInstance] registerSDKWithAppKey:@"MG#MGChat" apnsCertName:@"" otherConfig:@{kSDKConfigEnableConsoleLogger:@(NO)}];
1429890-e6764e24a534832b.png
2.注册
[[EaseMob
sharedInstance].chatManager asyncRegisterNewAccount:”MG” password:”123456” withCompletion:^(NSString *username, NSString *password,EMError *eror) {
     NSLog(@"error:%@,username:%@,pwd:%@",error,username,password);
} onQueue:nil];

3. 登录

  • 3.1 自动登录
屏幕快照 2017-04-24 下午2.21.53.png
  • 3.2 掉线自动登录

如果网络不通过,用户应该自动连接到服务器,以及时接收消息;
此功能无需程序员自己做,环信框架已实现,环信SDK会调用自动连接的代理方法来通知应用程序

/*!
 @method
 @brief 将要发起自动重连操作时发送该回调
 @discussion
 @result
 */
- (void)willAutoReconnect;

/*!
 @method
 @brief 自动重连操作完成后的回调(成功的话,error为nil,失败的话,查看error的错误信息)
 @discussion
 @result
 */
- (void)didAutoReconnectFinishedWithError:(NSError*)error;

4.好友

屏幕快照 2017-04-24 下午2.24.28.png

5.聊天

环信消息发送的流程:

1.先把记录保存到Conversation表

2.接着发送网络请求,API如下:

[[EaseMob sharedInstance].chatManager asyncSendMessage:message progress:self prepare:^(EMMessage *message, EMError *error) {
      NSLog(@"prepare %@",message.messageBodies);
} onQueue:nil completion:^(EMMessage *message, EMError *error) {
      NSLog(@"完成%@",message.messageBodies);
} onQueue:nil];
屏幕快照 2017-04-24 下午2.27.42.png
  • 环信提供会话管理者(EMConversation)来管理未读消息数和历史聊天记录,具体代码如下:
    总的未读消息数需要遍历conversations
// 1.获取所有历史会话
NSArray *conversations = [[EaseMob sharedInstance].chatManager conversations];

// 2.如果内存中,没有会话,从数据库中加载
if(conversations.count == 0)
{
    conversations = [[EaseMob sharedInstance].chatManager loadAllConversationsFromDatabaseWithAppend2Chat:YES];
}
1429890-19ee9e81764f398c.png
当进入聊天页面时,需要设置所有当前会话信息或者设置已经加载的消息为已读
// 设置当前会话所有消息都为已读
[self.conversation markAllMessagesAsRead:YES];

//设置某条消息为已读
 [self.conversation markMessageWithId:<#(NSString *)#> asRead:<#(BOOL)#>]

6.语言消息

  • 在录音前导入环信封装的两个录音框架,如图
1429890-0956ae0a330d57a2.png
  • 刚才导入的两个框架,已经实现了录音Api
// 开始录音 
[[EMCDDeviceManager
sharedInstance]
asyncStartRecordingWithFileName:fileName  completion:^(NSError *error){
     if(error) {
        NSLog(@"failure to start recording");
     }
}];
// 结束录音
[[EMCDDeviceManager sharedInstance] asyncStopRecordingWithCompletion:^(NSString *recordPath, NSInteger aDuration, NSError *error) {
    NSLog(@"%@",recordPath);
}];
// 语音对象
EMChatVoice *voice = [[EMChatVoice alloc] initWithFile:filePath displayName:@"audio"];

//消息体 
EMVoiceMessageBody *body = [[EMVoiceMessageBody alloc] initWithChatObject:voice];
EMMessage *message = [[EMMessage alloc] initWithReceiver:self.buddy.username bodies:@[body]];
message.messageType = eMessageTypeChat;// 私聊
// 不加密
message.requireEncryption = NO;
// 播放完成
[[EMCDDeviceManager sharedInstance] asyncPlayingWithPath:filePath completion:^(NSError *error) {
NSLog(@"播放完成%@",error);
  }];

7.退出(异步方法)

[[EaseMobsharedInstance].chatManagerasyncLogoffWithUnbindDeviceToken:YEScompletion:^(NSDictionary *info, EMError *error) {
    if (!error) {//退出成功
  }else{//退出失败;

  }
}
onQueue:nil];

Demo:

先来用户的登录和注册,由于只是搭建简单的基础聊天功能,我将这段代码写在appdelegate里面的,废话不多说,直接上图吧:

1400051-467a5403557dec81.jpg
注释中间有说明如何聊天

现在最主要的就是如何才能实现聊天界面的配置了,其实也是相当简单的,下面我们说说具体是怎么做的吧!!!其实聊天界面在我们刚才导入的EaseUI中就已经搭建好了,我们只需要跳转过去就行了

1400051-7024ee741c739758.jpg

这里我用的是button跳转,具体代码图片上面都有,下面给你们看下效果图吧!!!

1400051-d574a7dc52f0ab83.png 1400051-7c6eda53feb9088a.png
集成基础聊天功能到此就结束了,有什么不明白的和技术问题可以参考环信官方文档和在线咨询

相关优秀博客推荐链接:
(基于环信实现在线聊天功能)http://www.jianshu.com/p/055069fc10f3#
(基于环信实现实时视频语音通话功能)http://www.jianshu.com/p/c5c46c2fa9c6
(上帝说:要约炮!于是有了XMPP)https://www.jianshu.com/p/c7bbbad90639

相关文章

  • 聊天功能了解

    即时通讯 1.XMPP(http://xmpp.org) 1.概念 XMPP是一种基于标准通用标记语言的子集XML...

  • IOS 基于SQL实现可以保存聊天记录的功能实现

    IOS 基于SQL实现可以保存聊天记录的功能实现 由于公司要在app中实现聊天功能,在花了一段时间了解实现该功能后...

  • 聊天功能

    了解webscoket WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议...

  • 【直播聊天室】特点、构成及解决方案

    为方便开发者了解和接入融云直播聊天室,本文将介绍融云直播聊天室功能特点、主要构成及常见功能的解决方案。 一、产品介...

  • 阅读源码方法

    功能 从全局了解功能架构 接口 了解对外接口,提供什么服务 成员变量 了解成员变量的功能

  • 创业趋势海外版:Viber推出聊天新功能secret messa

    近期有外媒报道,即时聊天应用Viber推出加密功能secret messages。据了解,Viber是一款由开发商...

  • 8-聊天功能

    在Python里面实现实时通讯我们有很多种方法,既然选择了flask框架,那我们也一并采用flask下的flask...

  • Socket 实现聊天功能

    需要提前了解知识点java.net.Socket 解析java.net.ServerSocket 解析 使用soc...

  • Netty实现聊天功能

    客户端首先编写我们的服务端处理器 handler 服务端的初始化类 服务端 服务端服务端处理器 服务端初始化类 服...

  • 【2022-03-19】今日总结

    今天做的事情 1.聊天功能的讨论2.关注功能的讨论3.专家系统的讨论 总结事情 (1)聊天功能的讨论,从功能上聊天...

网友评论

    本文标题:聊天功能了解

    本文链接:https://www.haomeiwen.com/subject/nrzlnttx.html