什么是红点更新提示?

红点更新提示类似微信朋友圈有新的朋友消息 时会在“发现”tab上显示红点,表示有新的消息。

目前三种显示方式:

1.显示具体数字

2.只显示红点

3.显示省略,表示数量很多

方案思路:

1.显示红点:通过本地和服务器的时间戳对比,判断是否要显示红点,每个按钮都有其对应的本地时间戳和服务器时间戳。

2.隐藏红点:当红点显示时,点击把红点隐藏。 并判断是否要更新本地时间戳文件。

3.红点时间戳:时间戳需要进行缓存,方式为写入文件。

重点考虑问题:

1.点击按钮后是否要刷新该项的时间戳

2.由于更新时间戳要写入文件 所以尽快能少文件的保存

答:

1.只在按钮为红点显示状态下点击后更新本地时间戳文件。但在接口请求时不马上写入文件。原因如下:

接口请求过程中 -点击了按钮(无法得知该按钮记录的时间是否在服务器里时间的前面还是后面 保存点击时间 另起临时Temp字段 不覆盖当前本地Local时间字段)

              (等请求完后调用回调,再对临时字段的时间进行判断比较)
接口请求成功 -点击了按钮(如果!showing的话,直接返回。如果showing,改变状态,更新时间戳)

2.后台接口参数加入index字段,每次后台有更新服务器时间戳时将index升高,前端请求后对index进行判断,不同则刷新本地时间戳并保存到文件,index相等说明数据库时间戳没有更新,就不处理。

方案说明安排:

方案从后台的字段设计和前端的逻辑处理进行说明,代码所列的顺序为:

后台接口参数--》红点控件(继承ImageView)--》客服端接口类及方法(单例)--》监听类和方法(方法写在接口类中)--》Activity类中的方法

--》工具类Utils的方法(供Activity调用)--》时间戳Model(UserItemUpdateRecord)

接口参数说明

接口:http://X.XX.XX.XXX/api/get_user_item_update_status?
请求方式GET
参数说明:

token=DQR3WF6Q56QW56QW           //用户唯一识别码 可在后台做分组 拓展参数
output
{
"code":0,//状态码,0代表成功
"msg":"",//发生错误的原因
"data":{//具体数据
     "index":0,//当前点击记录
"tip":[//各点时间戳
{
"type":"home",
"date":"2015-09-01",    
       "count":0,//内容更新数量
        },
{
"type":"infomation",
"date":"2015-11-11",
       "count":5,
        },
{
"type":"me",
"date":"2016-01-15",
       "count":15,
        }
]
}

RedPointImageView

这里红点控件根据数量分成三类,数量判断标准自己定。

public class RedPointImageView extends ImageView {

  //红点长度类型
private static final int TYPE_ZERO = 0;
private static final int TYPE_SHORT = 1;
private static final int TYPE_LONG = 2; private int type;   //保存onSizeChange()里的宽高
private int width;
private int height;   //按钮Tag,用来识别
private String mTag;
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Rect mRect = new Rect();
private RelativeLayout mRlPoint = null;
private Drawable mDPoint = null;
private int radius;
private boolean mShow = false;
private int number;
private TextView mTvPoint; public RedPointImageView(Context context) {
super(context);
this.number = -1;
init();
} public RedPointImageView(Context context, AttributeSet attrs) {
super(context, attrs);
this.number = -1;
init();
} public RedPointImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.number = -1;
init();
} private void init() {
if(number<0)return;  //数量小于0直接返回
mPaint.setFilterBitmap(true);
radius = getResources().getDimensionPixelSize(R.dimen.red_point_radius); mRlPoint = new RelativeLayout(getContext()); mTvPoint = new TextView(getContext()); mTvPoint.setTextSize(14);
mTvPoint.setTextColor(getResources().getColor(R.color.white));
RelativeLayout.LayoutParams params1 = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params1.setMargins(0,0,0,0);
params1.addRule(RelativeLayout.CENTER_IN_PARENT);
mRlPoint.addView(mTvPoint,params1);
initUI();
}   
private void initUI(){
if(number == 0){          //ZERO类型  
mTvPoint.setText("");
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getResources().getDimensionPixelOffset(R.dimen.margin_8),getResources().getDimensionPixelOffset(R.dimen.margin_8));
params.setMargins(0,0,0,0);
mRlPoint.setLayoutParams(params);
mRlPoint.setBackgroundResource(R.drawable.icon_red_point);
type = TYPE_ZERO; }else if(number>0&&number<10){  //SHORT类型
mTvPoint.setText(String.valueOf(number));
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getResources().getDimensionPixelOffset(R.dimen.margin_15),getResources().getDimensionPixelOffset(R.dimen.margin_15));
params.setMargins(0,0,0,0);
mRlPoint.setLayoutParams(params);
mRlPoint.setBackgroundResource(R.drawable.icon_red_point);
type = TYPE_SHORT;
}else{                //LONG类型
mTvPoint.setText("···");
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getResources().getDimensionPixelOffset(R.dimen.margin_20),getResources().getDimensionPixelOffset(R.dimen.margin_12));
params.setMargins(0,0,0,0);
mRlPoint.setLayoutParams(params);
mRlPoint.setBackgroundResource(R.drawable.bg_corner_red);
type = TYPE_LONG;
}
mDPoint = new BitmapDrawable(null,convertViewToBitmap(mRlPoint));
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
updateRect(w, h);
} private void updateRect(int w, int h) {
int left,top,right,bottom;
if(type == TYPE_SHORT){
left = w - radius;
top = 0;
right = w;
bottom = radius;
}else if(type == TYPE_ZERO){
left = w - radius*2/3;
top = 0;
right = w;
bottom = radius*2/3;
}else{
left = w - radius/3*4;
top = 0;
right = w;
bottom = radius/5*4;
} mRect.set(left, top, right, bottom); } @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mShow) {
drawRedPoint(canvas);
}
} private void drawRedPoint(Canvas canvas) {
if (mDPoint == null) {
return;
} canvas.save();
// canvas.clipRect(mRect, Region.Op.DIFFERENCE); mDPoint.setBounds(mRect);
mDPoint.draw(canvas); canvas.restore();
} public void setShow(boolean isShow){
this.mShow = isShow;
invalidate();
} public boolean isShow(){
return mShow;
} public String getmTag() {
return mTag;
} public void setmTag(String mTag) {
this.mTag = mTag;
} public void updateItem(){
UserItemUpdateRecord userItemUpdateRecord = IpinClient.getInstance().getAccountManager().getUserItemUpdateRecord();
if(userItemUpdateRecord!=null){
userItemUpdateRecord.refreshUpdateImg(this);
}
} public void setNumber(int number){
this.number = number;
if(number<0) mShow = false;
else mShow = true;
init();
onSizeChanged(width,height,width,height);
invalidate();
} public static Bitmap convertViewToBitmap(View view){
view.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
view.buildDrawingCache();
Bitmap bitmap = view.getDrawingCache(); return bitmap;
} }

接口类方法

1.tryToLoadUserItemStatus() --> 从本地文件里读取红点时间戳对象

2.requestUserItemStatus() --> 接口请求服务器端的红点时间戳

3.saveItemUpdateStatusToFile() --> 保存红点时间戳到本地文件

    public static final String URL_GET_USER_ITEM_STATUS = "http://m.gaokao.ipin.com/api/get_user_item_update_status?";//获取用户按钮更新信息

    public static final String FILE_NAME_USER_ITEM_STATUS = "user_item_status.ipin";//按钮更新状态文件

    private AtomicBoolean isLoadedUserItemStatus = new AtomicBoolean(false);

    private HashSet<OnUpdateItemStatusListener> mOnUpdateItemStatusListeners = new HashSet<>();//监听列表

    private UserItemUpdateRecord userItemUpdateRecord;//时间戳model
