对于想了解Linux系统下UDP发送和接收广播消息小例子的读者,本文将是一篇不可错过的文章,我们将详细介绍linux发送udp广播包,并且为您提供关于ActiveMQ消息队列服务(二)发送和接收、An
对于想了解Linux 系统下 UDP 发送和接收广播消息小例子的读者,本文将是一篇不可错过的文章,我们将详细介绍linux发送udp广播包,并且为您提供关于ActiveMQ消息队列服务(二)发送和接收、Android 发送和接收即时消息、Android 深入四大组件(八)广播的注册、发送和接收过程、c – 套接字 – 发送和接收的有价值信息。
本文目录一览:- Linux 系统下 UDP 发送和接收广播消息小例子(linux发送udp广播包)
- ActiveMQ消息队列服务(二)发送和接收
- Android 发送和接收即时消息
- Android 深入四大组件(八)广播的注册、发送和接收过程
- c – 套接字 – 发送和接收
Linux 系统下 UDP 发送和接收广播消息小例子(linux发送udp广播包)
// 发送端
#include <iostream>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
using namespace std;
int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
fflush(stdout);
int sock = -1;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
cout<<"socket error"<<endl;
return false;
}
const int opt = 1;
//设置该套接字为广播类型,
int nb = 0;
nb = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
if(nb == -1)
{
cout<<"set socket error..."<<endl;
return false;
}
struct sockaddr_in addrto;
bzero(&addrto, sizeof(struct sockaddr_in));
addrto.sin_family=AF_INET;
addrto.sin_addr.s_addr=htonl(INADDR_BROADCAST);
addrto.sin_port=htons(6000);
int nlen=sizeof(addrto);
while(1)
{
sleep(1);
//从广播地址发送消息
char smsg[] = {"abcdef"};
int ret=sendto(sock, smsg, strlen(smsg), 0, (sockaddr*)&addrto, nlen);
if(ret<0)
{
cout<<"send error...."<<ret<<endl;
}
else
{
printf("ok ");
}
}
return 0;
}
// 接收端 http://blog.csdn.net/robertkun
#include <iostream>
#include <stdio.h>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
using namespace std;
int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
fflush(stdout);
// 绑定地址
struct sockaddr_in addrto;
bzero(&addrto, sizeof(struct sockaddr_in));
addrto.sin_family = AF_INET;
addrto.sin_addr.s_addr = htonl(INADDR_ANY);
addrto.sin_port = htons(6000);
// 广播地址
struct sockaddr_in from;
bzero(&from, sizeof(struct sockaddr_in));
from.sin_family = AF_INET;
from.sin_addr.s_addr = htonl(INADDR_ANY);
from.sin_port = htons(6000);
int sock = -1;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
{
cout<<"socket error"<<endl;
return false;
}
const int opt = 1;
//设置该套接字为广播类型,
int nb = 0;
nb = setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&opt, sizeof(opt));
if(nb == -1)
{
cout<<"set socket error..."<<endl;
return false;
}
if(bind(sock,(struct sockaddr *)&(addrto), sizeof(struct sockaddr_in)) == -1)
{
cout<<"bind error..."<<endl;
return false;
}
int len = sizeof(sockaddr_in);
char smsg[100] = {0};
while(1)
{
//从广播地址接受消息
int ret=recvfrom(sock, smsg, 100, 0, (struct sockaddr*)&from,(socklen_t*)&len);
if(ret<=0)
{
cout<<"read error...."<<sock<<endl;
}
else
{
printf("%s\t", smsg);
}
sleep(1);
}
return 0;
}
自已在 Linux 虚拟机下测试可以成功,前提是要把主机设置在同一网段内,还有就是不要忘记关闭 Linux 的防火墙.. 可以使用 setup 命令进行设置。
(我在测试的时候只能发送不收接收,折磨了我半天,后来才想到是 Linux 防火墙的问题。。)
关于虚拟机的网卡配置,建议选择桥接模式。NAT 的模式的话,是受限制的,可能会收不到广播消息。
具体的参考网上的文章吧。。
祝你成功。。
ActiveMQ消息队列服务(二)发送和接收
创建java项目,导入jar包
- ActiveMQ的解压包中,提供了运行ActiveMQ的所有jar。
创建消息生成者,发送消息
说明:ActiveMQ是实现了JMS规范的。在实现消息服务的时候,必须基于API接口规范。下述API都是接口类型,定义在javax.jms包中,是JMS标准接口定义。ActiveMQ完全实现这一套api标准。
##JMS常用的API说明##
ConnectionFactory
链接工厂, 用于创建链接的工厂类型。
Connection
链接,用于建立访问ActiveMQ连接的类型, 由链接工厂创建。
Session
会话, 一次持久有效、有状态的访问,由链接创建。
Destination & Queue & Topic
目的地, 即本次访问ActiveMQ消息队列的地址,由Session会话创建。
(1)interface Queue extends Destination
(2)Queue:队列模型,只有一个消费者。消息一旦被消费,默认删除。
(3)Topic:主题订阅中的消息,会发送给所有的消费者同时处理。
Message
消息,在消息传递过程中数据载体对象,是所有消息【文本消息TextMessage,对象消息ObjectMessage等】具体类型的顶级接口,可以通过会话创建或通过会话从ActiveMQ服务中获取。
MessageProducer
消息生成者, 在一次有效会话中, 用于发送消息给ActiveMQ服务的工具,由Session会话创建。
MessageCustomer
消息消费者【消息订阅者,消息处理者】, 在一次有效会话中, 用于ActiveMQ服务中获取消息的工具,由Session会话创建。
我们定义的消息生产者和消费者,都是基于上面API实现的。
第一步:创建MyProducer类,定义sendMessage方法
package cn.activemq.mq.producer;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MyProducer {
// 定义链接工厂
ConnectionFactory connectionFactory = null;
// 定义链接
Connection connection = null;
// 定义会话
Session session = null;
// 定义目的地
Destination destination = null;
// 定义消息生成者
MessageProducer producer = null;
// 定义消息
Message message = null;
public void sendToMQ(){
try{
/*
* 创建链接工厂
* ActiveMQConnectionFactory - 由ActiveMQ实现的ConnectionFactory接口实现类.
* 构造方法: public ActiveMQConnectionFactory(String userName, String password, String brokerURL)
* userName - 访问ActiveMQ服务的用户名, 用户名可以通过jetty-realm.properties配置文件配置.
* password - 访问ActiveMQ服务的密码, 密码可以通过jetty-realm.properties配置文件配置.
* brokerURL - 访问ActiveMQ服务的路径地址. 路径结构为 - 协议名://主机地址:端口号
* 此链接基于TCP/IP协议.
*/
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.18.33:61616");
// 创建链接对象
connection = connectionFactory.createConnection();
// 启动链接
connection.start();
/*
* 创建会话对象
* 方法 - connection.createSession(boolean transacted, int acknowledgeMode);
* transacted - 是否使用事务, 可选值为true|false
* true - 使用事务, 当设置此变量值, 则acknowledgeMode参数无效, 建议传递的acknowledgeMode参数值为
* Session.SESSION_TRANSACTED
* false - 不使用事务, 设置此变量值, 则acknowledgeMode参数必须设置.
* acknowledgeMode - 消息确认机制, 可选值为:
* Session.AUTO_ACKNOWLEDGE - 自动确认消息机制
* Session.CLIENT_ACKNOWLEDGE - 客户端确认消息机制
* Session.DUPS_OK_ACKNOWLEDGE - 有副本的客户端确认消息机制
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建目的地, 目的地命名即队列命名, 消息消费者需要通过此命名访问对应的队列
destination = session.createQueue("test-mq");
// 创建消息生成者, 创建的消息生成者与某目的地对应, 即方法参数目的地.
producer = session.createProducer(destination);
// 创建消息对象, 创建一个文本消息, 此消息对象中保存要传递的文本数据.
message = session.createTextMessage("hello,activeme");
// 发送消息
producer.send(message);
System.out.println("消息发送成功!");
}catch(Exception e){
e.printStackTrace();
System.out.println("访问ActiveMQ服务发生错误!!");
}finally{
try {
// 回收消息发送者资源
if(null != producer)
producer.close();
} catch (JMSException e) {
e.printStackTrace();
}
try {
// 回收会话资源
if(null != session)
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
try {
// 回收链接资源
if(null != connection)
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
第二步:创建一个测试类MessageTest
package cn.activemq.mq.test;
import org.junit.Test;
import cn.activemq.mq.producer.MyProducer;
public class MessageTest {
@Test
public void sendToMQ(){
MyProducer producer = new MyProducer();
producer.sendToMQ();
}
}
第三步:测试
- 设置防火墙,配置61616端口。注意修改之后重启防火墙。
- 测试结果:
- 查看ActiveMQ管理控制界面,消息发送成功
创建消息消费者,消费消息
第一步:创建MyConsumer类
package cn.activemq.mq.consumer;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
public class MyConsumer {
// 定义链接工厂
ConnectionFactory connectionFactory = null;
// 定义链接
Connection connection = null;
// 定义会话
Session session = null;
// 定义目的地
Destination destination = null;
// 定义消息消费者
MessageConsumer consumer = null;
// 定义消息
Message message = null;
public void recieveFromMQ(){
try{
/*
* 创建链接工厂
* ActiveMQConnectionFactory - 由ActiveMQ实现的ConnectionFactory接口实现类.
* 构造方法: public ActiveMQConnectionFactory(String userName, String password, String brokerURL)
* userName - 访问ActiveMQ服务的用户名, 用户名可以通过jetty-realm.properties配置文件配置.
* password - 访问ActiveMQ服务的密码, 密码可以通过jetty-realm.properties配置文件配置.
* brokerURL - 访问ActiveMQ服务的路径地址. 路径结构为 - 协议名://主机地址:端口号
* 此链接基于TCP/IP协议.
*/
connectionFactory = new ActiveMQConnectionFactory("admin", "admin", "tcp://192.168.18.33:61616");
// 创建链接对象
connection = connectionFactory.createConnection();
// 启动链接
connection.start();
/*
* 创建会话对象
* 方法 - connection.createSession(boolean transacted, int acknowledgeMode);
* transacted - 是否使用事务, 可选值为true|false
* true - 使用事务, 当设置此变量值, 则acknowledgeMode参数无效, 建议传递的acknowledgeMode参数值为
* Session.SESSION_TRANSACTED
* false - 不使用事务, 设置此变量值, 则acknowledgeMode参数必须设置.
* acknowledgeMode - 消息确认机制, 可选值为:
* Session.AUTO_ACKNOWLEDGE - 自动确认消息机制
* Session.CLIENT_ACKNOWLEDGE - 客户端确认消息机制
* Session.DUPS_OK_ACKNOWLEDGE - 有副本的客户端确认消息机制
*/
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// 创建目的地, 目的地命名即队列命名, 消息消费者需要通过此命名访问对应的队列
destination = session.createQueue("test-mq");
// 创建消息消费者, 创建的消息消费者与某目的地对应, 即方法参数目的地.
consumer = session.createConsumer(destination);
// 从ActiveMQ服务中获取消息
message = consumer.receive();
TextMessage tMsg = (TextMessage) message;
System.out.println("从MQ中获取的消息是:"+tMsg.getText());
}catch(Exception e){
e.printStackTrace();
System.out.println("访问ActiveMQ服务发生错误!!");
}finally{
try {
// 回收消息消费者资源
if(null != consumer)
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
try {
// 回收会话资源
if(null != session)
session.close();
} catch (JMSException e) {
e.printStackTrace();
}
try {
// 回收链接资源
if(null != connection)
connection.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
}
}
第二步:修改测试类MessageTest,新增测试方法
@Test
public void recieveFromMQ(){
MyConsumer consumer = new MyConsumer();
consumer.recieveFromMQ();
}
第三步:测试
- 查看Eclipse控制台
- 查看ActiveMQ管理控制界面,消息消费成功
Android 发送和接收即时消息
发送即时文本消息
一旦你拥有一个活跃的聊天会话,使用sendChatMessage方法来对会话中的联系人发送消息,如下面的代码片段所示:
Java代码:
chatSession.sendChatMessage(“HelloWorld!”);
指定的消息文本将会传送给在当前会话中的所有联系人。
接收即时文本消息
为了监听新来的消息,实现IChatListener接口,重写它的newMessageReceived处理函数。你可以通过特定的聊天会话或者更加通用的IM会话来注册这个接口,方法是调用addRemoteChatListener方法。
下面的代码片段显示了创建和注册IChatListener接口的框架代码,其中,同时在特定的聊天会话和IM会话中进行注册。需要注意的是,IChatListener接口包含一个桩类,当你创建自己的聊天监听者实现时你必须扩展它。
Java代码:
<!-- lang: java -->
IChatListenerchatListener=newIChatListener.Stub(){
publicvoidnewMessageReceived(Stringfrom,Stringbody){
//TODOHandleincomingmessages.
}
//要求实施小组闲谈.
publicvoidconvertedToGroupChat(StringoldJid,StringgroupChatRoom,longgroupId) {}
publicvoidparticipantJoined(StringgroupChatRoom,Stringnickname){}
publicvoidparticipantLeft(StringgroupChatRoom,Stringnickname){}
publicvoidchatClosed(StringgroupChatRoom)throwsRemoteException{}
publicvoidchatRead(Stringarg0)throwsRemoteException{}
};
//在聊天室聊天的听众增加会话.
chatSession.addRemoteChatListener(chatListener);
//增加听者的即时短信聊天的会议.
imSession.addRemoteChatListener(chatListener);
使用IM会话注册的聊天监听者将接收所有与这个会话有关联的任一聊天会话接收的消息,所以,这里的消息处理方式应该相当的通用。相反的,用一个聊天会话注册的监听者将只关注与这个特定的会话有关的消息和事件。
移动开发交流群:164427941
Android 深入四大组件(八)广播的注册、发送和接收过程
前言
我们接着来学习 Android 四大组件中的 BroadcastReceiver,广播主要就是分为注册、接收和发送过程。建议阅读此文前请先阅读 Android 深入理解四大组件系列的文章,知识重复的部分,本文不再赘述。
1. 广播的注册过程
BroadcastReceiver 的注册分为两种,分别是静态注册和动态注册,静态注册在应用安装时由 PackageManagerService 来完成注册过程,关于这一过程,我会在后续的介绍 PackageManagerService 文章中详细介绍。这里只介绍 BroadcastReceiver 的动态注册。
要想动态注册 BroadcastReceiver,需要调用 registerReceiver 方法,它的实现在 ContextWrapper 中,代码如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java


@Override
public Intent registerReceiver(
BroadcastReceiver receiver, IntentFilter filter) {
return mBase.registerReceiver(receiver, filter);
}
这里 mBase 具体指向就是 ContextImpl,不明白的请查看 Android 深入四大组件(二)Service 的启动过程这篇文章。ContextImpl 的 registerReceiver 方法有很多重载的方法最终会调用 registerReceiverInternal 方法:
frameworks/base/core/java/android/app/ContextImpl.java


private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
IntentFilter filter, String broadcastPermission,
Handler scheduler, Context context) {
IIntentReceiver rd = null;
if (receiver != null) {
if (mPackageInfo != null && context != null) {//1
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = mPackageInfo.getReceiverDispatcher(
receiver, context, scheduler,
mMainThread.getInstrumentation(), true);//2
} else {
if (scheduler == null) {
scheduler = mMainThread.getHandler();
}
rd = new LoadedApk.ReceiverDispatcher(
receiver, context, scheduler, null, true).getIIntentReceiver();//3
}
}
try {
final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
mMainThread.getApplicationThread(), mBasePackageName,
rd, filter, broadcastPermission, userId);//4
if (intent != null) {
intent.setExtrasClassLoader(getClassLoader());
intent.prepareToEnterProcess();
}
return intent;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
在注释 1 处判断如果 LoadedApk 类型的 mPackageInfo 不等于 null 并且 context 不等 null 就调用注释 2 处的代码通过 mPackageInfo 的 getReceiverDispatcher 方法获取 rd 对象,否则就调用注释 3 处的代码来创建 rd 对象。注释 2 和 3 的代码的目的都是要获取 IIntentReceiver 类型的 rd 对象,IIntentReceiver 是一个 Binder 接口,用于进行跨进程的通信,它的具体实现在
LoadedApk.ReceiverDispatcher.InnerReceiver,如下所示。
frameworks/base/core/java/android/app/LoadedApk.java


static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
final LoadedApk.ReceiverDispatcher mStrongRef;
InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
...
}
...
}
回到 registerReceiverInternal 方法,在注释 4 处调用了 ActivityManagerProxy(AMP)的 registerReceiver 方法,最终会调用 AMS 的 registerReceiver 方法,并将 rd 传就去。不明白的同学请查看 Android 深入四大组件(一)应用程序启动过程(前篇),这里不再赘述。
查看 AMS 的 registerReceiver 方法,如下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
synchronized(this) {
...
Iterator<String> actions = filter.actionsIterator();//1
...
// Collect stickies of users
int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
while (actions.hasNext()) {
String action = actions.next();
for (int id : userIds) {
ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
if (stickies != null) {
ArrayList<Intent> intents = stickies.get(action);
if (intents != null) {
if (stickyIntents == null) {
stickyIntents = new ArrayList<Intent>();
}
stickyIntents.addAll(intents);//2
}
}
}
}
}
ArrayList<Intent> allSticky = null;
if (stickyIntents != null) {
final ContentResolver resolver = mContext.getContentResolver();
for (int i = 0, N = stickyIntents.size(); i < N; i++) {
Intent intent = stickyIntents.get(i);
if (filter.match(resolver, intent, true, TAG) >= 0) {
if (allSticky == null) {
allSticky = new ArrayList<Intent>();
}
allSticky.add(intent);//3
}
}
}
...
}
注释 1 处根据传入的 IntentFilter 类型的 filter 的得到 actions 列表,根据 actions 列表和 userIds(userIds 可以理解为应用程序的 uid)得到所有的粘性广播的 intent,并在注释 2 处传入到 stickyIntents 中,在注释 3 处将这些粘性广播的 intent 存入到 allSticky 列表中,从这里可以看出粘性广播是存储在 AMS 中的。
接着查看 AMS 的 registerReceiver 方法的剩余内容:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


