注册
环信即时通讯云

环信即时通讯云

单聊、群聊、聊天室...
环信开发文档

环信开发文档

环信FAQ

环信FAQ

集成常见问题及答案
RTE开发者社区

RTE开发者社区

汇聚音视频领域技术干货,分享行业资讯
技术讨论区

技术讨论区

技术交流、答疑
资源下载

资源下载

收集了海量宝藏开发资源
iOS Library

iOS Library

不需要辛辛苦苦的去找轮子, 这里都有
Android Library

Android Library

不需要辛辛苦苦的去找轮子, 这里都有

好友列表展开、收起、点击修改、原理实现

好友列表,单独拉出来做了个文档,已上传。展开收起的实现其实是很简单的。首先我们把好友列表写在了UITableView 上,点击每个cell 可以触发 UITableViewDelegate 协议里面的 didSelectRowAtIndexPath:(NSIn...
继续阅读 »
好友列表,单独拉出来做了个文档,已上传。展开收起的实现其实是很简单的。首先我们把好友列表写在了UITableView 上,点击每个cell 可以触发 UITableViewDelegate 协议里面的 didSelectRowAtIndexPath:(NSIndexPath *)indexPath 这个协议方法跳转至下个界面查看并修改信息。而后在详情页即修改信息的控制器里面声明一个协议方法,-(void)editName:(NSString *)name andPhone:(NSString *)phone;  (可以再添加多个修改信息) [_delegate editName:nameText.text andPhone:phoneText.text]; // 实现修改     ,完成修改后,在委托者的控制器里面,这里是 ViewController,实现代理方法,并 [table reloadData]; // 刷新原数组显示。即可完成修改。同样在点击事件里,控制返回cell 行数,并 reloadData, 即可完成列表的展开、收起。 收起阅读 »

iOS集成支付宝

iOS
做了个文档传到附件了
做了个文档传到附件了

Ios上架流程

一、生成发布证书: 1、首先创建钥匙串配置文件,进入钥匙串 2、选择从证书颁发机构请求证书: 3、填写信息   4、保存配置信息 5、进入苹果开发者网页:https://d...
继续阅读 »
一、生成发布证书:

1、首先创建钥匙串配置文件,进入钥匙串


图片1.png


2、选择从证书颁发机构请求证书:


图片2.png



3、填写信息


图片3.png



 
4、保存配置信息


图片5.png


5、进入苹果开发者网页:https://developer.apple.com/
6、点击member center,如下图:


图片6.png


7、输入账号,进入开发者主页,并选择证书选项:


图片7.png


8、进入证书页,首先创建证书


图片8.png


9、选择production,并点击+

图片9.png


10、选择需要创建的证书类型,在这里我们选择production中的appstore and ad hoc,如果有推送功能,则重复以上步骤,选择第二个:


图片10.png


11、点击continu,直到进入Cenerate界面,点击choose file,上传之前创建好的


图片11.png


12、选择钥匙串授权文件,点击继续:


图片12.png


13、生成之后,我们会跳转到Download界面,点击界面中的“Download”下载下来,双击我们生成的.cer文件,一定要双击,双击后它会默认安装到钥匙串中。(推送证书重复之前步骤)
14、之后点击左边目录中的“Identifiers”下的“App IDs”,点击+:


图片14.png


15、填写创建证书所需要的信息(如果需要推送则选择第一个,固定标识):


图片15.png




图片16.png




图片17.png


16、点击continue,然后submit,如果有推送功能,则创建推送证书,创建推送证书过程中,会有个选择appids的选项,选择对应的appids:


图片18.png


17、注册手持设备,点击左边目录中的“Devices”,同样点击右上方的“十”号,进行添加。


图片19.png


18、输入设备名称以及udid,我们可以通过iTunes获取设备的udid:


图片20.png


19、点击continue,然后点击注册,完成设备的条件
20、创建Provisioning Profiles,也就是手机上使用的证书,点击最左边目录栏,选择“Provisioning Profiles”目录下的“All”,同样点击右上方的“十”号进入证书添加界面


图片21.png


21、选择APP Store,点击continue,选择我们之前创建的App ID,点击continue:


图片22.png


22、在下面选项里面选择我们之前生成的授权发布证书的名字,点击continue:


图片23.png


23、接下来输入证书名字,改名字将会在xcode中显示,然后点击Create来创建证书,最后下载,双击进行安装。
 

二、xcode打包发布的ipa
1、修改项目中TARGETS中的Bundle identifier,与我们之前创建的App ID中的标识保持一致:


图片24.png


2、分别设置TARGETS和PROJECT里面Build Settings中对应的Code Signing属性:


图片25.png


3、选择IOS Device,然后点击xcode菜单中的Product,选择Archive,进行打包:


图片26.png


4、打包完成后进入Archive界面,我们可以直接submit到appStore,也可以选择导出到本地,通过上传工具再上传:


图片27.png


5、点export,选择打包ipa的用途,我们选第一个,发布到appstore:


图片28.png


6、点击next,进入开发者账号的选择,如果之前设定好,会直接显示,反之则会提示输入账号和密码:


图片29.png


7、点击choose,会出现对应的应用信息,然后点击export,取个名字,直接保存即可
 

三、iTunes connect中创建app
1、进入苹果开发者网页:https://developer.apple.com/,进入member center,选择iTunes Connect:


图片30.png


3、点击+,选择新建IOS App,然后在弹出框填写自己的app信息:


图片31.png




图片32.png


4、点击创建,进入app详情页,上传自己的截图、app的展示图、app的描述等。
5:用application loader将ipa文件上传到iTunes connect,随后在app详情页的build处点击+,选择自己上传的ipa文件即可。
 
 
 
 
 
  收起阅读 »

iOS 通过 REST API 接口上传文件(图片)

官方文档中提到了通过通过 REST API 接口上传文件的问题。这里我简单介绍一下通过REST API 接口上传图片的问题。     我们知道由于iOS无法通过html表单来上传图片,因此想要上传图片,必须实现http请求,而不能像其他语言那样通过html表...
继续阅读 »


未命名.png


官方文档中提到了通过通过 REST API 接口上传文件的问题。这里我简单介绍一下通过REST API 接口上传图片的问题。
    我们知道由于iOS无法通过html表单来上传图片,因此想要上传图片,必须实现http请求,而不能像其他语言那样通过html表单的post就能上传。这个文档中貌似没有说清楚
上传图片的http post请求的格式是这样的:
    

11.png


第一行是指定了http post请求的编码方式为multipart/form-data(上传文件必须用这个)。 
boundary=AaB03x说明了AaB03x为分界线。比如 --AaB03x 就是一个分界线的意思 

content-disposition: form-data; name="field1" 

Hello Boris! 
 
 这句话声明了请求中的一个字段的名称,如field1  以及字段的值,如Hello Boris! 
这里类似form表单中的 
中间的空行是必须的。一般是文件的一些属性 

不同的字段之间用分界线分开,分界线需要单独一行,如 --AaB03x-- 

分界线的下一行,是下一个字段 

content-disposition: form-data; name="pic"; filename="boris.png" 
Content-Type: image/png 

... contents of boris.png ... 
--AaB03x-- 

这里声明了变量pic,也就是我们要传的文件,上传文件的时候需要在后边指定filename:filename="boris.png" 
并且需要在下一行指定文件的格式:Content-Type: image/png 

... contents of boris.png ...  这里是boris.png的二进制内容(也就是data类型),如 <89504e47 0d0a1a0a 0000000d 49484452 000000b4 000000b4 08020000 00b2af91 65000020 00494441 5478012c dd79b724 6b7616f6 8c888c88 8c9c8733 55ddb1d5 6a0db486 06218401 ...... 

在http post请求的结尾,需要有一个分界线,但是是前后都有--的:--AaB03x-- 

以上的这些格式,是http的规范,每个空行,空格都是必须的。 
 
下边是iOS的实现代码
/*---------------------------------上传图片-------------------------------------------*/

    //分界线的标识符
    NSString *TWITTERFON_FORM_BOUNDARY = @"AaB03x";
    //上传的接口
    NSURL *url = [NSURL URLWithString:@"https://a1.easemob.com/环信ID/APP名字/chatfiles"];
    //要上传的图片,得到data
    NSData *data = UIImagePNGRepresentation([UIImage imageNamed:@"defain"]);//这是我的本地图片
    //声明file字段,文件名为defain.png, 声明上传文件的格式
    NSString* strBodyBegin = [NSString stringWithFormat:@"--%@\nContent-Disposition: form-data; name=\"%@\"; filename=\"%@\"\nContent-Type: %@\n\n", TWITTERFON_FORM_BOUNDARY, @"file",  @"defain.png", @"image/png"];
    //声明结束符:--AaB03x--
    NSString* strBodyEnd = [NSString stringWithFormat:@"\n--%@--",TWITTERFON_FORM_BOUNDARY];
    NSMutableData *httpBody = [NSMutableData data];
    //表单开始
    [httpBody appendData:[strBodyBegin dataUsingEncoding:NSUTF8StringEncoding]];
    //填入数据
    [httpBody appendData:data];
    //表单结束
    [httpBody appendData:[strBodyEnd dataUsingEncoding:NSUTF8StringEncoding]]; 
/*
注意:这里我没有用下面的字段(它主要用来描述text格式的文本信息)
content-disposition: form-data; name="field1" 
Hello Boris! 
*/   
    NSMutableURLRequest* httpPutRequest = [[NSMutableURLRequest alloc] init];
    [httpPutRequest setURL:url];
    //设置请求方法
    [httpPutRequest setHTTPMethod:@"POST"];
    [httpPutRequest setTimeoutInterval: 60000];
    //设置HTTPHeader中token的值
    [httpPutRequest setValue:@"Bearer **************************************************" forHTTPHeaderField:@"Authorization"];
    //设置访问权限
    [httpPutRequest setValue:@"true" forHTTPHeaderField:@"restrict-access"];
    //设置Content-Length
    [httpPutRequest setValue:[NSString stringWithFormat:@"%@", @(httpBody.length)] forHTTPHeaderField:@"Content-Length"];
    //设置HTTPHeader中Content-Type的值
    [httpPutRequest setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",TWITTERFON_FORM_BOUNDARY] forHTTPHeaderField:@"Content-Type"];
    //将表单添加到请求体
    httpPutRequest.HTTPBody = httpBody;
    //异步请求
    [NSURLConnection sendAsynchronousRequest:httpPutRequest queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
       if (connectionError == nil) {
            id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
            NSLog(@"服务器返回信息%@",obj);
        }
    }];
 
//服务器返回信息
2016-01-03 14:30:20.272 Fuhome-App[1356:154571] 服务器返回信息{
    action = post;
    application = "************";
    applicationName = **********;
    duration = 81;
    entities =     (
                {
            "share-secret" = "*********";
            type = chatfile;
            uuid = "***********";
        }
    );
    organization = ******;
    path = "/chatfiles";
    timestamp = 1451802620175;
    uri = "https://a1.easemob.com/******/*******/chatfiles";
}
 

      
  收起阅读 »

关于demo3.0表情,文字混合输入问题

在上一个2.0版本中,我有一天脑洞大开,突然想试试,聊天的输入内容是如何删除的。于是我输入了一些文字,发现删除时确实是一个一个删除的,接着测试,我在输入文字的同时,输入了表情,demo自带的表情。然后删除,发现也没问题。那么继续,我在输入文字时,夹杂了系统中得...
继续阅读 »
在上一个2.0版本中,我有一天脑洞大开,突然想试试,聊天的输入内容是如何删除的。于是我输入了一些文字,发现删除时确实是一个一个删除的,接着测试,我在输入文字的同时,输入了表情,demo自带的表情。然后删除,发现也没问题。那么继续,我在输入文字时,夹杂了系统中得emoji表情。然后,哈哈哈,程序跪了。查看代码后,发现一个emoji表情占两个字符,而是不是emoji表情居然是通过是否包含在_defaultEmoji进行的判断,而_defaultEmoji只包含demo自己的30多个表情,果然demo就是demo,参考一下就好。造成奔溃的原因就是我们输入了不属于_defaultEmoji的表情,在进行删除时,没有按两个字符进行删除。
#pragma mark - DXFaceDelegate

- (void)selectedFacialView:(NSString *)str isDelete:(BOOL)isDelete
{
NSString *chatText = self.inputTextView.text;

NSMutableAttributedString *attr = [[NSMutableAttributedString alloc] initWithAttributedString:self.inputTextView.attributedText];

if (!isDelete && str.length > 0) {
NSRange range = [self.inputTextView selectedRange];
[attr insertAttributedString:[EaseEmotionEscape attStringFromTextForInputView:str] atIndex:range.location];
self.inputTextView.text = @"";
self.inputTextView.attributedText = attr;
// self.inputTextView.text = [NSString stringWithFormat:@"%@%@",chatText,str];
}
else {
if (chatText.length > 0) {
NSInteger length = 1;
if (chatText.length >= 2) {
NSString *subStr = [chatText substringFromIndex:chatText.length-2];
if ([_defaultEmoji containsObject:subStr]) {
length = 2;
}
}
self.inputTextView.attributedText = [self backspaceText:attr length:length];
}
}

[self textViewDidChange:self.inputTextView];
}
解决也很简单,我再判断表情时,同时使用了下面的方法。这个方法我加在了NSString的扩展中。
+ (BOOL)stringContainsEmoji:(NSString *)string
{
__block BOOL returnValue = NO;

[string enumerateSubstringsInRange:NSMakeRange(0, [string length])
options:NSStringEnumerationByComposedCharacterSequences
usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
const unichar hs = [substring characterAtIndex:0];
if (0xd800 <= hs && hs <= 0xdbff) {
if (substring.length > 1) {
const unichar ls = [substring characterAtIndex:1];
const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
if (0x1d000 <= uc && uc <= 0x1f77f) {
returnValue = YES;
}
}
} else if (substring.length > 1) {
const unichar ls = [substring characterAtIndex:1];
if (ls == 0x20e3) {
returnValue = YES;
}
} else {
if (0x2100 <= hs && hs <= 0x27ff) {
returnValue = YES;
} else if (0x2B05 <= hs && hs <= 0x2b07) {
returnValue = YES;
} else if (0x2934 <= hs && hs <= 0x2935) {
returnValue = YES;
} else if (0x3297 <= hs && hs <= 0x3299) {
returnValue = YES;
} else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) {
returnValue = YES;
}
}
}];