private boolean isRequestingUserItemStatus = false;//是否正在获取数据 /**
* 从文件中读取按钮更新信息
*/
private void tryToLoadUserItemStatus(){ TaskExecutor.getInstance().post(new Runnable() {
@Override
public void run() {
synchronized (AccountManager.class) { boolean b = checkAndCopyUserData(AccountConstant.FILE_NAME_USER_ITEM_STATUS);
if (!b) {
isLoadedUserItemStatus.set(true);
requestUserItemStatus();
return;
}
String path = StorageManager.getInstance().getPackageFiles() + AccountConstant.FILE_NAME_USER_ITEM_STATUS;
Object object = FileUtil.readObjectFromPath(path); if (object != null && object instanceof UserItemUpdateRecord) {
userItemUpdateRecord = (UserItemUpdateRecord) object;
} isLoadedUserItemStatus.set(true);
requestUserItemStatus();
}
}
});
}

    private boolean checkAndCopyUserData(String path) {
      String newPath = StorageManager.getInstance().getPackageFiles() + path;

      String oldPath = StorageManager.getInstance().getPackageCacheRoot() + path;
      File newFile = new File(newPath);
      if (newFile.exists()) {
        return true;
      }

      File oldFile = new File(oldPath);
      if (!oldFile.exists()) {
        return false;
      }

      return oldFile.renameTo(newFile);
    }

/**
* 请求服务器更新按钮的时间戳
*/
public void requestUserItemStatus(){ isRequestingUserItemStatus = true;
final IRequest request = (IRequest) IpinClient.getInstance().getService(IpinClient.SERVICE_HTTP_REQUEST);
if (request == null) {
return;
}
request.sendRequestForPostWithJson(AccountConstant.URL_GET_USER_ITEM_STATUS, getParamForGetUserInfo(getIpinToken()), new IRequestCallback() {
@Override
public void onResponseSuccess(JSONObject jsonObject) { if (jsonObject == null) {
return;
} if(jsonObject.getInteger(Constant.KEY_CODE)!=0)return;
if(jsonObject.getJSONObject(Constant.KEY_DATA)==null)return; jsonObject = jsonObject.getJSONObject(Constant.KEY_DATA);
if(userItemUpdateRecord!=null&&userItemUpdateRecord.getIndex() == jsonObject.getInteger("index"))
            return;//如果服务器的index与本地的一样,则目前已经保存最新的更新时间戳,不执行更新本地时间戳操作 UserItemUpdateRecord updateRecord = new UserItemUpdateRecord();
updateRecord.decode(jsonObject);
userItemUpdateRecord = updateRecord;
isRequestingUserItemStatus = false;
dispatchOnUpdateItemStatusListener();
TaskExecutor.getInstance().post(new Runnable() {
@Override
public void run() {
saveItemUpdateStatusToFile();//将时间戳保存至文件
}
}); } @Override
public void onResponseSuccess(String str) {
isRequestingUserItemStatus = false;
} @Override
public void onResponseError(int code) {
isRequestingUserItemStatus = false;
}
});
} /**
* 保存用户按钮更新信息
*/
private void saveItemUpdateStatusToFile() { TaskExecutor.getInstance().post(new Runnable() {
@Override
public void run() {
if (userItemUpdateRecord != null) {
String path = StorageManager.getInstance().getPackageFiles() + AccountConstant.FILE_NAME_USER_ITEM_STATUS;//path为文件路径
FileUtil.writeObjectToPath(userItemUpdateRecord, path);
} }
});
}

接口请求完的服务器时间戳需要保存到文件里,并更新本地model

监听类及方法

    public interface OnUpdateItemStatusListener {
void updateItemStatus();
} private HashSet<OnUpdateItemStatusListener> mOnUpdateItemStatusListeners = new HashSet<>(); public void registerOnUpdateItemStatusListener(OnUpdateItemStatusListener listener) {
mOnUpdateItemStatusListeners.add(listener);
} public void unregisterOnUpdateItemStatusListener(OnUpdateItemStatusListener listener) {
mOnUpdateItemStatusListeners.remove(listener);
} private void dispatchOnUpdateItemStatusListener() {
for (OnUpdateItemStatusListener listener : mOnUpdateItemStatusListeners) {
listener.updateItemStatus();
}
}