public Intent registerReceiver(IApplicationThread caller, String callerPackage,
IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...
synchronized (this) {
...
ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());//1
if (rl == null) {
rl = new ReceiverList(this, callerApp, callingPid, callingUid,
userId, receiver);//2
if (rl.app != null) {
rl.app.receivers.add(rl);
}
...
}
...
BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
permission, callingUid, userId);//3
rl.add(bf);//4
if (!bf.debugCheck()) {
Slog.w(TAG, "==> For Dynamic broadcast");
}
mReceiverResolver.addFilter(bf);//5
...
return sticky;
}
}
注释 1 处获取 ReceiverList 列表,如果为空则在注释 2 处创建,ReceiverList 继承自 ArrayList,用来存储广播接收者。在注释 3 处创建 BroadcastFilter 并传入此前创建的 ReceiverList,BroadcastFilter 用来描述注册的广播接收者,并在注释 4 通过 add 方法将自身添加到 ReceiverList 中。注释 5 处将 BroadcastFilter 添加到 mReceiverResolver 中,这样当 AMS 接收到广播时就可以从 mReceiverResolver 中找到对应的广播接收者了。下面给出广播的注册过程的时序图。
绘图 1_副本.png
2. 广播的发送和接收过程
ContextImpl 到 AMS 的调用过程
广播可以发送多种类型,包括无序广播(普通广播)、有序广播和粘性广播,这里以无序广播为例,来讲解广播的发送过程。
要发送无序广播需要调用 sendBroadcast 方法,它的实现同样在 ContextWrapper 中:
frameworks/base/core/java/android/content/ContextWrapper.java