return returnValue;
}
注意:下面的代码为更新版本,上面的代码还是太久远了,下面的也是网上找到的,感觉还比较新。
+ (void)load {

    VariationSelectors = [NSCharacterSet characterSetWithRange:NSMakeRange(0xFE00, 16)];

}




- (BOOL)isEmoji {

    if ([self rangeOfCharacterFromSet: VariationSelectors].location != NSNotFound) {

        return YES;

    }

    

    const unichar high = [self characterAtIndex: 0];

    

    // Surrogate pair (U+1D000-1F9FF)

    if (0xD800 <= high && high <= 0xDBFF) {

        const unichar low = [self characterAtIndex: 1];

        const int codepoint = ((high - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000;

        

        return (0x1D000 <= codepoint && codepoint <= 0x1F9FF);

        

        // Not surrogate pair (U+2100-27BF)

    } else {

        return (0x2100 <= high && high <= 0x27BF);

    }

}
收起阅读 »

如何通过REST接口获取token

/*--------------------------获取管理员oken---------------------------------------*/      NSURL *url = [NSURL URLWithString:@"https://a...
继续阅读 »
/*--------------------------获取管理员oken---------------------------------------*/
     NSURL *url = [NSURL URLWithString:@"https://a1.easemob.com/企业ID/应用app名称/token"];
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
     request.HTTPMethod = @"POST";
     NSDictionary *body = @{@"grant_type":@"client_credentials",@"client_id": @"填写Client Id",@"client_secret":@"填写Client Secret"};
     [request setHTTPBody:[body JSONData]];//JSONData为一个库的方法,该库在下面附件中
     [request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
     //连接,异步
     [NSURLConnection sendAsynchronousRequest:request queue:[[NSOperationQueue alloc] init] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) {
     if (connectionError == nil) {
     id obj = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
     NSLog(@"服务器返回信息%@",obj);
     NSString* token = [obj objectForKey:@"access_token"];
     if(token)
     {
     //     access_token 保存一下(可以把token保存到本地)
     [[NSUserDefaults standardUserDefaults] setObject:token forKey:@"access_token"];
     [[NSUserDefaults standardUserDefaults] synchronize];
     }
     }
     NSLog(@"错误信息%@",connectionError);
     }]; 收起阅读 »

【有奖征集】环信开发文档Feedback

为了更好地解决开发者在集成环信产品时遇到的问题,降低使用难度。 我们专门开设一个帖子征集开发文档的不足之处。请大家不吝指正,毫无保留的提出意见和建议。 开发文档的地址是:http://docs.easemob.com 不管是界面,内容,还是结构。任何跟文档...
继续阅读 »
为了更好地解决开发者在集成环信产品时遇到的问题,降低使用难度。
我们专门开设一个帖子征集开发文档的不足之处。请大家不吝指正,毫无保留的提出意见和建议。

开发文档的地址是:http://docs.easemob.com

不管是界面,内容,还是结构。任何跟文档站相关的统统可以吐槽。我们将统一收集整理,并不断改进我们的产品。

当然,这一切也许还会有意想不到的惊喜哦~ 我们将对有价值的回复提供现金赞赏!

这种主动求吐槽,还派发奖赏的玩法好像也就我们一家了吧~

GO! GO! GO! 收起阅读 »

推荐一款编程字体,让代码看着更美

    在IDE(集成开发环境)中进行程序开发时,字体的选择看上去并不是那么的重要。但是,一款优雅的字体在整体视觉上能让代码看上去更美,营造一个积极的气氛,眼睛不再那么累,心情就会显得不错,那么你的开发效率自然也会有所提升。   今天,就给大家推荐一款字体,一...
继续阅读 »
 
 
在IDE(集成开发环境)中进行程序开发时,字体的选择看上去并不是那么的重要。但是,一款优雅的字体在整体视觉上能让代码看上去更美,营造一个积极的气氛,眼睛不再那么累,心情就会显得不错,那么你的开发效率自然也会有所提升。
 
今天,就给大家推荐一款字体,一款美美的字体,也是个人长久使用的字体。在这之前,先简单介绍一下Eclipse的默认字体 —— Consolas。既然被Eclipse选为默认字体,Consolas必然已经得到了广大编程人员的认可,优势之处无需多言,但美中不足的是,Consolas显示的中文比英文要小,如图所示:


1094967-49c4561e6be8713c.jpg


通常,为了便于操作,我们总是希望在PC一屏中显示更多的代码,于是便采取缩小字号的方式。但是,在Eclipse默认字体下,中文汉字、中文符号等就显得特别小以至于很难看清,眼睛非常之累。

相比之下,本文中的主角要出现了 —— YaHei.Consolas.1.12.ttf 。这是一款集成了微软雅黑的Consolas,非常漂亮,具体效果,有图有真相:


1094967-e0dbdbee5b20d42d.jpg


是不是有一种焕然一新的感觉呢?是不是显得特别大气呢?接下来就看一下在Eclipse中的安装教程吧。

首选下载 YaHei.Consolas.1.12.ttf 字体并复制到电脑控制面板中的字体目录下,Google Code 官方下载地址(翻墙):

https://code.google.com/p/uigroupcode/downloads/detail?name=YaHei.Consolas.1.12.zip&can=2&q=

CSDN资源下载地址:

http://download.csdn.net/detail/wenbitianxiafeng/9384674

在Eclipse中,选择[Windows] --> [Preference] --> [General] --> [appearance] --> [Colors and Fonts] --> [Basic] --> [Text Font],进入字体编辑窗口:

1094967-9c2edd5670821939.jpg


点击 [Edit],可以看到Eclipse默认的是Consolas字体:

1094967-b558b6e6c763601d.jpg


在字体列表中选中 YaHei.Consolas.1.12.ttf ,确定并应用即可。

然后就可以尽情的享受这款字体带来的愉悦吧。

PS:如果大家有更好的编程字体,欢迎评论推荐~ 收起阅读 »

ios详细的证书创建和上传

为新手制定一个详细的创建推送证书流程 第一步:生成.certSigningRequest证书 打开钥匙串,在钥匙串导航栏找到,“钥匙串访问”这一项,在钥匙串访问找到“从证书颁发机构请求证书”这一项,如图 点击后进入到如图界面 用...
继续阅读 »


为新手制定一个详细的创建推送证书流程
第一步:生成.certSigningRequest证书
打开钥匙串,在钥匙串导航栏找到,“钥匙串访问”这一项,在钥匙串访问找到“从证书颁发机构请求证书”这一项,如图

p1.png


点击后进入到如图界面

p2.png


用户电子邮件地址:写上你的邮件就行
常用名称:随便写
然后选择储存到磁盘,CA电子邮件地址不用写,点继续就生成了.certSigningRequest文件了,记得这个文件的储存位置,一会我们生成推送证书还要用到
第二步:创建推送证书
打开开发者中心,进入生成证书界面(应该找的到吧),进入之后,如果你要生成测试环境的推送证书,选择Certificates-Development,如果你要生成正式环境证书就选择Certificates-Priduction,这里我们以测试为例,点击右上角加号,如图

p3.png


进入下个界面,如果你要生成测试环境推送证书,就选择红色部分,你要生成正式环境证书,就选择绿色部分,如图

p4.png


点Continue,进入下个界面,让选择app id,提醒一下,选择app id要和你工程的Bundle Identifier对应,新手应该找的到Bundle Identifier吧,找不到看下面的图,我截了张图

p10.png


选择完app id ,一直点击Continue,进入如图界面

p5.png


让选择文件,还记得我们刚开始用钥匙串生成的.certSigningRequest在哪放着吧,就选择它就对了,选择完之后,接着往下面走,把证书下载下来之后,双击就放到钥匙串里面了,这时候推送证书就生成完了
第三步:生成p12文件
我们最终放到环信后台的证书是p12证书,打开钥匙串,找到我们刚才生成的证书,右键找到导出选项,如图

p6.png


点击导出“Apple Development.....”之后进入下个界面,如图:

p7.png


红色圈着的部分,储存为:里面要写上证书的名字,名字随便起,不过要记住这个名字,一会在环信后台上传证书还要用到这个名字,名字填完之后,点击储存,储存的位置要记住,一会还要用这个文件,这个文件就是我们要上传到环信的后台的p12文件,(储存的时候会让你输入密码,这个密码你要记住,一会上传到环信后台证书的时候要用到这个密码),这时候p12文件就制作完成了。
第四步:把p12文件上传到环信后台
打开我们的环信后台,找到 推送证书-ios,下面有上传证书的地方,如图

p8.png


证书名称:就是我们刚才生成p12的名称,我的叫zhuma_test
证书:就是我们刚才生成的p12证书,找到传上去
证书密码:生成p12证书的时候输入的密码(不是瞎输的吧,能记得吗,记不得重新导p12文件吧)
证书类型:我生成的是开发环境的证书,就勾选开发环境
填完之后就可以上传了,上传成功,就ok了 收起阅读 »

环信移动客服v4.2产品更新说明

环信移动客服v4.2 产品更新说明   新增功能   【客服系统】 多级会话标签 【数据统计】 聊天消息导出 【客服系统】 客服头像和企业logo 【客服系统】 访客超时未回复自动结束会话 【客服系统】 待接入筛选和搜索 【...
继续阅读 »
环信移动客服v4.2
产品更新说明
 

QQ截图20151229144613.png



新增功能
 
【客服系统】 多级会话标签
【数据统计】 聊天消息导出
【客服系统】 客服头像和企业logo
【客服系统】 访客超时未回复自动结束会话
【客服系统】 待接入筛选和搜索
【客服系统】 微博集成
【web插件】 访客端web插件开发者版
【客服系统】 会话显示关联名称
【客服系统】 自定义事件推送
【客服系统】 访客统计
【客服系统】 Android手机客服工作台新功能同步   
 
优化功能    
 
【web插件】 客服端增加ctrv+v图片预览
【客服系统】 欢迎语长度增加
【客服系统】 客服模式支持菜单收起
【数据统计】 历史会话支持模糊查询
【数据统计】 历史会话增加渠道、关联、评分等查询维度
【客服系统】 增加“系统开关”设置面板,统一管理系统开关
【客服系统】 优化文案
 
进入体验最新功能吧:  http://www.easemob.com/services

  收起阅读 »

在较成熟的中大型公司做创业项目,是种什么样的体验?

在体制发展较成熟的中大型公司里,搞创业项目,也是创业的一种姿态。     记得之前经常会碰到一些即将毕业的或者面临择业的朋友,会问到“依照现在国内互联网公司发展的情况,现在选择去一些较成熟的公司,如BAT这类的公司更好呢,还是选择去一些创业公司更好呢?”   ...
继续阅读 »
在体制发展较成熟的中大型公司里,搞创业项目,也是创业的一种姿态。  
 
记得之前经常会碰到一些即将毕业的或者面临择业的朋友,会问到“依照现在国内互联网公司发展的情况,现在选择去一些较成熟的公司,如BAT这类的公司更好呢,还是选择去一些创业公司更好呢?” 
 

                                   

banner_sample.png


 
 
很多专业的职业规划指导师,都会列出N条这样选择或者那样选择的优缺点,但是今天我想和大家分享另外一种工作的姿态,如标题所示。
 
什么样的公司称得上较成熟的中大型公司呢?这个当然也没有统一标准,不过按照目前国内互联网公司的现状来说的话,除了以BAT为首的大佬们,很多二三线的公司也称得上中大型公司,如网易、携程、美团、赶集网、新浪、搜狐等等;除了互联网行业,每个行业也有其龙头标杆,一般排在行业前5的公司,也可以算我要说的这种中大型企业;另外公司员工也可以作为参照,员工至少也是千人以上的级别。 
 
我所在的公司的是一家传统做软件外包服务的公司,员工近2000人,公司成立十年,业务发展稳定,在行业中也排的上前几。 
 
只是在这两年互联网发展如此迅猛的情况下,公司高层也一直谋划转型,希望做试水一些互联网的项目,因此我们的项目也在这种的情况下孵化了。 
 
我们产品叫摘客,是一款主推个性化推荐的互联网资讯聚合阅读产品,目前除了网站,iOS和Android版本的App也都已经上线。
 
摘客官网
 
言归正传,在这样的公司背景下,做这样一个未知的项目,体验可以分为以下几点: 
 
 1)生存问题没创业公司那么严峻 这一点可能也是唯一的好处啊,但它却是重要和必须的。显而易见,不太需要担心钱用完了,明天公司倒闭了这样的问题,因此在选择做与不做上,没有太多梦想与面包的挣扎; 
 
2)公司少数支持,资源匮乏 除了以上一点,貌似剩下的都是呕心沥血啊。大公司发展到这个地步,自然有它成熟的机制和业务发展模式,每个部门、岗位都仍然在它正轨上运行着;而你这样一样不合群新生的项目,自然在没有关系利益的时候,很难获得其他资源部门的支持;比如我们在APP,做网站的时候,需要界面UI设计,而我们核心的项目组成员保持为核心后台的开发,因此需要找我们公司UED项目组来合作;但是由于我们目前并不盈利,而高层也不可能来经常盯着这些事情,因此在和不同部门沟通协作的时候, 通常被作为优先级不高的、要求不高的事项,设计师自然也不会卖力做设计,这样的出品自然劳心劳力啊,吐槽一百遍~ 
 
3)没有经验积累 既然是创业项目,当然是和公司原先的业务发展不同,可想而知,公司在这项目方面的积累也是少之又少的。非常明显的一点,就是互联网产品的运营。由于公司之前都做的都是软件外包,公司的市场部也只要是针对2b的模式来做,完全不懂2C的玩法和运营。因此在完成产品的同时,运营的工作也成了我们项目的一大难题,由于没有经验可带队的人,我们运营都是从头开始,一路摸索而来,现在仍在继续抹黑爬行~~继续吐血~ 
 