使用红点的类中的方法

  private RedPointImageView mButton1;
  private RedPointImageView mButton2;
  private RedPointImageView mButton3;
  mButton1.setmTag(UserItemUpdateRecord.KEY_HOME);//给按钮设置备注,可做判断识别
  mButton2.setmTag(UserItemUpdateRecord.KEY_INFORMATION);
  mButton3.setmTag(UserItemUpdateRecord.KEY_ME);
  MyClient.getInstance().getAccountManager().registerOnUpdateItemStatusListener(this);//注册监听

    @Override
public void updateItemStatus() {//监听回调方法
checkAndUpdateItemStatus();
} private void checkAndUpdateItemStatus(){
List<RedPointImageView> views = new ArrayList<>();
views.add(mButton1);
views.add(mButton2);
views.add(mButton3);
MyUtils.updateRedPointItem(getActivity(),views);//调用工具类中的方法
} @Override
public void onDestroy() {
super.onDestroy();
IpinClient.getInstance().getAccountManager().unregisterOnUpdateItemStatusListener(this);//注销监听
} //点击时按钮调用方法
mButton.updateItem();

工具类MyUtils中的方法

    /**
* 检查更新按钮红点状态
* @param imageView
*/
public static void updateRedPointItem(List<RedPointImageView> imageView){
for (RedPointImageView view : imageView){
updateRedPointItem(view);
}
} public static void updateRedPointItem(RedPointImageView imageView){
UserItemUpdateRecord userItemUpdateRecord = IpinClient.getInstance().getAccountManager().getUserItemUpdateRecord();
if(userItemUpdateRecord.isNeedUpdate(imageView)){
imageView.setShow(true);
}
}