@Override
public void sendBroadcast(Intent intent) {
mBase.sendBroadcast(intent);
}
接着来看 ContextImpl 中的 sendBroadcast 方法,如下所示。
frameworks/base/core/java/android/app/ContextImpl.java


@Override
public void sendBroadcast(Intent intent) {
warnIfCallingFromSystemProcess();
String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
try {
intent.prepareToLeaveProcess(this);
ActivityManagerNative.getDefault().broadcastIntent(
mMainThread.getApplicationThread(), intent, resolvedType, null,
Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
getUserId());//1
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
注释 1 处又是熟悉的代码,最终会调用 AMS 的 broadcastIntent 方法:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


public final int broadcastIntent(IApplicationThread caller,
Intent intent, String resolvedType, IIntentReceiver resultTo,
int resultCode, String resultData, Bundle resultExtras,
String[] requiredPermissions, int appOp, Bundle bOptions,
boolean serialized, boolean sticky, int userId) {
enforceNotIsolatedCaller("broadcastIntent");
synchronized(this) {
intent = verifyBroadcastLocked(intent);//1
...
/**
* 2
*/
int res = broadcastIntentLocked(callerApp,
callerApp != null ? callerApp.info.packageName : null,
intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
requiredPermissions, appOp, bOptions, serialized, sticky,
callingPid, callingUid, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
我们来查看注释 1 处的 verifyBroadcastLocked 方法:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


final Intent verifyBroadcastLocked(Intent intent) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors() == true) {//1
throw new IllegalArgumentException("File descriptors passed in Intent");
}
int flags = intent.getFlags();//2
if (!mProcessesReady) {
if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {//3
} else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {//4
Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
+ " before boot completion");
throw new IllegalStateException("Cannot broadcast before boot completed");
}
}
if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) {
throw new IllegalArgumentException(
"Can''t use FLAG_RECEIVER_BOOT_UPGRADE here");
}
return intent;
}
verifyBroadcastLocked 方法主要是验证广播是否合法,在注释 1 处验证 intent 是否不为 null 并且有文件描述符。注释 2 处获得 intent 中的 flag。注释 3 处如果系统正在启动过程中,判断如果 flag 设置为 FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT(启动检查时只接受动态注册的广播接收者)则不做处理,如果不是则在注释 4 处判断如果 flag 没有设置为 FLAG_RECEIVER_REGISTERED_ONLY(只接受动态注册的广播接收者)则会抛出异常。
我们再回到 broadcastIntent 方法,在注释 2 处调用了 broadcastIntentLocked 方法,代码如下所示。
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java


final int broadcastIntentLocked(ProcessRecord callerApp,
String callerPackage, Intent intent, String resolvedType,
IIntentReceiver resultTo, int resultCode, String resultData,
Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
...
if ((receivers != null && receivers.size() > 0)
|| resultTo != null) {
BroadcastQueue queue = broadcastQueueForIntent(intent);
/**
* 1
*/
BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
callerPackage, callingPid, callingUid, resolvedType,
requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
resultData, resultExtras, ordered, sticky, false, userId);
...
boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
if (!replaced) {
queue.enqueueOrderedBroadcastLocked(r);
queue.scheduleBroadcastsLocked();//2
}
}
...
}
return Act
这里省略了很多代码,前面的工作主要是将动态注册的广播接收者和静态注册的广播接收者按照优先级高低存储在不同的列表中,再将这两个列表合并到 receivers 列表中,这样 receivers 列表包含了所有的广播接收者(无序广播和有序广播)。在注释 1 处创建 BroadcastRecord 对象并将 receivers 传进去,在注释 2 处调用 BroadcastQueue 的 scheduleBroadcastsLocked 方法。
这里先给出 ContextImpl 到 AMS 的调用过程的时序图。
绘图 8_副本.png
AMS 到 BroadcastReceiver 的调用过程
BroadcastQueue 的 scheduleBroadcastsLocked 方法的代码如下所示。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java