4)发挥空间相对更多 相较于企业的传统业务和项目,创业型的项目在公司更有发挥空间。因为传统业务已经有较成熟的模式,分工细致,岗位明确,因此对于个人而言,在常规业务中想要争取向上层升职,也是较困难的。而在新兴项目中,基本都是空白,较容易成为第一个吃螃蟹的人。 
 
先写这些啦,其实在不同状态、环境下,都有来自各方无形的压力,写这些希望能分享给大家,纯属个人体验,欢迎交流分享~
 
下面分享我们产品的链接

                                   

最新二维码下载.png


 
  收起阅读 »

感恩有你,Merry Christmas

  因为有你,环信连接人与人才有价值。 因为有你,环信连接人与商业才有基础。 感谢有你,环信飞速发展,一年四轮融资。 感谢有你,环信即时通讯云稳居行业第一,移动客服领军行业。 感谢有你,环信大数据产品,反垃圾服务等如雨后春笋。 感谢有你,感恩有你,感激有你,“...
继续阅读 »
 
因为有你,环信连接人与人才有价值。
因为有你,环信连接人与商业才有基础。
感谢有你,环信飞速发展,一年四轮融资。
感谢有你,环信即时通讯云稳居行业第一,移动客服领军行业。
感谢有你,环信大数据产品,反垃圾服务等如雨后春笋。
感谢有你,感恩有你,感激有你,“环信”有“你”而精彩!
 
 
Merry Christmas
真诚的祝你圣诞快乐!