UserItemUpdateRecord类

    public class UserItemUpdateRecord  implements Serializable, IParse {

            private static final String KEY_INDEX = "index";

            //这里拿了三个按钮作为例子,每一个按钮需要配置一个字符串(Json转换)和
//三个参数(服务器时间戳mDSystemXX、移动端时间戳mDXX、移动端临时时间戳mDTempXX,这个临时时间戳的作用前面已经说明)
public static final String KEY_HOME = "home";
public static final String KEY_INFORMATION = "information";
public static final String KEY_ME = "me"; private int index; private Date mDHome;
private Date mDTempHome;
private Date mDSystemHome;
private Date mDInformation;
private Date mDTempInformation;
private Date mDSystemInformation;
private Date mDMe;
private Date mDTempMe;
private Date mDSystemMe; public UserItemUpdateRecord() {
mDTempHome = getDateForTemp("2015-01-01 01:01:01");
mDTempInformation = getDateForTemp("2015-01-01 01:01:01");
mDTempMe = getDateForTemp("2015-01-01 01:01:01");
mDHome = new Date();
mDTempHome = new Date();
mDSystemHome = new Date();
} public void decode(JSONObject object){
if(index == object.getInteger(KEY_INDEX))return;
index = object.getInteger(KEY_INDEX);
mDSystemHome = object.getDate(KEY_HOME);
mDSystemInformation = object.getDate(KEY_INFORMATION);
mDSystemMe = object.getDate(KEY_ME); if(mDHome==null)mDHome = mDSystemHome;
if(mDInformation==null)mDInformation = mDSystemInformation;
if(mDMe==null)mDMe = mDSystemMe;
} @Override
public JSONObject encode(Object o) {
return null;
} @Override
public void release() { } /**
* 判断是否需要显示红点
* @param imageView
* @return
*/
public boolean isNeedUpdate(RedPointImageView imageView){
String tag = imageView.getmTag();
switch (tag){
case KEY_HOME:
return judgeIsNeedUpdate(imageView,mDHome,mDTempHome,mDSystemHome);
case KEY_INFORMATION:
return judgeIsNeedUpdate(imageView,mDInformation,mDTempInformation,mDSystemInformation);
case KEY_ME:
return judgeIsNeedUpdate(imageView,mDMe,mDTempMe,mDSystemMe);
default:
return false;
} } /**
* 只有当mDSystem在mDLocal、mDTemp之后才需要显示
* @param mDLocal 本地点击时间
* @param mDTemp 点击最新时间
* @param mDSystem 系统更新时间
* @return
*/
private boolean judgeIsNeedUpdate(RedPointImageView view,Date mDLocal ,Date mDTemp,Date mDSystem){
if(mDLocal.before(mDSystem)){
if(mDTemp==null)mDTemp = new Date(mDLocal.getTime());
if(mDSystem.before(mDTemp)){ //判断方法加入刷新动作 这里处理了前面说到的接口请求过程中点击按钮,把时间保存在临时Temp参数,这里进行判断并处理,可减少写入文件次数。
mDLocal.setTime(mDTemp.getTime());
executeUpdate(view,mDInformation,mDTempInformation);
//刷新
return false;
}else{
return true;
} }else{
return false;
} } /**
* 点击时触发的处理方法
* @param view
*/
public void refreshUpdateImg(RedPointImageView view){
String tag = view.getmTag();
switch (tag){
case KEY_HOME:
executeUpdate(view,mDHome,mDTempHome);
break;
case KEY_INFORMATION:
executeUpdate(view,mDInformation,mDTempInformation);
break;
case KEY_ME:
executeUpdate(view,mDMe,mDTempMe);
break;
}
} private void executeUpdate(RedPointImageView view,Date mDLocal ,Date mDTemp){
boolean flag = IpinClient.getInstance().getAccountManager().isRequestingUserItemStatus();
if(flag){
mDTemp.setTime(new Date().getTime());//只更新Temp时间,等待接口请求完刷新状态的时候做是否要保存点击时间的判断
if(view.isShow()){
view.setShow(false);
}
}else{
if(view.isShow()){ //接口已经请求完 并且处于红点显示状态,使红点不显示,并且保存当前点击时间
mDLocal.setTime(new Date().getTime());
IpinClient.getInstance().getAccountManager().saveItemUpdateStatusToFile();
view.setShow(false);
}else{
//接口已经请求完 并且处于红点不显示状态,不做时间保存处理
}
}
} private Date getDateForTemp(String time){
Date date = new Date();
SimpleDateFormat df=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
try {
date = df.parse(time);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
} public int getIndex() {
return index;
} public void setIndex(int index) {
this.index = index;
} public Date getmDHome() {
return mDHome;
} public void setmDHome(Date mDHome) {
this.mDHome = mDHome;
} public Date getmDInformation() {
return mDInformation;
} public void setmDInformation(Date mDInformation) {
this.mDInformation = mDInformation;
} public Date getmDMe() {
return mDMe;
} public void setmDMe(Date mDMe) {
this.mDMe = mDMe;
} public Date getmDSystemMe() {
return mDSystemMe;
} public void setmDSystemMe(Date mDSystemMe) {
this.mDSystemMe = mDSystemMe;
} public Date getmDSystemHome() {
return mDSystemHome;
} public void setmDSystemHome(Date mDSystemHome) {
this.mDSystemHome = mDSystemHome;
} public Date getmDSystemInformation() {
return mDSystemInformation;
} public void setmDSystemInformation(Date mDSystemInformation) {
this.mDSystemInformation = mDSystemInformation;
}
}

Android应用中-更新提示显示红点的方案的更多相关文章

  1. Android Studio中设置提示函数用法

    Eclipse有一个很好的功能,就是当你代码调用某个android API时,鼠标移到对应的函数或者方法上,就会自动有一个悬浮窗提示该函数的说明(所包含的参数含义,该方法功能).迁移到Android ...

  2. Android: Service中创建窗口显示

    WindowManager.LayoutParams: int TYPE_SYSTEM_ALERT  Window type: system window, such as low power ale ...

  3. Android studio无法更新 提示网络连接失败

    Android studio 更新时,提示网络问题 “Connection failed. Please check your network connection and try again” 在默 ...

  4. 如何控制android系统中NavigationBar 的显示与隐藏

    我们使用的大多数android手机上的Home键,返回键以及menu键都是实体触摸感应按键.如果你用Google的Nexus4或Nexus5话,你会发现它们并没有实体按键或触摸感应按键,取而代之的是在 ...

  5. 在 android studio 中更新安卓应用版本号

    随着应用程序不断修改,版本号也应当变化.要更新安卓应用的版本号,只需要在 build.gradle(module:app) 中修改 versionCode 和 versionName 即可,也可以只改 ...

  6. eclipse,代码中有错误,项目或者java类中却不显示红叉

    修改eclipse代码提示级别1.单个项目修改项目上右键-->properties-->java compiler-->building-->enable project sp ...

  7. Eclipse,代码中有错误,项目中却不显示红叉

    ***修改eclipse 代码提示级别1.单个项目修改项目上右键-->properties-->java compiler-->building-->enable projec ...

  8. ionic ng-src 在网页显示,但是导出apk在android手机中运行不显示图片

    解决方法参照: http://stackoverflow.com/questions/29896158/load-image-using-ng-src-in-android-ionic-aplicat ...

  9. Android studio设置参数提示

    在Android studio的使用的过程中,那么就需要对当前的代码显示当前的方式做一个的提示信息,那么可以通过Android studio的的设置的方法,来对Android studio方法的提示显 ...

随机推荐

  1. windows 花式装系统

    目录 一.安装系统前准备 准备U盘 准备好一个制作启动盘的软件 准备系统镜像 二.接下来先制作启动盘(以微PE为例) 三.插上u盘,调BIOS(BIOS即基本输入输出系统) 四.进入PE 五.开始安装 ...

  2. Windows 下如何设置 只允许固定IP远程访问

    通过设置IP安全策略限制固定IP访问 说明: (1)以XP环境为例,步骤:先禁止所有IP,再允许固定IP访问. (2)配置过程中很多步骤图是重复的,一些没价值的图就省略了: (3)光看的话可能中间重复 ...

  3. 糟糕的双重检查加锁(DCL)

    在Java并发编程时,同步都会存在着巨大的性能开销,因此,人们使用了很多的技巧来降低同步的影响,这其中有一些技巧很好,但是也有一些技巧存在一些缺陷,下面要结束的双重检查加锁(DCL)就是有缺陷的一类. ...

  4. git 与 github 最简单的用法

    今天发现cygwin里已经装了git,所以顺便测试一下git是怎么用的. 大概最简单的用法是这样的: 1.在github创建一个repository.复制右边那个 HTTPS clone URL 2. ...

  5. Codeforces Gym 100286F Problem F. Fibonacci System 数位DP

    Problem F. Fibonacci SystemTime Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudg ...

  6. android圆角View实现及不同版本这间的兼容(android3.0过后的版本)

    http://blog.csdn.net/lovecluo/article/details/8710174 在做我们自己的APP的时候,为了让APP看起来更加的好看,我们就需要将我们的自己的View做 ...

  7. Tomcat安装、配置、优化及负载均衡详解

    一.常见JavaWeb服务器      1.WebLogic:是BEA公司的产品.WebSphereAS:是IBM公司的产品.JBossAS:红帽公司的产品,可以自行了解      2.Tomcat服 ...

  8. 【XS128】Link error L1822 symbol _FADD / _FSUB/ _FDIV/ _FMUL.....错误解决的方法

    转载请注明出处 因为阅历有限,篇幅不周之处还望指出,谢谢 假设方法确实奏效,请一定回复点赞哦,给后来人也是一种帮助,谢谢! 这是飞思卡尔 XS128平台比較常见的LINK错误. 可是要解决起来也比較头 ...

  9. Linux堆内存管理深入分析

    (上半部) 作者:走位@阿里聚安全 前言 近年来,漏洞挖掘越来越火,各种漏洞挖掘.利用的分析文章层出不穷.从大方向来看,主要有基于栈溢出的漏洞利用和基于堆溢出的漏洞利用两种.国内关于栈溢出的资料相对较 ...

  10. 网络IPC:套接字之套接字选项

    套接字机制提供两个套接字选项接口来控制套接字的行为.一个接口用来设置选项,另一个接口允许查询一个选项的状态.可以获取或设置的三种选项: (1)通用选项,工作在所有套接字类型上. (2)在套接字层次管理 ...