public void scheduleBroadcastsLocked() {
...
mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));//1
mBroadcastsScheduled = true;
}
在注释 1 处向 BroadcastHandler 类型的 mHandler 对象发送了 BROADCAST_INTENT_MSG 类型的消息,这个消息在 BroadcastHandler 的 handleMessage 方法中进行处理,如下所示。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java


private final class BroadcastHandler extends Handler {
public BroadcastHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case BROADCAST_INTENT_MSG: {
if (DEBUG_BROADCAST) Slog.v(
TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
processNextBroadcast(true);
} break;
...
}
}
}
在 handleMessage 方法中调用了 processNextBroadcast 方法,processNextBroadcast 方法对无序广播和有序广播分别进行处理,旨在将广播发送给广播接收者,下面给出 processNextBroadcast 方法中对无序广播的处理部分。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java


final void processNextBroadcast(boolean fromMsg) {
...
if (fromMsg) {
mBroadcastsScheduled = false;//1
}
// First, deliver any non-serialized broadcasts right away.
while (mParallelBroadcasts.size() > 0) {//2
r = mParallelBroadcasts.remove(0);//3
...
for (int i=0; i<N; i++) {
Object target = r.receivers.get(i);
if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST,
"Delivering non-ordered on [" + mQueueName + "] to registered "
+ target + ": " + r);
deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);//4
}
...
}
}
从前面的代码我们得知 fromMsg 的值为 true,因此注释 1 处会将 mBroadcastsScheduled 设置为 flase,表示对于此前发来的 BROADCAST_INTENT_MSG 类型的消息已经处理了。注释 2 处的 mParallelBroadcasts 列表用来存储无序广播,通过 while 循环将 mParallelBroadcasts 列表中的无序广播发送给对应的广播接收者。在注释 3 处获取每一个 mParallelBroadcasts 列表中存储的 BroadcastRecord 类型的 r 对象。注释 4 处将这些 r 对象描述的广播发送给对应的广播接收者,deliverToRegisteredReceiverLocked 方法如下所示。
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java