J{(}BNF76ZK0LDUV06X0R4M.jpg

收起阅读 »

【新特性】移动客服上线自定义富媒体消息的扩展功能

移动客服这边实现了自定义富媒体消息的扩展功能,允许客户为自己的客服坐席提供自 定义的包含图片,文字,链接等丰富内容的消息,用来向APP用户发送精美的产品介 绍,订单信息等等   简介 环信自定义消息API允许客户在坐席工作台加载自己的消息编辑页面,按照环信自定...
继续阅读 »
移动客服这边实现了自定义富媒体消息的扩展功能,允许客户为自己的客服坐席提供自
定义的包含图片,文字,链接等丰富内容的消息,用来向APP用户发送精美的产品介
绍,订单信息等等
 
简介
环信自定义消息API允许客户在坐席工作台加载自己的消息编辑页面,按照环信自定义消息的格式发送包含图文和链接的消息给访客,从而实现从坐席到访客的产品介绍,订单消息等丰富内容的可扩展消息。
 
使用场景:
开通自定义消息功能后,在消息编辑窗上方会增加自定义消息按钮:

图片1.png


 

点击自定义消息按钮,会弹出iFrame窗口并加载客户在开通功能时提供的的消息编辑页面

图片2.png


 

在编辑页面勾选消息,并确认发送后,消息会直接发送给访客,并显示在客服坐席的聊天记录窗口中。

图片3.png




图片4.png



 
访客APP一侧按照约定的消息格式做展示
 
与客服对话中的自定义消息:

图片5.png


 

点击打开后的自定义消息内容:

图片6.png


 

多个产品组合的购物清单:

图片7.png



新特性开通请联系“在线技术支持
新特性体验请点击“环信移动客服收起阅读 »

注册环信

我认为注册有两种方案,分别对应上图。 1.预注册一批账号备用。 这个方式主要是服务器端先注册一定数量的环信账号放到号码池,这样当用户注册时,可以直接从号码池分配,当号码池中的号码不足一定数量时,服务器再去注册,这样将用户中注册的流程分成了异步,提高了注册效率。...
继续阅读 »

注册账号.jpg


我认为注册有两种方案,分别对应上图。
1.预注册一批账号备用。
这个方式主要是服务器端先注册一定数量的环信账号放到号码池,这样当用户注册时,可以直接从号码池分配,当号码池中的号码不足一定数量时,服务器再去注册,这样将用户中注册的流程分成了异步,提高了注册效率。
2.直接注册。
这种方式就是当用户注册我的app时,我的服务器同步向环信注册。这样会增加客户端返回时间。
  收起阅读 »

【环信集成-入门篇】 环信能帮助我们做什么

作为一名常年混迹imGeek开发者论坛的程序猿,本着响应论坛号召的“我为人人,人人为我”口号,赠人玫瑰,手留余香。所以写下这期集成笔记,写这期笔记主要是为了让大家更直观的了解和解决大家的常见问题,比如:“环信是什么?我能不能使用?以及,怎么集成环信?”   ...
继续阅读 »
作为一名常年混迹imGeek开发者论坛的程序猿,本着响应论坛号召的“我为人人,人人为我”口号,赠人玫瑰,手留余香。所以写下这期集成笔记,写这期笔记主要是为了让大家更直观的了解和解决大家的常见问题,比如:“环信是什么?我能不能使用?以及,怎么集成环信?
 
先讲一下对环信的理解吧,其实我们可以把它想的简单一点。环信无非就是提供一个聊天通道,是吧,就这么简单的事。环信能帮我们干嘛呢?据说可以让APP、网站拥有微信、QQ 那样的通讯功能,可以让用户可以在自己的APP里发图片 文字语音等即时通讯功能。听起来是很高大上的,那我们就去看下环信能不能办到宣传的那样神奇 和究竟应该怎么使用这个”通道”吧。

官网文档是这样的

QQ截图20151224110147.png


 
求心里面的阴影面积呀,这也太繁琐了吧。不过为了这么高大上的功能花点时间研究也是值得的。
一个上午过去了,在怀着孜孜不倦的求知欲和技术支持团队可耐妹子的详细讲解下终于弄明白了

QQ截图20151224110416.png


大致分为了一下两步

1:客户端的集成
2:服务端的集成


客户端集成以 什么形式?有什么作用呢?
环信提供了原生的sdk ,Android iOS,webim ,需要把sdk导到自己的 项目,然后调用对应的api,比如登陆收发消息。

讲到这里,估计有的用户又有问题了,环信不是已经提供了聊天通道吗,为什么还要我们服务端集成呢?
这里需要注意的是环信“只是一个聊天通道”,不包含APP 的用户信息,也就是自己APP 的用户体系(昵称头像、好友备注分组等是需要自己维护的),所以这时候就需要服务端集成 了,集成的形式是写http请求,去调用环信restapi,不需要jar ,对语言没限制。
具体怎么做呢?这里我们把注册和登陆提出来重点讲一下
注册的流程:
用户在自己的应用里注册了账号,或者是已经上线的应用里面有了用户,现在需要使用环信im ,就需要服务端调环信rest接口为用户创建对应的im账号,调注册用户接口的时候传入的只有username和pwd,(建议加密过来,比如md5)调接口成功在response里会返回刚注册好的账号,自己服务器需要绑定起来把这个im账号与自己应用的账号对应。
登陆的流程:
用户在客户端登陆的是自己服务器,验证通过去拿到对应的im账号,再在客户端进行一个二次登陆,登陆环信的服务器,建立长连接。
当然,服务端的集成肯定不止这些,还可以对自己应用的im账号和群组进行关联,导出聊天记录等,具体的可以看看rest api文档。


这一期集成指南就介绍到这里吧,大家对环信有什么产品需求或者集成过程有什么建议可以在下方评论里留言。
  收起阅读 »

群组中如何使用@功能?

群组中,@某人的和群组普通消息没有区别,只是针对被@的用户在ui上显示会有不同。可以通过环信的扩展消息来实现。 1.发送方将想要@的人的环信id通过扩展字段放到扩展消息中,并把消息发到群里。 2.群中成员在收到消息时,先检查扩展字段中是否有对应的字段,如果有,...
继续阅读 »
群组中,@某人的和群组普通消息没有区别,只是针对被@的用户在ui上显示会有不同。可以通过环信的扩展消息来实现。
1.发送方将想要@的人的环信id通过扩展字段放到扩展消息中,并把消息发到群里。
2.群中成员在收到消息时,先检查扩展字段中是否有对应的字段,如果有,取出其中的环信id。
3.检查取出的环信id与当前登录的环信id是否一致。
4.如果检测一致,需要在ui上做特殊处理,显示出对应的提示信息,如“[有人@我]“,如不一致,不做处理。
 
发送方具体举例:
iOS:
message.ext = @{@"remindEId":@"6001"}
 
android:
message.setAttribute("remindEId","6001")
然后将消息发送出去。
 
接收方具体举例:
iOS:
-(void)didReceiveMessage:(EMMessage *)message{
    // 获取当前登录用户环信id
    NSString *currentUserId = [[[EaseMob sharedInstance].chatManager loginInfo] objectForKey: kSDKUsername];
    // 被@用户环信id
    NSString *remindEID = [message.ext objectForKey:@"remindEId"];
    if ([remindEID isEqualToString:currentUserId]) {
        // 当前登录用户被@,需要UI做单独处理
    }
}
 
android:
// 获取当前登录用户环信id
String currentUserId = EMChatManager.getInstance().getCurrentUser();
EMMessage message = EMChatManager.getInstance().getMessage(msgId);
// 被@用户环信id
String remindEID = message.getStringAttribute("remindEId")
if(remindEID==currentUserId){
    // 当前登录用户被@,需要UI做单独处理
 } 收起阅读 »

甲骨文收购StackEngine 提升云业务竞争力

12月23日消息,据华尔街日报报道,甲骨文最新的一笔收购虽然规模不大(涉及不到50名员工),但它可谓该公司重塑自我适应快速市场变化过程中迈出的重要一步。该数据库巨头上周五悄然收购了德州奥斯汀的创业公司StackEngine。   甲骨文未披露该交易的条款,但该...
继续阅读 »
12月23日消息,据华尔街日报报道,甲骨文最新的一笔收购虽然规模不大(涉及不到50名员工),但它可谓该公司重塑自我适应快速市场变化过程中迈出的重要一步。该数据库巨头上周五悄然收购了德州奥斯汀的创业公司StackEngine。
 
甲骨文未披露该交易的条款,但该收购将会有利于其具有重大战略意义的公共云业务的发展。

该集装箱运营平台将会促进甲骨文基于集装箱软件的新一代云服务的销售。前谷歌工程师、风投公司Accel Partners入驻创业者乔·布雷达(Joe Breda)指出,集装箱是计算机程序开发方式突变的一部分,给软件开发者带来了在大型网络上快速开发和推出软件的新途径。

该收购可能有助于甲骨文追赶其它在创业公司Docker崛起后十分看重集装箱技术的科技巨头。Docker于2013年年初以开源许可形式开放其集装箱技术,该概念由此开始普及。Docker产品高级副总裁斯科特·约翰斯顿(Scott Johnston)指出,在过去的一年里,包括IBM、VMware、微软和亚马逊在内的云计算提供商都曾推出基于Docker的软件与服务。

开发集装箱管理软件的创业公司Kismatic CEO帕特里克·雷利(Patrick Reilly)说道,“事实上,这些公司都在针对这一趋势进行投资,感觉大公司们都开始意识到该理念不同凡响了。”

甲骨文计划让StackEngine团队留在奥斯汀,其员工将在那里为甲骨文的云计算产品开发新功能。甲骨文云开发副总裁彼得·马格努松(Peter Magnusson)表示,StackEngine打造的集装箱管理软件将应用于甲骨文的集装箱服务,该类服务将“成为企业计算的重要部分”。

StackEngine的技术或许可帮助甲骨文的云服务吸引更多的企业客户。甲骨文在云领域的发展并不顺利,目前它的营收还主要依靠其它的业务。传统软件的许可和产品支持合约占该公司营收的70%。

在最近一个季度,甲骨文的新软件许可销售额同比下降18%。它的云计算业务收入为6.49亿美元,同比增长7%,但该增幅要远远落后于亚马逊。后者的云业务在最近一个季度实现了高达78%的增长。 收起阅读 »

一个IT人的职业规划

今年的校园秋季招聘已经进入尾声,计算机软件行业,作为每年校园招聘的重头戏,都会受到极大的关注。虽然阿里巴巴裁员、百度暂停校招的消息此起彼伏,但依旧无法阻止一大波鲜肉孤注一掷得注入互联网行业。同学们开始关注各大公司的官网,开始一遍遍得修改自己的简历,开始上网搜索...
继续阅读 »
今年的校园秋季招聘已经进入尾声,计算机软件行业,作为每年校园招聘的重头戏,都会受到极大的关注。虽然阿里巴巴裁员、百度暂停校招的消息此起彼伏,但依旧无法阻止一大波鲜肉孤注一掷得注入互联网行业。同学们开始关注各大公司的官网,开始一遍遍得修改自己的简历,开始上网搜索今年的面试经验,开始刷旧的笔试题目…… 
 
找工作的状态总的来说就是迷茫!迷茫!迷茫!实习中的状态总的来说就是心累!心累!心累! 
 
摘客作为贴心的公众号,为正在心累的工作瓶颈的同事们和还在迷茫和学校社会衔接处的同学们答疑解惑。 
 
主讲人李海峰的经历就是一个典型的IT人经历:从信息管理学院毕业,进入软件行业工作,从技术到管理层,又从管理层做精准技术,其中不断提升自己的英语水平,金融知识水平,自学机器学习。 
 
李海峰在自己的见解上介绍了计算机行业最基本的工作:开发/测试/PM的具体工作,也介绍了时下最容易入门,最热门的计算机语言。 
 
对于同学们的提问,李海峰都进行一一解答。作为一个成功的技术人和管理者,李海峰最大的收获就是要在每一次的转型中努力寻找到自己所喜欢做的事情,学习的过程也是以点成线,以线成面。 
 
最后借用王国维《人间词话》的内容来总结海峰分享给大家的学习之道: 
“古今之成大事业、大学问者,必经过三种之境界: 
‘昨夜西风凋碧树。独上高楼,望尽天涯路。’此第一境也。 
‘衣带渐宽终不悔,为伊消得人憔悴。’此第二境也。 
‘众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。’此第三境也。” 
 
 以下为李海峰演讲实录: 
 
大家晚上好,我是来自浙江网新恒天软件有限公司大数据项目组的技术负责人李海峰,今天由我给大家分享一个IT人的职业生涯规划。 我从信息管理学院毕业,进入软件行业工作,从技术到管理层,又从管理层做精准技术,其中不断提升自己的英语水平,金融知识水平,自学机器学习。在过程中我就是在不断得寻找自己喜欢的东西,并且坚持做下去。 
 
接下来我来讲讲一个软件公司最多的职业,开发。对开发的要求,不外乎下面几个:具有完整设计一个中型项目的能力;深入理解其所使用工具的原理,熟练使用各种标准类库或第三方类库;独立完成多个项目或多个Release的核心模块;代码质量优异,Debug能力强;系统设计和开发中具备很强的创新能力;具有带领团队进行技术攻关的能力。在刚刚进入公司的时候,我建议大家至少要熟练掌握一种语言,多学习。正常的情况下,用3年的时间就可以把自己锻炼成一个高级软件工程师。 
 
然后是相对较多的一个职业,测试。对测试的要求是这样的:具有一定的测试规划和管理能力;深入理解软件测试过程,具有制定和改进软件测试过程的能力;具有较强的代码、文档审查能力;深入理解各种软件测试工具,并能熟练应用;对软件度量有一定的理解,具有较强的软件测试分析和设计能力;具有带领团队进行相关测试工作的能力。所以测试需要有一定的文档的编辑能力,同时需要具有比较好的交流能力,因为需要和客户方进行软件功能上的交流。 
 
大家应该都很关心什么时候可以做一个program manager,但做PM的要求不仅像做一个开发工程师,首先需要可以搞定客户,对需求进行分析,制定实现方案,对项目的预期管理,从接触到合同到上线都要全程把关;同时要管理团队,包括对一个团队的组建,调节团队氛围,在成员出现矛盾时做一个救火队长。作为一个PM必须要做到的是先做人,再做事! 
 
 接下来给大家分享一下现在比较热门的语言:
 | Java 仍然是主流,应用越来广泛,非常适合作为基础语言学习。强烈推荐大家学习
 | Python 胶水语言,各个语言的粘合剂。在大数据时代也是数据分析基础语言,如对数据分析挖掘感兴趣,推荐学习。 
 | PHP 网站快速开发的不二选择,在互联网+创业的高潮,PHP人才的薪水是涨幅最快的。
 | JS 前端语言,HTML5未来有更广阔的应用空间,对前端有兴趣的尽量学好。 
 | OC 只能应用在苹果平台,比较局限,建议慎重选择。 
 | Scalla 函数式编程的代表语言,spark上的主流开发语言。不过只能打辅助,不能当Carry,可以做为学好Java后的一个补充。
 | Shell 脚本语言,是Linux平台开发必备技能。大牛都要掌握一两门脚本语言的哦~ 
 
总结来说,我觉得大家都不需要太着急,不要纠结于,我到底应该学些什么,我什么时候才能变成一个牛人。以前我每天爬山背单词,也不知道自己的英语究竟在什么水平了,但有一天碰到一个老外,才知道自己的英语原来已经这么溜了!所以其实,当你在实践中,逐渐得学习了很多东西,当你回过头来看的时候,就会发现自己已经站在很高的地方了!  
最后借用人生的三个境界来分享我的学习心境: 
最开始是迷茫的:昨夜西风凋碧树。独上高楼,望尽天涯路。 然后找到了自己喜欢的东西:衣带渐宽终不悔,为伊消得人憔悴。 接着就可以找到一些喜悦:众里寻他千百度,蓦然回首,那人却在,灯火阑珊处。 
 
 谢谢! 
 
 下面是一些问答实录:
 
 1、对于非IT专业的在IT界发展有啥建议吗?
 
 首先找准职业方向,可能会走很多弯路,再找方向的阶段多听专业人的意见,找到适合自己的 ,然后沉下来学习即可。最重要的是好好提高编码能力,要从基础开始,从java基本语法开始,扎实基本功,IT是最不问出生的行业,无论是什么专业,只要有技术都能找到好工作。
 
 2、30岁后程序员转哪些职业? 
找到自己奋斗的方向,就不会觉得累也不会没有目标。找目标的过程很重要!我找到的就是大数据方向,对我来说为伊消得人憔悴,我不会转型,会朝这个方向一直努力。 
 
3、大数据应该学哪些东西?是不是对算法要求比较高? 
 
首先,优酷上找一些视频,如R语言的视频教程;然后学习公开课;《数据挖掘概念与技术》等机器学习方面的书;读机器学习方面的博客,一篇好的博客可以写一个算法。主要是借助网络来学习。《数学之美》把数学讲的很通俗,机器学习必看。 
 
 4、Java的基本功能书有推荐吗? 
《thinking in Java》。 JDK源代码,tomcat源代码,还是要多看源代码,面试的时候很有用。
 
 5、如何管理你的知识? 
技多不压身,不需要管理。 
 
6、追求技术的同时,怎么看待薪水和现实之间的矛盾?
 薪水和现实之间是没有矛盾的,前几年不要对薪水有太高的期望,够生活,够自己花就好了,三年后你奋斗成高级工程师后再想着攒钱买房这些事情,所以我认为只要好好学技术就好,不会有太大的矛盾。 收起阅读 »

环信推反垃圾服务,八大技术为用户保驾护航

那些年,用户被虚假广告塞的坛满钵满;那些年,网民被欺诈信息骗的倾家荡产;那些年,色情图片搞得社区难以正常发展;那些年,反动信息让平台面临关停的风险。据《中国网民权益保护调查报告(2015)》显示,2015年网民因个人信息泄露、垃圾信息、诈骗信息等现象,总体损失...
继续阅读 »
那些年,用户被虚假广告塞的坛满钵满;那些年,网民被欺诈信息骗的倾家荡产;那些年,色情图片搞得社区难以正常发展;那些年,反动信息让平台面临关停的风险。据《中国网民权益保护调查报告(2015)》显示,2015年网民因个人信息泄露、垃圾信息、诈骗信息等现象,总体损失高达805亿元。同时,垃圾信息也极大影响互联网产品用户体验,如果不加以控制,只能眼睁睁地看着活跃度下降用户不断流失。


16-15122111201L10.jpg


诈骗垃圾消息示例
 
 互联网企业因此面临着各方面巨大的压力,为此不得不投入大量的资源。大型互联网公司都有专门的反垃圾研发团队,人数动辄数人甚至数十人。但大多数中小型互联网创业公司资源有限,很难专门建立自己的反垃圾团队。在美国,已经有多家创业公司以云服务或其他形式提供反垃圾服务。而在中国,反垃圾市场缺口巨大,但目前还没有第三方的专业反垃圾服务提供商。


16-151221112029239.jpg


广告、色情垃圾消息示例
 
为了维护平台以及网民的合法权益不再遭受垃圾信息的侵害,作为全球最大的即时通讯云PaaS平台,环信率先推出了反垃圾服务
 
 
一,反垃圾服务“Anti-spam”面临六大挑战

反垃圾服务“Anti-spam”是一项长期并且艰巨的工作,面临以下六大挑战:
1)1:n,一个anti-spam团队要面临看不见的千千万万的spammer(人或者机器);
2)spammer利益丰厚,已经形成完整的地下黑色产业链;
3)spam的成本越来越低,打码网站(第三世界打码平台),万能的淘宝(IP代理库,手机黑卡,马甲账号)等;
4)spam质量越来越高,软文与正常用户的文章差异越来越少;
5)anti-spam是拉锯战,学术界和工业界多采用机器学习的方法进行,但只要产品有流量,spammer会坚持不懈地研究策略和规则来绕开设定的屏障。anti-spam与其说是machine learning,不如说是adversarial learning;
6)anti-spam在大多数公司都是一个黑盒,为了保护自己,核心技术很少会拿出来公开交流。


二,环信反垃圾“anti-spam”服务八大技术让垃圾消息无所遁形

    环信作为国内即时通讯云行业的开创者,有着连接人与人,连接人与商业的愿景。不仅致力于为用户提供高稳定高可靠的底层消息服务,更致力于帮助用户不断优化产品打造更好的用户体验,从而实现双赢。在反垃圾服务方面,环信anti-spam团队将通过以下技术力图识别恶意机器程序,将用户spam控制在可接受的范围内。


16-151221112110Z5.jpg


环信反垃圾服务架构图
 
1、关键词过滤系统,主要用来过滤非法政治言论以及部分色情信息;
2、基于行为分析的spammer识别系统,借助互联网用户行为的特征进行spammer识别;
3、恶意内容样本自学习系统。基于内容的spammer识别系统,通过训练,指针对用户的内容数据做判断,从语义的角度分析spam的类别;
4、实时策略部署,紧急帮助用户拦截临时爆发的spam;
5、用户产品指导等(注册马甲拦截,活动刷单等); 

    在上述anti-spam过程中,环信将会对用户的用户ID做匿名处理,充分保护用户隐私。并且将使用高效、准确的机器学习模型进行智能识别。

    未来,环信还将部署以下三大技术: 1、消息恶意代码检测,xss等潜在的恶意攻击;2、恶意URL检测,钓鱼网站等虚假URL检测; 3、语音,图片,视频等spammer智能识别系统;


三,环信反垃圾“anti-spam”增值服务流程简介:

1、环信反垃圾服务属于增值服务,将面向所有互联网企业,同时将优先向环信即时通讯云和环信移动客服老用户开放。
2、企业可以联系环信商务申请开通反垃圾服务。
3、环信反垃圾团队将跟企业沟通,了解用户垃圾消息的定义,商定垃圾消息的处理流程。
4、对企业的数据匿名处理,训练模型,上线服务。
5、环信将不断改进模型,提高准确率和召回率,同时帮助企业应对临时事件。

    截至12月份,已经有数十家环信老用户试用开通了环信反垃圾服务。某知名女性社交App在申请试用环信反垃圾服务以后,环信选择其数据使用环信行为识别系统进行识别,发现该App目前的垃圾消息占比高达40%,其中,垃圾消息主要分为非法广告和虚假兼职两类,比例为9:1,极度影响用户体验。

    通过环信行为检测系统,垃圾消息监测准确率高达99%,垃圾消息召回率高达82%。该社交产品负责人表示:“环信反垃圾服务上线后用户活跃度获得了明显提升,以后再也不用为各种色情、政治类消息提心吊胆了。”

    环信反垃圾服务将于近期正式对外开放申请,具体请联系环信商务或者关注环信官网(http://www.easemob.com/)更新。

    名词解释:召回率,是机器学习的评判指标之一。举个例子,现在某App有1000条消息,其中300条是垃圾消息,通过系统识别出了240条我们“算法认定”的垃圾消息,经过人工鉴定,这240条垃圾消息就是样本中的垃圾消息,那么算法的召回率是240/300=80%,算法准确率是100%。因为算法是一个学习的过程,所以会漏掉一些垃圾消息用作学习成本。
  收起阅读 »

webim自动登录 多网页共存 输入框空消息判断

集成环信webim ,发现需要输入账号密码才能登陆,如果需要实现打开页面就直接登陆该怎么办? 以webdemo为例(demo下载地址http://www.easemob.com/downloads),打开index ,demo 的登陆是写了一个login...
继续阅读 »
集成环信webim ,发现需要输入账号密码才能登陆,如果需要实现打开页面就直接登陆该怎么办?

以webdemo为例(demo下载地址http://www.easemob.com/downloads),打开index ,demo 的登陆是写了一个login方法调用的conn.open。只需要在这个方法前写一个页面加载函数 ,直接调用login就可以实现我们需要的打开页面登陆功能
 

QQ截图20151221173013.png


就是这么酷炫,小伙伴们快去试一下吧!
 
接下来我们来看第二个问题,怎么实现多网页共存?还是老样子,先讲一下原理吧,
-conn.open的时候多传递参数,resource:随机值,每个tab中随机值不能一样,一样则会把前一个登录的踢出,默认都是webim
如果觉得看不懂也没关系,这个在环信webimsdk里已经写好了的,打开easemob.im-1.0.7.js 
把截图里圈出来的地方注释打开,下面resource_value注释掉(这下是不是明白了呢)


QQ截图20151221173151.png


 
 
在使用webdemo 的时候发现打开会话界面不输入消息,点击发送按钮(也就是发送一条空消息)这样会导致后续发不出去消息,这个应该怎么处理呢?
 
把这个判断注释掉即可


QQ截图20151221173324.png


 
上面说的三个技能都掌握了吗?附件为一份简单集成的demo,可以拿去参考下。

有关于环信集成问题或者想了解哪方面移动互联网知识,可以在下方评论留言,我们就计划更新出来。
  收起阅读 »

关于视频聊天的问题

我在集成了EaseUI3.0后,发现对于视频聊天的请求,如果用户选择了不给权限,那么虽然应用会提示我们去隐私-》相机然后后找到相应的应用打开权限,但我测试发现在列表中并没有列出来我们的app。经过查找,感觉问题在CallViewController.m的can...
继续阅读 »
我在集成了EaseUI3.0后,发现对于视频聊天的请求,如果用户选择了不给权限,那么虽然应用会提示我们去隐私-》相机然后后找到相应的应用打开权限,但我测试发现在列表中并没有列出来我们的app。经过查找,感觉问题在CallViewController.m的canVideo方法。这里对能否访问摄像头进行了判断,但没有尝试去获取权限。因此我将代码修改为如下内容。完美解决这个问题。
+ (BOOL)canVideo
{
if([[[UIDevice currentDevice] systemVersion] compare:@"7.0"] != NSOrderedAscending){
// if(!([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo] == AVAuthorizationStatusAuthorized)){\
// UIAlertView * alt = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"setting.cameraNoAuthority", @"No camera permissions") message:NSLocalizedString(@"setting.cameraAuthority", @"Please open in \"Setting\"-\"Privacy\"-\"Camera\".") delegate:self cancelButtonTitle:nil otherButtonTitles:NSLocalizedString(@"ok", @"OK"), nil];
// [alt show];
// return NO;
// }
NSString *mediaType = AVMediaTypeVideo;// Or AVMediaTypeAudio
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
NSLog(@"---cui--authStatus--------%d",authStatus);
// This status is normally not visible—the AVCaptureDevice class methods for discovering devices do not return devices the user is restricted from accessing.
if(authStatus ==AVAuthorizationStatusRestricted){
NSLog(@"Restricted");
}else if(authStatus == AVAuthorizationStatusDenied){
// The user has explicitly denied permission for media capture.
NSLog(@"Denied"); //应该是这个,如果不允许的话
UIAlertView * alt = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"setting.cameraNoAuthority", @"No camera permissions") message:NSLocalizedString(@"setting.cameraAuthority", @"Please open in \"Setting\"-\"Privacy\"-\"Camera\".") delegate:self cancelButtonTitle:nil otherButtonTitles:NSLocalizedString(@"ok", @"OK"), nil];
[alt show];
return NO;
}
else if(authStatus == AVAuthorizationStatusAuthorized){//允许访问
// The user has explicitly granted permission for media capture, or explicit user permission is not necessary for the media type in question.
NSLog(@"Authorized");

}else if(authStatus == AVAuthorizationStatusNotDetermined){
// Explicit user permission is required for media capture, but the user has not yet granted or denied such permission.
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
if(granted){//点击允许访问时调用
//用户明确许可与否,媒体需要捕获,但用户尚未授予或拒绝许可。
NSLog(@"Granted access to %@", mediaType);
}
else {
NSLog(@"Not granted access to %@", mediaType);
}

}];
}else {
NSLog(@"Unknown authorization status");
}
}

return YES;
}
收起阅读 »

集成笔记:环信移动客服如何实现全媒体接入

本篇集成笔记参考于环信官网移动客服集成文档 ,写这篇笔记是为了让大家更直观的了解“移动客服是什么”“我能不能使用移动客服”“怎么集成移动客服” 官网移动客服集成文档地址:http://docs.easemob.com/cs/start 移动客服是由环信提供...
继续阅读 »
本篇集成笔记参考于环信官网移动客服集成文档 ,写这篇笔记是为了让大家更直观的了解“移动客服是什么”“我能不能使用移动客服”“怎么集成移动客服”

官网移动客服集成文档地址:http://docs.easemob.com/cs/start

移动客服是由环信提供的一套完整的客服服务SaaS解决方案,既包括多渠道接入的客户端(APP,网页,微信,微博),也包括客服座席使用的服务端(Web和APP都有),只是在APP端使用了IM作为通讯方式。
访客端是由自己集成(集成步骤在下面文档会写出),目前支持:APP、微信公众号、微博公众号、网站
客服后台是由环信提供,现支持:移动端、web、pc端(插件方会在以后版本开放,具体可咨询环信在线技术支持)


QQ截图20151221160139.png


集成环信移动客服前需注册环信客服账号,并登录。......
环信客服注册地址:http://kefu.easemob.com/mo/register


网页插件集成:
网页插件集成移动客服是比较简单,原理是在自己网站的标签之前加入一段由环信提供的js,即可完成环信在线客服的植入,实现访客在网页端与在线客服的沟通。

QQ截图20151221160416.png


这段js获取步骤如下:
管理员模式下==》渠道设置==》网页插件

QQ截图20151221160522.png


下面来讲下网页插件的功能介绍和自定义开发(V4.1版本)
功能介绍可参考imgeek社区新产品发布:http://community.easemob.com/article/825307446 

一些常见的自定义开发
Q:网页插件 集成自定义按钮
A:首先,将插件JS串中的hide=false修改为hide=true;
其次,在你网页的目标元素上添加如下超链接,例如:你的目标元素,即可完成自定义客服按钮
Q:网页插件区分技能组
A:http://kefu.easemob.com/webim/im.html?tenantId=xxxx&emgroup=技能组名称
注意:技能组名称外面没有单引号


手机APP集成:
手机APP集成可分为以下两步
1:先在客服后台创建一个关联
管理员模式下==》渠道设置==》手机APP 右上角添加环信关联
关联名称由自己定义,AppKey,ClientId,ClientSecret可以再环信开发者后台拿到(注册环信开发者账号https://console.easemob.com/index_register.html  注册完成创建应用--查看应用概况即可得到)
这里详细介绍下im服务号,im服务号为自己应用下的一个im账号(不用为每个用户创建,根据自己业务逻辑指定一个或者多个)
2:客户端写个联系客服按钮,点击联系客服,调用发消息方法,接收对象为客服后台创建的关联的im服务号,消息就会到客服后台,根据分配策略分配给对应的客服(V4.1版本 客服坐席分配策略为:max(客服坐席可接待人数-客服坐席已接待人数), 也就是说,新来会话会分配给理论最闲的那个客服坐席)

一些常见的自定义开发:
指定客服、技能组、满意度调查、显示用户信息可参考(原理为联系客服时通过传入扩展属性键值对,由客服后台解析辨别。键为环信实现约定好的,值是自定义。)文档地址:http://docs.easemob.com/cs/300visitoraccess/10nativeapp 


这里列举几个实现用户信息常遇到的问题
Q:已经按照文档传入用户信息,为什么客服后台还是没有看到用户信息
A:客服关闭会话,再次联系客服就行,已经建立的会话目前没有刷新访客昵称
Q:之前传入用户信息在客服后台显示了,后来传入新的用户信息为什么客服后台没有更新
A:已经在客服后台显示的用户信息不会随着扩展属性的更新而更新,目前的设计是访客第一次进入时展示信息给客服辨别,后续由客服手动备注
PS:如果自己APP 是基于h5开发的,或者不想集成环信im怎么使用环信的移动客服,可以参考之前的网页插件集成,将那段js中的tenantID填入以下网址中xxxx的位置:http://kefu.easemob.com/webim/im.html?tenantId=xxxx
在自己APP中点击联系客服跳转到这个H5会话窗口即可


微信公众号集成:
微信工作号集成需要注意的是个人的订阅号、测试公众号等,微信均没有开通客服接口的权限,所以粉丝收不到客服消息。
微信公众号集成分以下几类:
1:直接授权(通过在客服后台管理员模式==》渠道设置==》微信公众号 添加微信公众号即可)
需要注意的是授权以后自己之前对微信公众号所做的开发都不能使用,微信用户通过微信的输入框直接联系客服后台移动客服
2:通过定义菜单栏点击联系客服

QQ截图20151221160626.png


可以在微信公众号定义菜单栏,点击跳转到环信指定的网址(这个地址可参考之前的网页插件集成,将那段js中的tenantID填入以下网址中xxxx的位置:http://kefu.easemob.com/webim/im.html?tenantId=xxxx )
3:回调集成(需要开发者,涉及到服务端程序的集成)
实现原理为环信发消息给微信,是通过环信的实时消息旁路接口。微信发消息给环信,是通过环信提供的REST发消息接口。
按照以下步骤:
1)在微信公众号开发者设置第三方服务器接收微信公众号的消息进行业务逻辑处理
2)调用环信rest接口将消息发给客服后台绑定的IM服务号(文档地址http://docs.easemob.com/doku.php?id=start:100serverintegration:50messages)
3)客服的回复消息是到环信服务器,通过回调接口把这消息转发到用户指定的服务器(需要符合环信格式,配置消息回调可联系环信在线技术支持)
4)服务器拿到消息之后在推给微信粉丝就行
PS:需要注意的是消息格式的变化(V4.1视频和位置暂不⽀持,会在下⼀版本提供⽀持)


微博公众号集成:
微博公众号集成目前支持私信、 @ ,评论暂不支持(V4.1版本)
集成方式:
管理员模式下==》渠道设置==》微博公众号


QQ截图20151221160725.png


 

  收起阅读 »

赞赏提现功能已经ready , 收到赞赏的同学可以申请提现了,真金白银,童叟无期

终于 ,我们可以愉快的打赏了。      
终于 ,我们可以愉快的打赏了。  


1.jpg




2.jpg


 
 

艾媒咨询(iiMedia Research ): 2015中国移动客服市场发展研究报告

话不多说,要下载的速度啦!


QQ截图20151216120731.jpg




QQ截图20151216120743.jpg




QQ截图20151216120759.jpg


话不多说,要下载的速度啦!

”活动“模块上线,大家有地儿活动啦

有没有同学注意到, imgeek“活动”模块悄然上线啦, imgeek将定期组织线下技术活动,区别于众多的活动站,imgeek将遵循开源社区开放、自由、分享的精神,具有几个特点:   1.开放演讲申请,任何人都可以提交演讲 2.开放投票,你的投票将直接决定演讲...
继续阅读 »
有没有同学注意到, imgeek“活动”模块悄然上线啦, imgeek将定期组织线下技术活动,区别于众多的活动站,imgeek将遵循开源社区开放、自由、分享的精神,具有几个特点:
 
1.开放演讲申请,任何人都可以提交演讲
2.开放投票,你的投票将直接决定演讲顺序
3.开放赞助商申请,有钱出钱,没钱出礼品,同时imgeek给一些品牌露出的机会
4.开放志愿者申请,给虽然暂时不能登上讲坛,又热心参与活动的同学一些贡献的机会,每个志愿者都将得到一个精美纪念品
5.开放“向讲师提问”,你的提问同时将进入imgeek“问题”模块,不光讲师可以及时回复你的问题,更多的同学将会看到
6.如果你还没有imgeek社区账号,报名后将为你自动生成一个社区账号,省去频繁注册账号的麻烦
 
总之,活动是大家的,舞台是大家的,尽情参与吧!
 
最近的一期活动将在2016-01-09 13:30 举行, 大家记得报名哦
 
活动入口:点击顶部“活动”菜单 or http://www.imgeek.org/activity/
 
 
 
  收起阅读 »

登录,大概3秒多,感觉有点慢,能优化下吗

登录,大概3秒多,感觉有点慢,能优化下吗
登录,大概3秒多,感觉有点慢,能优化下吗

小众公众号的简单运营

我们从4月初开始运营一个新的公众号。目前一直在持续运营中,粉丝数量和粉丝关注度也保持稳步上升,每天分享的文章也有很多的好评,得到转发。同时也收到过很多的吐槽,但我们一直很关注粉丝的反馈和吐槽,毕竟这才能让我们做的更好。 但是公众号运营的痛点太多了。粉丝增...
继续阅读 »
我们从4月初开始运营一个新的公众号。目前一直在持续运营中,粉丝数量和粉丝关注度也保持稳步上升,每天分享的文章也有很多的好评,得到转发。同时也收到过很多的吐槽,但我们一直很关注粉丝的反馈和吐槽,毕竟这才能让我们做的更好。

但是公众号运营的痛点太多了。粉丝增长不够快,用户粘性不够大,文章阅读数量不够多,传播力度不够广。对于一个公众号来说,不论粉丝多还是少,热情还是不热情,都需要运营人员精心的照料。今天我不说怎么推广这个公众号,只从自己经营这个公众号以来得到的一点经验来分项怎么做一个公众号。
 

  • 公众号建设

  • 首先公众号要有一个自己的形态,现在分订阅号、服务号、企业号三种,最常见的,也是我正在做的就是订阅号,我的号也是提供了公众号最基础的服务,信息推送。



  • 1.不可忽视的自动回复内容:



  • 自动回复有下面几种情况:被添加的内容、自动回复内容、特定关键词回复内容






  • 被添加的自动回复是一个用户对你的公众号的第一次接触,如果写的过长或者过短都会影响用户对公众号的认知。如果是“你好,欢迎关注***”,那么用户只会觉得,你没有用心在经营这个号,如果长篇大论得说你的产品特色,也容易让人厌倦。所以建议是,先打招呼,再简单一句话介绍公众号的功能。






  • 自动回复,根据微信公众号的规则,在1小时内自动回复只会被出发一次,所以这块建议就是写上,已收到,会尽快回复之类的字眼就可以。






  • 关键词自动回复,这块就比较灵活。我的公众号在做信息查询的时候会常用到这个。一般也不需要设置关键词回复。特别的可以提示的是,比方说有活动这类的内容,那么关键词可以设置一个“活动”,方便用户找到想要的信息。






  • 2.让信息更有条理的菜单栏设置


 

  • 很多小微公众号会忽视菜单栏的设置,但是其实菜单栏是必不可少的。公众号的特点是每天只能建立一条群发,这就容易导致,万一添加的用户是在你群发之后,或者今天用户不巧没有收到你的信息(这个情况还是很少见),那就会造成进入后,没有直接沟通交流的机会,很尴尬,所以如果你的公众号设置一个简单的菜单,让新用户,或者是想了解更多的用户有查看的平台。






菜单栏的作用.png


  • 每日推送内容

  • 每日推送也是公众号最重要的内容。我们的公众号是做IT资讯推荐的,所以,我们每天有基本的模式。






  • 如果文章分2、1,其中2为引流文章,让用户看了愿意分享的,1为增强用户知识体系,看了愿意不停关注来提升自己的能力。选文章的前提是精和新。



  • 每天的基本模式:精选2+技术干货1+大数据1+互联网金融1+o2o1+电商1+产品经理1






  • 其实在固定模式下推送新闻,不用多久就能发现用户比较关注的焦点。而且推送内容的走向也和时间有一定联系。由一周的统计可以看出秋季招聘期间面试经验、干货内容是比较受关注的,双十一期间电商和创业的关注度也比较高。



  • 169  【面试经验】腾讯、百度、网易游戏、华为Offer及笔经面经



  • 151  【技术干货】Google开源最新机器学习系统TensorFlow



  • 146  【面试经验】产品经理面经:3个产品Offer是如何拿到的



  • 153  【面试经验】零产品经验斩获校招产品Offer经历



  • 146  【面试经验】给Android程序员的一些面试建议



  • 190  【技术干货】分类算法总结



  • 153  【创业】2015年的互联网创业趋势可能不是你想象的那么回事



  • 147  【电商】双11这天,90%程序员都在干什么?



  • 143  【产品】别老想着脱单,今年流行“性冷淡”产品



  • 145  【创业】曾穷到被女友抛弃的他投奔马云后身价102亿



  • 177  【创业】那家将融10亿美元的AR公司Magic Leap到底有多拽



  • 162  【社会】人与人之间变得更冷漠,和手机一毛关系都没有



  • 以上是根据一周的统计内容得到最关注的焦点

  • 内容编辑

  • 标题:无所谓标题党,但标题请简洁。太长的标题,在碎片化信息时代,容易令人厌倦。



  • 摘要:你可能会觉得摘要没有用,编辑内容的时候我们总是会忽视摘要的部分,而在转发的时候摘要是必须会出现的,这时候如果摘要不编辑好,内容就会很乱,也影响转发的效果。转发的时候也别空转发,稍微写点内容,自己的看法,会更吸引其他用户的注意。







让转发更好看.png

     摘要写好了:


摘要写好了.png

     摘要没写好:


摘要没写好.png


  • 文字:分段,分段,分段!文字必须分段,不分段的手机效果就是挤在一起,令人不想看。字号的大小,新闻类的文章建议16px,这样看起来最舒服;文艺小清新类的文章建议14px,文艺范儿的小一点反而轻巧。



  • 图片:文章最好能够图文并茂,图片居中看起来会舒服很多。



  • 段落:用编辑器加一些看起来更简洁的小图标,对整篇文章的帮助是很大的。






分段小图标.png





  • 找对人群

  • 公众号相当于一个产品,所以找准定位对于找对一群适合的人群很重要。对的人群指的应该不是一次突然骤增的人数,而是长期用户的保留。




做一次校园IT推广的强度.png

     我们找准定位为正在找工作的大学生之后在做推广,效果就出现了非常显著的高峰。

  • 最重要的小事

  • 推送时间:



  • 推送时间真的非常重要,甚至是一篇相同的文章,不同的时间推送,阅读量和转发量也会大不相同。之前一直在探索最佳的推送时间,得到一个基本觉得很靠谱的结论。



  • 最忌:中午推送



  • 一般:早晨



  • 最佳:晚上8点-9点之间


收起阅读 »

环信登录提示 invalid user or password

接入环信的时候,遇到登录的问题。用环信demo注册的用户,在自己注册的appkey中是无法登录的。需要自己去注册的管理后台添加对应IM用户才能登录。在此记录一下,希望对遇到同样问题的朋友有帮助。
接入环信的时候,遇到登录的问题。用环信demo注册的用户,在自己注册的appkey中是无法登录的。需要自己去注册的管理后台添加对应IM用户才能登录。在此记录一下,希望对遇到同样问题的朋友有帮助。

imGeek社区上线现金打赏功能,拥抱共享经济开启单点技术问题众包新模式

(下面是我们PR写的新闻稿,高大上吧)  一提到共享经济必定会提到Uber和Airbnb,这两只巨无霸独角兽的崛起让“共享经济”一词异常火热,作为共享经济最具代表性的两家企业,Uber和Airbnb分别为出租车业和酒店业带来了革命性的改变,也让人们看到了共享...
继续阅读 »
(下面是我们PR写的新闻稿,高大上吧)

 一提到共享经济必定会提到Uber和Airbnb,这两只巨无霸独角兽的崛起让“共享经济”一词异常火热,作为共享经济最具代表性的两家企业,Uber和Airbnb分别为出租车业和酒店业带来了革命性的改变,也让人们看到了共享经济在未来的巨大潜力。共享经济这种新的经济模式并不只会在出租车业和酒店业发挥作用,利用人们业余时间和空间的特点,它几乎可以渗透到各个行业,当然移动开发市场也首当其冲。
 

1449803054986.jpg


偶们imGeek开发者社区(http://www.imgeek.org)已悄然上线现金打赏功能,在所有问题回复的下侧悄然出现了一个“赞赏”图标。这个是imGeek开发者社区在共享经济浪潮中的一次尝试,希望借助开发者的力量帮助开发者,人人为我,我为人人。

“赞赏”功能一经推出,受到了开发者的热烈追捧,同时不少论坛KOL们已经陆续收到不少“赞赏”,少则一元,多则上百元,虽然钱数总额不多,但能激发人类最原始的互助潜能,也是对知识的一种尊重。正如打赏时弹出的“知识价值的认可,源自您的赞赏”,正是开发者之间相互的认可,促进了社区中提出的问题可以得到及时的回复。据观察,imGeek开发者社区上面的提出的问题基本在10分钟以内即可得到回复,一个问题基本可以很快的得到解决。

在共享经济的浪潮中也催生了软件服务行业众包模式。其中,Coding,猿团因为众包分别得到了资本的青睐,开源中国更是凭借众包概念上了新三板。 但笔者认为,软件行业的众包并不是一个新生事物,软件项目的外包失败大多是因为项目发包方过于强势,需求变化频繁,众包并不能真正解决这些问题。

但如果将一个项目分解为一个个技术问题, 通过对单点技术问题进行众包,每次付费的金额少,笔者认为imGeek开发者社区这种运营做法是更易操作的方式。 现在imGeek开发者社区的“赞赏”功能还仅是依赖开发者志愿进行,并不强制,这样的操作既照顾到社区的自发性,也为那些乐于分享的勤恳开发者提供了高于社区荣誉的经济收益。

基于互联网的共享经济目前来看已经大大改变了不少行业的格局,相对传统行业,它们拥有更大的优势:1,更节约的时间。2,更优化的资源配置。3,更灵活的就业。也许不久的将来,imGeek开发者社区将在共享经济和众包的浪潮中趟出一条新路。

据悉,imGeek开发者社区是由环信赞助,截止2015年5月,环信服务了23763个App以及几万开发者,环信的创始团队均是世界著名开源项目的贡献者,运营总监是原开源力量社区的创始人,相信正是由于他们对移动开发者的深度了解,促使imGeek开发者社区将逐步发展成为移动开发者们的核心聚集地。 收起阅读 »

推荐算法和机器学习入门

互联网是什么? 马云说:“很多人还没搞清楚什么是PC互联网的时候,移动互联网来了,我们还没搞清楚移动互联的时候,大数据时代又来了。” 大数据又是什么? 有人说,从庞杂的数据背后挖掘、分析用户的行为习惯和喜好,找出更符合用户“口味”的产品和服务,就是大数据...
继续阅读 »
互联网是什么?

马云说:“很多人还没搞清楚什么是PC互联网的时候,移动互联网来了,我们还没搞清楚移动互联的时候,大数据时代又来了。”

大数据又是什么?

有人说,从庞杂的数据背后挖掘、分析用户的行为习惯和喜好,找出更符合用户“口味”的产品和服务,就是大数据。

且听浙江网新恒天大数据技术负责人李海峰为大家分享的课程。
课程视频:
推荐算法和机器学习入门介绍

<烟波浩渺,算法为王>
<他山之石,可以攻玉> 收起阅读 »

Java实现环信服务器端接口,需要的联系我qq1138789752

本人花了1周实现本测试了目前环信服务端所有的接口,需要的代码克联系我qq1138789752,接口列表如下: import java.io.File; import com.liaozi.imhere.entity.TalkMsg; import com.li...
继续阅读 »
本人花了1周实现本测试了目前环信服务端所有的接口,需要的代码克联系我qq1138789752,接口列表如下:
import java.io.File;
import com.liaozi.imhere.entity.TalkMsg;
import com.liaozi.imhere.entity.TalkNode;
public interface TalkDataService {
    /**
     * 登录环信平台
     * @param username 注册账户
     * @param password 登录密码
     * @param nickname 用户昵称
     * */
    public TalkNode login(String username, String password) throws Exception;
    /**
     * 退出环信平台
     * @param username 注册账户
     * */
    public TalkNode logout(String username) throws Exception;
    /**
     * 添加用户信息(单个)
     * @param username 注册账户
     * @param password 登录密码
     * @param nickname 用户昵称
     * */
    public TalkNode userSave(String username, String password, String nickname) throws Exception;
    /**
     * 添加用户信息(批量)
     * @param username 注册账户
     * @param password 登录密码
     * @param nickname 用户昵称
     * */
    public TalkNode userSave(String[] username, String[] password, String[] nickname) throws Exception;
    /**
     * 删除用户信息(单个)
     * @param username 注册账户
     * */
    public TalkNode userDrop(String username) throws Exception;
    /**
     * 删除用户信息(批量)
     * @param size 当前页数
     * */
    public TalkNode userDrop(Long size) throws Exception;
    /**
     * 启用禁用账户
     * @param username 注册账户
     * */
    public TalkNode userModifyAccess(String username, Boolean access) throws Exception;
    /**
     * 修改用户密码
     * @param username 注册账户
     * @param password 登录密码
     * */
    public TalkNode userModifyPassword(String username, String password) throws Exception;
    /**
     * 修改用户昵称
     * @param username 注册账户
     * @param nickname 用户昵称
     * */
    public TalkNode userModifyNickname(String username, String nickname) throws Exception;
    /**
     * 获取用户信息
     * @param username 注册账户
     * */
    public TalkNode userGet(String username) throws Exception;
    /**
     * 获取用户信息
     * @param size 当前页数
     * @param cursor 当前游标
     * @param start 开始毫秒
     * */
    public TalkNode userList(Long size, String cursor, Long start) throws Exception;
    /**
     * 判断用户状态
     * @param username 注册账户
     * */
    public TalkNode userLine(String username) throws Exception;
    /**
     * 获取所属群组(好友群)
     * @param username 注册账户
     * */
    public TalkNode userGroupList(String username) throws Exception;
    /**
     * 获取所属群组(聊天室)
     * @param username 注册账户
     * */
    public TalkNode userRoomList(String username) throws Exception;
    /**
     * 获取消息数量(离线)
     * @param username 注册账户
     * */
    public TalkNode userMessageCount(String username) throws Exception;
    /**
     * 获取消息状态(离线)
     * @param username 注册账户
     * @param id 消息编号
     * */
    public TalkNode userMessageLine(String username, String id) throws Exception;
    /**
     * 添加好友账户
     * @param username 注册账户
     * @param friend 好友账户
     * */
    public TalkNode friendSave(String username, String friend) throws Exception;
    /**
     * 删除好友账户
     * @param username 注册账户
     * @param friend 好友账户
     * */
    public TalkNode friendDrop(String username, String friend) throws Exception;
    /**
     * 获取好友列表
     * @param username 注册账户
     * */
    public TalkNode friendList(String username) throws Exception;
    /**
     * 添加拦截账户
     * @param username 注册账户
     * @param friend 拦截账户
     * */
    public TalkNode blockSave(String username, String[] friend) throws Exception;
    /**
     * 删除拦截账户
     * @param username 注册账户
     * @param friend 拦截账户
     * */
    public TalkNode blockDrop(String username, String friend) throws Exception;
    /**
     * 获取拦截列表
     * @param username 注册账户
     * */
    public TalkNode blockList(String username) throws Exception;
    /**
     * 添加群组信息
     * @param owner 所属用户
     * @param name 群组名称
     * @param desc 群组描述
     * @param limit 用户上限
     * @param friend 好友账户
     * */
    public TalkNode groupSave(String owner, String name, String desc, Integer limit, String[] friend) throws Exception;
    /**
     * 删除群组信息
     * @param id 群组编号
     * */
    public TalkNode groupDrop(String id) throws Exception;
    /**
     * 修改群组信息
     * @param id 群组编号
     * @param name 群组名称
     * @param desc 群组描述
     * @param limit 用户上限
     * */
    public TalkNode groupModify(String id, String name, String desc, Integer limit) throws Exception;
    /**
     * 修改群组群主
     * @param id 群组编号
     * @param owner 所属用户
     * */
    public TalkNode groupModifyOwner(String id, String owner) throws Exception;
    /**
     * 获取群组信息
     * @param id 群组编号
     * */
    public TalkNode groupGet(String[] id) throws Exception;
    /**
     * 获取群组列表
     * @param size 当前页数
     * @param cursor 当前游标
     * @param start 开始毫秒
     * */
    public TalkNode groupList(Long size, String cursor, Long start) throws Exception;
    /**
     * 添加群组好友(单个)
     * @param id 群组编号
     * @param friend 好友账户
     * */
    public TalkNode groupFriendSave(String id, String friend) throws Exception;
    /**
     * 添加群组好友(批量)
     * @param id 群组编号
     * @param friend 好友账户
     * */
    public TalkNode groupFriendSave(String id, String[] friend) throws Exception;
    /**
     * 删除群组好友(单个)
     * @param id 群组编号
     * @param friend 好友账户
     * */
    public TalkNode groupFriendDrop(String id, String friend) throws Exception;
    /**
     * 删除群组好友(批量)
     * @param id 群组编号
     * @param friend 好友账户
     * */
    public TalkNode groupFriendDrop(String id, String[] friend) throws Exception;
    /**
     * 获取群组好友
     * @param id 群组编号
     * */
    public TalkNode groupFriendList(String id) throws Exception;
    /**
     * 添加群组拦截(单个)
     * @param id 群组编号
     * @param friend 拦截账户
     * */
    public TalkNode groupBlackSave(String id, String friend) throws Exception;
    /**
     * 添加群组拦截(批量)
     * @param id 群组编号
     * @param friend 拦截账户
     * */
    public TalkNode groupBlackSave(String id, String[] friend) throws Exception;
    /**
     * 删除群组拦截(单个)
     * @param id 群组编号
     * @param friend 拦截账户
     * */
    public TalkNode groupBlackDrop(String id, String friend) throws Exception;
    /**
     * 删除群组拦截(批量)
     * @param id 群组编号
     * @param friend 拦截账户
     * */
    public TalkNode groupBlackDrop(String id, String[] friend) throws Exception;
    /**
     * 获取群组拦截
     * @param id 群组编号
     * */
    public TalkNode groupBlackList(String id) throws Exception;
    /**
     * 上传文件数据
     * @param file 文件数据
     * */
    public TalkNode fileUpload(File file) throws Exception;
    /**
     * 下载文件数据
     * @param id 文件编号
     * @param share 分享编号
     * @param thumb 是否缩放
     * @param file 本地文件
     * */
    public void fileDown(String id, String share, Boolean thumb, File file) throws Exception;
    /**
     * 添加消息数据
     * @param data 消息数据
     * */
    public TalkNode messageSave(TalkMsg data) throws Exception;
    /**
     * 获取消息列表
     * @param size 当前页数
     * @param cursor 当前游标
     * @param start 开始毫秒
     * */
    public TalkNode messageList(Long size, String cursor, Long start) throws Exception;
    /**
     * 获取聊天列表
     * @param size 当前页数
     * @param cursor 当前游标
     * @param start 开始毫秒
     * */
    public TalkNode chatList(Long size, String cursor, Long start) throws Exception;
    /**
     * 添加聊天群组
     * @param owner 所属用户
     * @param name 群组名称
     * @param desc 群组描述
     * @param limit 用户上限
     * @param friend 聊天账户
     * */
    public TalkNode roomSave(String owner, String name, String desc, Integer limit, String[] friend) throws Exception;
    /**
     * 修改聊天群组
     * @param id 群组编号
     * @param name 群组名称
     * @param desc 群组描述
     * @param limit 用户上限
     * */
    public TalkNode roomModify(String id, String name, String desc, Integer limit) throws Exception;
    /**
     * 删除聊天群组
     * @param id 群组编号
     * */
    public TalkNode roomDrop(String id) throws Exception;
    /**
     * 获取聊天群组
     * @param id 群组编号
     * */
    public TalkNode roomGet(String id) throws Exception;
    /**
     * 获取聊天群组
     * */
    public TalkNode roomList() throws Exception;
    /**
     * 获取聊天好友(单个)
     * @param id 群组编号
     * @param friend 聊天账户
     * */
    public TalkNode roomFriendSave(String id, String friend) throws Exception;
    /**
     * 获取聊天好友(批量)
     * @param id 群组编号
     * @param friend 聊天账户
     * */
    public TalkNode roomFriendSave(String id, String[] friend) throws Exception;
    /**
     * 删除聊天好友(单个)
     * @param id 群组编号
     * @param friend 聊天账户
     * */
    public TalkNode roomFriendDrop(String id, String friend) throws Exception;
    /**
     * 删除聊天好友(批量)
     * @param id 群组编号
     * @param friend 聊天账户
     * */
    public TalkNode roomFriendDrop(String id, String[] friend) throws Exception;
} 收起阅读 »

imgeek社区上线“活动”板块,求玩环

社区上线“活动”板块,公测中,求玩环   http://www.imgeek.org/activity/   花了偶们一个月的时间,改版后的社区活动模块开始公测,相对于以前的和传统的活动报名网站,有了几个改进: 1. 开放演讲报名 2. 开放投票,你的投票将...
继续阅读 »
社区上线“活动”板块,公测中,求玩环
 
http://www.imgeek.org/activity/
 
花了偶们一个月的时间,改版后的社区活动模块开始公测,相对于以前的和传统的活动报名网站,有了几个改进:
1. 开放演讲报名
2. 开放投票,你的投票将决定演讲顺序
3. 未注册用户也可以报名
4. 活动中的提问和社区相结合,在活动中的提问将进入社区问题流,更多的人将会看到你的问题 
5. 开放合作伙伴和志愿者申请通道,并得到展示  
 
  收起阅读 »

php和nodejs服务端代码示例参考

https://github.com/easemob/emchat-server-examples   这个github上的php和nodejs已经是最新代码,找不到的小伙伴也可以从这里下载查看,有什么问题欢迎随时交流,我QQ:1003432453,邮箱:fe...
继续阅读 »
https://github.com/easemob/emchat-server-examples   这个github上的php和nodejs已经是最新代码,找不到的小伙伴也可以从这里下载查看,有什么问题欢迎随时交流,我QQ:1003432453,邮箱:fengpei@easemob.com。 收起阅读 »

新版AndroidStudio以及新版sdk开发环境创建新项目可能会出现的一些错误问题

首先说下我这边开发的环境: AndroidStudio version:1.4.1 jdk version:1.7.0 SDKTools version:24.4.x Build-tools version:23.0.1 Compile SDK version...
继续阅读 »
首先说下我这边开发的环境:
AndroidStudio version:1.4.1
jdk version:1.7.0
SDKTools version:24.4.x
Build-tools version:23.0.1
Compile SDK version:API 22 (5.1)
Minimum SDK version:API 15(4.0.3)
Gradle version:2.4

连接地址:
AndroidStudio等工具地址:
Android官网:http://developer.android.com/
国内收集: http://www.androiddevtools.cn/
gradle v2.4下载(as有时自动下载不成功,所以要自己下载):https://downloads.gradle.org/distributions/gradle-2.4-all.zip
关于gradle首次创建项目卡住的问题可以看下这篇文章AndroidStudio 新建及clone项目关于gradle出现的问题"

创建项目
这里使用AndroidStudio创建一个空的项目就OK了,
打开AndroidStudio 点击Start a new Android Studio project创建新项目

当创建成功后发现会出现了一些问题
问题一
Error:A problem occurred configuring project ':app'.
> Could not download junit.jar (junit:junit:4.12)
> Could not get resource 'https://jcenter.bintray.com/junit/junit/4.12/junit-4.12.jar'.
> Could not GET 'https://jcenter.bintray.com/junit/junit/4.12/junit-4.12.jar'.
> peer not authenticated
Error:A problem occurred configuring project ':app'.
> Could not download hamcrest-core.jar (org.hamcrest:hamcrest-core:1.3): No cached version available for offline mode

这个是因为最新的创建项目会使用junit库来进行代码测试,在下载这个库的内容的时候发现他引用了hamcrest这个框架,不过国内下载这个框架的这个接啊日报hamcrest-core.jar不成功,所以会报这个错误,可以把build.gradle里引用的junit给删除或注释
问题二
D:\develop\android\workspace\studio\EaseUICustomer\app\build\intermediates\exploded-aar\com.android.support\appcompat-v7\23.1.0\res\values-v23\values-v23.xml
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'.
Error:(2) Error retrieving parent for item: No resource found that matches the given name 'android:Widget.Material.Button.Colored'.
Error:Execution failed for task ':app:processDebugResources'.
> com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command 'D:\develop\android\android_sdk\build-tools\build-tools-23.0.1\aapt.exe'' finished with non-zero exit value 1

这个是因为创建项目默认使用appcompat-v7包23.0.1版本,这个版本好像有问题,手动把这个版本号改下就ok了,改成22.+然后同步 就ok
 
同步发表到个人博客:http://blog.melove.net 收起阅读 »

AndroidStudio一个工程内查看多个项目的实现

现在已经有很多人开始使用AndroidStudio开始开发Android了,但是这货有一点儿不好,一个界面只能打开一个项目,当我们在开发自己的项目时,如果想像eclipse查看别的demo的代码或者功能,只能再另外打开一个Window新开项目,其实呢Andro...
继续阅读 »
现在已经有很多人开始使用AndroidStudio开始开发Android了,但是这货有一点儿不好,一个界面只能打开一个项目,当我们在开发自己的项目时,如果想像eclipse查看别的demo的代码或者功能,只能再另外打开一个Window新开项目,其实呢AndroidStudio可以在一个项目中导入多个Module,这里以导入环信最新版的几个demo来实现在AndroidStudi中查看多个项目;
首先导入3.0的项目,3.0的demo引入了EaseUI库,在导入AndroidStudio的时候会以Module的形式自动导入EaseUI,EaseUI库中放着simpledemo这个小demo,我们先把它剪切出来,和其他的demo放在一起,等下我们就在一个AndroidStudio项目中导入环信的这些全部demo
首先导入3.0Demo:

photo000.png



因为要导入所有的demo所以给导入的项目重新命名下

photo001.png




photo002.png



导入完成后就可以开始导入其他项目了,不过想在当前项目中导入其他项目要选择Import Module的方式,下边以导入环信老版本的demo为例

photo003.png




photo004.png




OK 导入成功,可以看到项目中有三个Module了 easeUI easeUIDemo oldDemo,其中oldDemo就是刚才导入的老版本的demo,只是改了个名字

photo005.png



然后看下Run/Debug configuration 可以发现这里有两个配置项,就说明当前工程内有两个可运行的Android项目

photo006.png



下边再导入那个简单的easeuidemo看下
导入之后看下Project Structure 可以看到当前项目有四个Module,一个EaseUI library库,其他三个是Android app项目easeUIDemo 和easeUISimpleDemo引用了EaseUI库,oldDemo是老版本直接集成的demo

photo007.png



可以根据这里的选择来运行不同的module,并且可以在一个工程内查看所有的代码

photo008.png



同步发表到个人博客:http://melove.net
文章地址:http://blog.melove.net/lzan13/develops/android-develop/androidstudio-project-module-941.html 收起阅读 »

环信移动客服云产品使用指南抢先看

问产品经理拿到新鲜出炉带着热气的产品使用指南。    这个不是最终版,正式版本以这里的为准: http://docs.easemob.com/    要提前学习的同学可以下载啦!   免费注册试用环信移动客服云-->    
问产品经理拿到新鲜出炉带着热气的产品使用指南。 
 
这个不是最终版,正式版本以这里的为准: http://docs.easemob.com/ 
 
要提前学习的同学可以下载啦!   免费注册试用环信移动客服云-->
 
 

关于环信新版EaseUI库导入AndroidStudio问题

环信大牛最新封装了一个供开发者直接使用的UI库 EaseUI,这个可以让大家快速的进行集成环信的sdk进行实现聊天,官方也说了老版本的demo不会进行维护,重点维护这个EaseUI,但是在自己导入的时候有时会有些问题,这里用1.4版本的AndroidStudi...
继续阅读 »
环信大牛最新封装了一个供开发者直接使用的UI库 EaseUI,这个可以让大家快速的进行集成环信的sdk进行实现聊天,官方也说了老版本的demo不会进行维护,重点维护这个EaseUI,但是在自己导入的时候有时会有些问题,这里用1.4版本的AndroidStudio导入3.0的demo来说明下;
首先就是打开as导入项目

tupian010.png


我喜欢给他改个名字EaseUIDemo

tupian011.png


导入完成
我这边导入时没有问题的,有时导入demo或者我们自己创建的项目 然后导入easeui库,并加入到自己的项目中去的时候可能会出现下边这样的错误 ,出现问题的原因的大致因为EaseUI默认引入的v4包的版本20.0.0,但是大家的开发环境不同,sdk版本以及编译器和support库版本不同,会出现错误;

tupian012.png



解决办法:这个时候就去点击项目设置,选中EaseUI把sdk版本设置成和build.gradle里一样的版本就行了,如果过低,建议更新sdk,还不行,就把自己的项目的sdk版本和EaseUI都设置成一样,v4库也设置成一样



tupian013.png


设置了之后,要记得同步gradle 1 or 2 方式都可以

tupian014.png


如果是自己创建的项目想在as中引入EaseUI库,可以点击File->New->Import Module选项

tupian009.png


同步发表到个人博客:http://melove.net 收起阅读 »

关于新版AndroidStudio导入环信Demo的一些注意事项

这里是以AndroidStudio v1.4版本为例 新版AndroidStudio已经可以直接导入eclipse的项目了,具体步骤看下边(会的飘过) 有一点要注意:as导入eclipse的项目,如果项目有引入library项目,并且library项目路径正...
继续阅读 »
这里是以AndroidStudio v1.4版本为例

新版AndroidStudio已经可以直接导入eclipse的项目了,具体步骤看下边(会的飘过)
有一点要注意:as导入eclipse的项目,如果项目有引入library项目,并且library项目路径正确,as在导入eclipse的项目的同时会自动导入library为module

启动as 进入起始页,有些设置的是启动直接打开项目了,可以在设置里先设置下:


tupian000.png



设置了之后,在启动就是进入到启动界面了:
选择Import project(Eclipse ADT, Gradle, etc)选项开始选择导入eclipse创建的Android项目

tupian001.png



然后选择项目路径,这里以最新版环信demo2.2.4为例


注意:环信的ChatDemoUI这个demo里边因为研发的同事为了照顾老版本的as使用者,已经用eclipse生成了build.gradle文件,所以如果要导入新版as 请把build.gradle删除


tupian002.png


 




tupian003.png


然后选择项目目的路径(相当于工作空间,带上项目目录名)

tupian004.png


提示目录不存在,不存在就对了  ok

tupian005.png



继续默认 Finish

tupian006.png



然后就是等待,这个一般有时会卡住,因为有时需要下载gradle的包,会很慢很慢,解决办法就是自己去gradle官网去下载gradle包,至于怎么解决看下这篇:关于环信新版EaseUI库导入AndroidStudio问题

tupian007.png



导入成功,打开项目就是这样了

tupian008.png


下边多说一句,就是如果自己创建的项目想要引用EaseUI这个库(不只是这个库,其他任何的library库),可以直接在项目界面,点击菜单栏File->New->Import Module 然后选择easeui路径就ok

tupian009.png


同步发表到个人博客:http://melove.net 收起阅读 »

新增“代码”区

收录了基于环信SDK开发的开源项目,大家写代码的时候就有更多参考了。   如果你们什么好的项目,特别是基于环信SDK开发的,并且愿意开源分享出来,请在http://www.imgeek.org/page/code后跟贴回复,我们将收录在的“代码”区,让你的成果...
继续阅读 »
收录了基于环信SDK开发的开源项目,大家写代码的时候就有更多参考了。
 
如果你们什么好的项目,特别是基于环信SDK开发的,并且愿意开源分享出来,请在http://www.imgeek.org/page/code后跟贴回复,我们将收录在的“代码”区,让你的成果与更多人分享。
  收起阅读 »

ASN 和 PB 的编码效率比较

作者:王纯业 ,转自http://blog.easemob.com/ ### 第一个例子 http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thr...
继续阅读 »

作者:王纯业 ,转自http://blog.easemob.com/

### 第一个例子
http://martin.kleppmann.com/2012/12/05/schema-evolution-in-avro-protocol-buffers-thrift.html 是一个很好的比较例子。
我类似的做了一个 ASN1 的结构
```
Person DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
  Person ::= SEQUENCE {
    username PrintableString,
    favouritenumber INTEGER,
    interests SEQUENCE OF PrintableString
  }
END
```
用下面的方法编译
```
erlc -I. -bper Person.asn
erl
> c("Person").
{ok,'Person'}
> {ok, B} = 'Person':encode('Person', #'Person'{username = "Martin", favouritenumber = 1337, interests = ["daydreaming", "hacking"]}).
{ok,<<6,77,97,114,116,105,110,2,5,57,2,11,100,97,121,100,
      114,101,97,109,105,110,103,7,104,97,99,...>>}
> byte_size(B).
31
```
这个例子里面,ASN1 用了 31 bytes ,protobuf 用了 33 bytes, Avro 用了
32 bytes。 这不是一个公平的比较,对于大量使用小数据结构的时候,例如,
enum command type 之类的,ASN1 可以节省更多的 bytes 。
### 第二个例子
这是 protobuf 的定义。
```
package dummy;
message S {
  optional int32 a =1;
  optional bool b =2;
  optional int32 c =3;
  optional D   d =4;
}
message D {
  optional bool d1 = 1;
  optional bool d2 = 2;
}
```
用 erlang 编译 参考 [https://github.com/tomas-abrahamsson/gpb]()
```
> deps/gpb/bin/protoc-erl -I. -o-erl src -o-hrl include s1.proto
> erl -sname a@localhost
(a@localhost)1> R = #'S'{a=1,b=true,c=2, d=#'D'{d1 = true, d2 = true} }.
#'S'{a =1,b = true,c = 2,d = #'D'{d1 = true,d2 = true}}
(sync@localhost)2> s1:encode_msg(R).
<<8,1,16,1,24,2,34,4,8,1,16,1>>
(a@localhost)14> byte_size(s1:encode_msg(R)).
12
```
可以看到 protobuf 用了 12 个字节。
ASN1 的例子,使用  PER 编码方式。
```
Dummy DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
  Dummy ::= SEQUENCE {
   a INTEGER (0..7),
   b BOOLEAN,
   c INTEGER (0..3),
   d SEQUENCE {
      d1 BOOLEAN,
      d2 BOOLEAN }}
END
```
```
> erlc -I. -bper Dummy.asn
> erl
(a@localhost)1> 'Dummy':encode('Dummy', #'Dummy'{ a = 1, b = true, c = 2, d = #'Dummy_d'{d1= true, d2 = true }}).
{ok,<<";">>}
```
protobuf 用了  12 个字节, ASN1 用了 1 个字节。同样,这也不是一个公平的比较。
很难做出公平的比较。但是可以说在大多数情况下 ASNPER 的编码是更加节省带宽的。
### 为什么 ASN1 PER 的编码效率比 PB 的高
1. ASN1 PER 是面向 bit 的编码方式,PB 是面向字节的编码方式。
2. PB 中 message 都是可以扩展的,ASN1 中只有使用  `...` 关键字的类型,才是可以扩展的。
3. PB 中的整数很简单,都是可以扩展到 64 位,ASN1 中有更加灵活(复杂) 的整数扩展方式。
2. PB 中的 `required`, `optional`, `oneof`, 和 `extensions` 的特性,对编码没有影响。例如,就算是 `required` 的字段,编码的时候,也是需要 tag 。
3. ASN1 PER 对很多关键字都是敏感的。例如
   1. `required` 的字段不会添加表明类型的 tag
   2. `required` 的字段按顺序编码。
4. tag 在 PER 中不做编码。
5. by default, every message is extensible in PB. Instread, ASN1 extensibility should be explicitly specified.
4. PB 中支持的整数类型不支持 subtype, 而 ASN1 PER 中的整数支持 subtype , 可以实现高效编码。
### 使用 ASN1 的优点
1. 编码紧凑,节省带宽。这是为什么几乎所有的 2G/3G/4G/5G 的无线通信协议都使用 ASN1 的原因之一。
2. ASN1 久经考验,asn1c 的项目已经十多年了,依然活跃开发。 Erlang 因为是通信公司创造的,语言内嵌 ASN 的支持。 Erlang 没有默认支持 PB 需要使用第三方开发库。
3. ASN1 支持 XER (XML) ,可以方便的调试。
4. wireshark 本身对 ASN1 的支持很好。
### 使用 ASN1 PER 的风险
1. ASN1 本身很复杂。ASN1 的学习成本高
2. PER 编码很复杂。可以用700行 C 代码实现 PB 的编解码,但实现 PER 编码不行。
3. ASN1 对语言的支持不多,似乎只有  C/Erlang 有比较好的使用。由于历史原因,通信领域几乎没有其他语言可供选择。
  收起阅读 »