private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
BroadcastFilter filter, boolean ordered, int index) {
...
try {
if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
"Delivering to " + filter + " : " + r);
if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
...
} else {
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);//1
}
if (ordered) {
r.state = BroadcastRecord.CALL_DONE_RECEIVE;
}
} catch (RemoteException e) {
...
}
}
这里省去了大部分的代码,这些代码是用来检查广播发送者和广播接收者的权限。如果通过了权限的检查,则会调用注释 1 处的 performReceiveLocked 方法:
frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java


void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
Intent intent, int resultCode, String data, Bundle extras,
boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
// Send the intent to the receiver asynchronously using one-way binder calls.
if (app != null) {//1
if (app.thread != null) {//2
// If we have an app thread, do the call through that so it is
// correctly ordered with other one-way calls.
try {
app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
data, extras, ordered, sticky, sendingUser, app.repProcState);//3
}
}
...
} else {
receiver.performReceive(intent, resultCode, data, extras, ordered,
sticky, sendingUser);/
}
}
注释 1 和 2 处的代码表示如果广播接收者所在的应用程序进程存在并且正在运行,则执行注释 3 处的代码,表示用广播接收者所在的应用程序进程来接收广播,这里 app.thread 指的是 ApplicationThread,我们来查看 ApplicationThread 的 scheduleRegisteredReceiver 方法,代码如下所示。
frameworks/base/core/java/android/app/ActivityThread.java


public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
int resultCode, String dataStr, Bundle extras, boolean ordered,
boolean sticky, int sendingUser, int processState) throws RemoteException {
updateProcessState(processState, false);
receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
sticky, sendingUser);//1
}
注释 1 处调用了 IIntentReceiver 类型的对象 receiver 的 performReceive 方法,这里实现 receiver 的类为 LoadedApk.ReceiverDispatcher.InnerReceiver,代码如下所示。
frameworks/base/core/java/android/app/LoadedApk.java


static final class ReceiverDispatcher {
final static class InnerReceiver extends IIntentReceiver.Stub {
...
InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
mStrongRef = strong ? rd : null;
}
@Override
public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final LoadedApk.ReceiverDispatcher rd;
...
if (rd != null) {
rd.performReceive(intent, resultCode, data, extras,
ordered, sticky, sendingUser);//1
} else {
...
}
...
}
在注释 1 处调用了 ReceiverDispatcher 类型的 rd 对象的 performReceive 方法:
frameworks/base/core/java/android/app/LoadedApk.java


public void performReceive(Intent intent, int resultCode, String data,
Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
final Args args = new Args(intent, resultCode, data, extras, ordered,
sticky, sendingUser);//1
...
if (intent == null || !mActivityThread.post(args)) {//2
if (mRegistered && ordered) {
IActivityManager mgr = ActivityManagerNative.getDefault();
if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
"Finishing sync broadcast to " + mReceiver);
args.sendFinished(mgr);
}
}
在注释 1 处将广播的 intent 等信息封装为 Args 对象,并在注释 2 处调用 mActivityThread 的 post 方法并传入了 Args 对象。这个 mActivityThread 是一个 Handler 对象,具体指向的就是 H,注释 2 处的代码就是将 Args 对象通过 H 发送到主线程的消息队列中。Args 继承自 Runnable,这个消息最终会在 Args 的 run 方法执行,Args 的 run 方法如下所示。
frameworks/base/core/java/android/app/LoadedApk.java


public void run() {
...
try {
ClassLoader cl = mReceiver.getClass().getClassLoader();
intent.setExtrasClassLoader(cl);
intent.prepareToEnterProcess();
setExtrasClassLoader(cl);
receiver.setPendingResult(this);
receiver.onReceive(mContext, intent);//1
} catch (Exception e) {
...
}
...
}
在注释 1 处执行了广播接收者的 onReceive 方法,这样注册的广播接收者就收到了广播并得到了 intent。
广播的注册、发送和接收过程就讲到这,最后给出剩余部分的调用时序图。
c – 套接字 – 发送和接收
现在我的问题:
我是否必须检查其他线程当前是否正在使用套接字,还是send / recv函数只是等待套接字就绪?
解决方法
我们今天的关于Linux 系统下 UDP 发送和接收广播消息小例子和linux发送udp广播包的分享就到这里,谢谢您的阅读,如果想了解更多关于ActiveMQ消息队列服务(二)发送和接收、Android 发送和接收即时消息、Android 深入四大组件(八)广播的注册、发送和接收过程、c – 套接字 – 发送和接收的相关信息,可以在本站进行搜索。
本文标签: