RILD负责modem和RILJ端的通信,信息分两种:unsolicited和solicited,前者是由modem主动上报的,诸如时区更新、通话状态、网络状态等消息,后者是RILJ端发请求并需要modem反馈的信息。RILJ与RILD之间的通信由主线程s_tid_dispatch负责监听,读取和分发,RILD与modem之间的通信由s_tid_mainloop和s_tid_reader负责写入和读取。
先看看目录,以android 7.0为例,RILD目录位于../hardware/ril,包括:
include —- 各种头文件定义,其中include/telephony/ril.h定义了135个RIL_REQUEST_XXX和45个RIL_UNSOL_XXX的宏定义,前者用于RILD向modem发送的请求消息id,后者用于由modem直接推送给RILD的消息id。
libril—-主要定义消息分发主线程和RILJ与RILD之间的socket监听
librilutils—-辅助类
reference-ril—-顾名思义,这个目录中的类有参考作用,因为整个RILD架构中,RILD与modem之间设计以库的形式加载,这个在RILD在初始化时能看到,这样就方便厂商定制
rild—-RILD的入口
在深入理解Android Telephony之RILD的启动 一文中看到,RILD通过rc加载,系统启动RILD的入口就到了rild.c中的main()函数,main主要做了三件事:
1、打开ril外部库,即厂商定制库;
2、启动事件分发主线程;
3、调用厂商库中的接口进行初始化:使用RIL_Init初始化RILD与modem的通信线程,使用RIL_register注册RILD与RILJ之间的socket,启动通信线程。
int main(int argc, char **argv) {
......
//打开libreference-ril.so
dlHandle = dlopen(rilLibPath, RTLD_NOW);
//创建主线程s_tid_dispatch,用来监听RILJ下发到socket的消息并分发。监听的原理是每个socket都注册上监听事件,然后把事件塞到s_tid_dispatch,一旦socket中有消息,s_tid_dispatch读到之后就触发事件的回调
RIL_startEventLoop();
//加载动态库,地址传给rilInit
rilInit =
(const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))
dlsym(dlHandle, "RIL_Init");
...
rilUimInit =
(const RIL_RadioFunctions *(*)(const struct RIL_Env *, int, char **))
dlsym(dlHandle, "RIL_SAP_Init");
//使用rilInit 初始化RIL,s_rilEnv是个静态回调数组
funcs = rilInit(&s_rilEnv, argc, rilArgv);
//
RIL_register(funcs);
}
看看主线程s_tid_dispatch,事件循环eventLoop–>ril_event_loop, watch_table,timer_list,pending_list三者为消息队列。
static struct ril_event * watch_table[MAX_FD_EVENTS];
static struct ril_event timer_list;
static struct ril_event pending_list;
RIL_startEventLoop(void) {
//标志s_tid_dispatch线程是否创建成功并启动
s_started = 0;
//创建线程,eventLoop是线程创建后的回调函数,其中会把s_started置为1
int result = pthread_create(&s_tid_dispatch, &attr, eventLoop, NULL);
//如果线程尚未创建成功,进入死循环等待
while (s_started == 0) {
pthread_cond_wait(&s_startupCond, &s_startupMutex);
}
......
}
static void *
eventLoop(void *param) {
//s_tid_dispatch线程创建成功
s_started = 1;
......
//定义匿名管道
ret = pipe(filedes);
......
s_fdWakeupRead = filedes[0];
s_fdWakeupWrite = filedes[1];
fcntl(s_fdWakeupRead, F_SETFL, O_NONBLOCK);
ril_event_set (&s_wakeupfd_event, s_fdWakeupRead, true,
processWakeupCallback, NULL);
rilEventAddWakeup (&s_wakeupfd_event);
ril_event_loop();
......
}
一、看看RILD是如何通过socket读取RILJ下发的消息
以s_wakeupfd_event为例:
1、首先封装一个事件,使用ril_event_set
void ril_event_set(struct ril_event * ev, int fd, bool persist, ril_event_cb func, void * param)
{
memset(ev, 0, sizeof(struct ril_event));
ev->fd = fd;
ev->index = -1;
ev->persist = persist;
ev->func = func;
ev->param = param;
fcntl(fd, F_SETFL, O_NONBLOCK);
}
2、把事件添加到消息队列中,使用rilEventAddWakeup,其中分两小步
static void rilEventAddWakeup(struct ril_event *ev) {
ril_event_add(ev);
triggerEvLoop();
}
第一步ril_event_add把事件加到消息队列,第二步使用triggerEvLoop触发事件循环
void ril_event_add(struct ril_event * ev)
{
MUTEX_ACQUIRE();
for (int i = 0; i < MAX_FD_EVENTS; i++) {
if (watch_table[i] == NULL) {
watch_table[i] = ev;
ev->index = i;
dump_event(ev);
FD_SET(ev->fd, &readFds);
if (ev->fd >= nfds) nfds = ev->fd+1;
break;
}
}
MUTEX_RELEASE();
}
ril_event_add就是把事件添加到watch_table中,最大可接纳8个事件,然后把事件文件描述符添加到readFds文件描述符集合中,nfds是当前readFds中的文件描述符总数。
triggerEvLoop就是往管道中写入空字符,来唤醒线程s_tid_dispatch,当当前线程不是s_tid_dispatch时,就需要唤醒s_tid_dispatch。
static void triggerEvLoop() {
int ret;
if (!pthread_equal(pthread_self(), s_tid_dispatch)) {
do {
ret = write (s_fdWakeupWrite, " ", 1);
} while (ret < 0 && errno == EINTR);
}
}
3、s_tid_dispatch被唤醒,循环体ril_event_loop肯定的执行了
void ril_event_loop()
{
for (;;) {
//把readFds文件描述符集合拷贝到本地rfds中
memcpy(&rfds, &readFds, sizeof(fd_set));
//看看timer_list中有没有定时事件,如果没有,ptv 赋值为空,传到select中,那么select将处于阻塞状态
if (-1 == calcNextTimeout(&tv)) {
ptv = NULL;
} else {
//如果timer_list中有定时事件,那么select将处于定时阻塞状态,时间到了,不管文件描述符集合rfds中是否有文件可读,select都要返回
ptv = &tv;
}
//关于select的介绍可以参考http://www.cnblogs.com/moonvan/archive/2012/05/26/2518881.html
n = select(nfds, &rfds, NULL, NULL, ptv);
......
//如果select处于非阻塞状态了,则继续往下走,那如果select一直处于阻塞状态呢?看这里是必须有定时事件,是否有可能一直没有定时事件,这样timer_list为空,而watch_table中的事件一直就处理不了?
//把定时事件从timer_list放到pending_list
processTimeouts();
//如过n大于0,表示rfds中有文件读状态发生改变了(通过FD_ISSET来识别),则把watch_table中的事件放到pending_list
processReadReadies(&rfds, n);
//timer_list和watch_table中的事件都放到pending_list之后,遍历pending_list中的事件并调用其回调。rilEventAddWakeup事件则是回调processWakeupCallback函数
firePending();
}
}
static void firePending()
{
struct ril_event * ev = pending_list.next;
while (ev != &pending_list) {
struct ril_event * next = ev->next;
removeFromList(ev);
//回调事件处理方法
ev->func(ev->fd, 0, ev->param);
ev = next;
}
}
接着看RIL_Init,主线程创建好之后,从库中动态加载RIL_Init函数,地址赋给rilInit。看看大体流程:
const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **argv)
{
//回调方法数组
s_rilenv = env;
......
//创建s_tid_mainloop线程,线程循环体为mainLoop
ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);
return &s_callbacks;
}
static void *
mainLoop(void *param __unused)
{
......
for (;;) {
fd = -1;
while (fd < 0) {
......
s_closed = 0;
//fd是读写文件描述符,如果读取到unsolicited类型命令,则回调onUnsolicited,后面再看at_open具体做什么
ret = at_open(fd, onUnsolicited);
RIL_requestTimedCallback(initializeCallback, NULL, &TIMEVAL_0);
...
}
}
RIL_Init的第一个参数是指向RIL_Env 的指针,实参是定义在rild.c中的s_rilEnv。
struct RIL_Env {
//solicited类型命令请求完成的回调函数指针
void (*OnRequestComplete)(RIL_Token t, RIL_Errno e,
void *response, size_t responselen);
//unsolicited类型命令应答函数指针
#if defined(ANDROID_MULTI_SIM)
void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen, RIL_SOCKET_ID socket_id);
#else
void (*OnUnsolicitedResponse)(int unsolResponse, const void *data, size_t datalen);
#endif
//计时请求的超时回调函数指针
void (*RequestTimedCallback) (RIL_TimedCallback callback,
void *param, const struct timeval *relativeTime);
};
static struct RIL_Env s_rilEnv = {
RIL_onRequestComplete,
RIL_onUnsolicitedResponse,
RIL_requestTimedCallback
};
参数传入后,直接赋给reference-ril.c的静态变量s_rilenv ,只在下面的宏定义中使用到s_rilenv 。
#ifdef RIL_SHLIB
static const struct RIL_Env *s_rilenv;
#define RIL_onRequestComplete(t, e, response, responselen) s_rilenv->OnRequestComplete(t,e, response, responselen)
#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
#define RIL_requestTimedCallback(a,b,c) s_rilenv->RequestTimedCallback(a,b,c)
#endif
RIL_Init返回一个RIL_RadioFunctions类型结构体地址&s_callbacks
static const RIL_RadioFunctions s_callbacks = {
RIL_VERSION,
onRequest,
currentState,
onSupports,
onCancel,
getVersion
};
typedef struct {
int version; //RIL的版本号
RIL_RequestFunc onRequest; //请求的函数指针
RIL_RadioStateRequest onStateRequest; //请求当前的radio状态
RIL_Supports supports; //判断是否支持当前的请求
RIL_Cancel onCancel; //取消当前请求
RIL_GetVersion getVersion; //获取当前RIL版本号
} RIL_RadioFunctions;
RIL_Init的返回值作为参数传入RIL_register 中,然后拷贝到变量s_callbacks,s_callbacks主要在下面的宏定义用到。
#if defined(ANDROID_MULTI_SIM)
#define RIL_UNSOL_RESPONSE(a, b, c, d) RIL_onUnsolicitedResponse((a), (b), (c), (d))
#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d), (e))
#define CALL_ONSTATEREQUEST(a) s_callbacks.onStateRequest(a)
#else
#define RIL_UNSOL_RESPONSE(a, b, c, d) RIL_onUnsolicitedResponse((a), (b), (c))
#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d))
#define CALL_ONSTATEREQUEST(a) s_callbacks.onStateRequest()
#endif
RIL_register 是在RILJ和RILD之间定义socket,支持几张卡,就定义几个socket,最多四个。每个socket有个事件监听器fdListen,监听事件丢入到s_tid_dispatch线程中,事件的回调是listenCallback。一旦RILJ通过socket写入一个命令,s_tid_dispatch线程的循环处理ril_event_loop会调用注册的回调方法listenCallback对命令进行预处理,并把预处理结果又写入到s_tid_dispatch中,同时注册回调为processCommandsCallback, ril_event_loop又执行回调processCommandsCallback,processCommandsCallback中调用processCommandBuffer,而processCommandsCallback中最终调用dispatchFunction。
extern "C" void
RIL_register (const RIL_RadioFunctions *callbacks) {
......
memcpy(&s_callbacks, callbacks, sizeof (RIL_RadioFunctions));
//这里支持几张卡就定义几个socket param,并注册监听
s_ril_param_socket = {
RIL_SOCKET_1, /* socket_id */
-1, /* fdListen */
-1, /* fdCommand */
PHONE_PROCESS, /* processName */
&s_commands_event, /* commands_event */
&s_listen_event, /* listen_event */
processCommandsCallback, /* processCommandsCallback */
NULL /* p_rs */
};
startListen(RIL_SOCKET_1, &s_ril_param_socket);
//类似地可定义RIL_SOCKET_2、RIL_SOCKET_3、RIL_SOCKET_4的参数并注册监听
......
}
static void startListen(RIL_SOCKET_ID socket_id, SocketListenParam* socket_listen_p) {
......
//在主线程中添加event,一旦RILJ通过socket写入事件,回调listenCallback
ril_event_set (socket_listen_p->listen_event, fdListen, false,
listenCallback, socket_listen_p);
rilEventAddWakeup (socket_listen_p->listen_event);
}
了解了s_tid_dispatch线程的原理,这里可以直接看回调了。
static void listenCallback (int fd, short flags, void *param) {
...
SocketListenParam *p_info = (SocketListenParam *)param;
if(RIL_SAP_SOCKET == p_info->type) {
listenParam = (MySocketListenParam *)param;
sapSocket = listenParam->socket;
}
......
if(NULL == sapSocket) {
processName = PHONE_PROCESS;
} else {
processName = BLUETOOTH_PROCESS;
}
//从相应的socket读取命令
fdCommand = accept(fd, (sockaddr *) &peeraddr, &socklen);
......
err = getsockopt(fdCommand, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds);
//如果不是radio socket,也不是bluetooth radio,则不处理命令
if (!is_phone_socket) {
RLOGE("RILD must accept socket from %s", processName);
close(fdCommand);
......
return;
}
ret = fcntl(fdCommand, F_SETFL, O_NONBLOCK);
if(NULL == sapSocket) {
p_info->fdCommand = fdCommand;
p_rs = record_stream_new(p_info->fdCommand, MAX_COMMAND_BYTES);
p_info->p_rs = p_rs;
//获取到socket中的信息之后,再把具体的命令事件add到s_tid_dispatch线程中处理,回调processCommandsCallback
ril_event_set (p_info->commands_event, p_info->fdCommand, 1,
p_info->processCommandsCallback, p_info);
rilEventAddWakeup (p_info->commands_event);
//RIL_onUnsolicitedResponse发送ril connected和radio state changed等,推送ril connected消息会把ril的版本号带给RILJ,也就是说,每次读到socket中有信息从RILJ到RILD,则应答RIL_UNSOL_RIL_CONNECTED和RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED信息给RILJ,如果时区数据有更新,也推送上去
onNewCommandConnect(p_info->socket_id);
} else {
......
}
}
static void onNewCommandConnect(RIL_SOCKET_ID socket_id) {
int rilVer = s_callbacks.version;
RIL_UNSOL_RESPONSE(RIL_UNSOL_RIL_CONNECTED,
&rilVer, sizeof(rilVer), socket_id);
RIL_UNSOL_RESPONSE(RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED,
NULL, 0, socket_id);
if (s_lastNITZTimeData != NULL) {
sendResponseRaw(s_lastNITZTimeData, s_lastNITZTimeDataSize, socket_id);
free(s_lastNITZTimeData);
s_lastNITZTimeData = NULL;
}
......
}
接着看p_info->commands_event事件的回调processCommandsCallback
static void processCommandsCallback(int fd, short flags, void *param) {
SocketListenParam *p_info = (SocketListenParam *)param;
for (;;) {
ret = record_stream_get_next(p_rs, &p_record, &recordlen);
if (ret == 0 && p_record == NULL) {
break;
} else if (ret < 0) {
break;
} else if (ret == 0) { /* && p_record != NULL */
//处理命令
processCommandBuffer(p_record, recordlen, p_info->socket_id);
}
}
if (ret == 0 || !(errno == EAGAIN || errno == EINTR)) {
//处理完了则关闭对应的文件,并从watch_event中删除这个命令的事件
close(fd);
p_info->fdCommand = -1;
ril_event_del(p_info->commands_event);
record_stream_free(p_rs);
rilEventAddWakeup(&s_listen_event);
onCommandsSocketClosed(p_info->socket_id);
}
}
}
static int
processCommandBuffer(void *buffer, size_t buflen, RIL_SOCKET_ID socket_id) {
......
//这里就调用ril_commands.h中诸如dispatchVoid的方法。在ril_commands.h中,定义了各种命令的dispatch方法和response方法,那么哪里调用response方法呢?
pRI->pCI->dispatchFunction(p, pRI);
return 0;
}
//以{RIL_REQUEST_GET_SIM_STATUS, dispatchVoid, responseSimStatus}为例,
static void
dispatchVoid (Parcel& p, RequestInfo *pRI) {
clearPrintBuf;
printRequest(pRI->token, pRI->pCI->requestNumber);
CALL_ONREQUEST(pRI->pCI->requestNumber, NULL, 0, pRI, pRI->socket_id);
}
CALL_ONREQUEST在ril.cpp中定义宏,实际调用s_callbacks中的onRequest
#define CALL_ONREQUEST(a, b, c, d, e) s_callbacks.onRequest((a), (b), (c), (d), (e))
s_callbacks在RIL_register 中由传入的参数赋值,这个参数值是RIL_Init方法的返回值,实质就是reference-ril.c中的
static const RIL_RadioFunctions s_callbacks = {
RIL_VERSION,
onRequest,
currentState,
onSupports,
onCancel,
getVersion
};
根据RIL_RadioFunctions的定义,s_callbacks.onRequest指向onRequest方法:
static void
onRequest (int request, void *data, size_t datalen, RIL_Token t)
{
......
switch (request) {
case RIL_REQUEST_GET_SIM_STATUS: {
RIL_CardStatus_v6 *p_card_status;
char *p_buffer;
int buffer_size;
int result = getCardStatus(&p_card_status);
if (result == RIL_E_SUCCESS) {
p_buffer = (char *)p_card_status;
buffer_size = sizeof(*p_card_status);
} else {
p_buffer = NULL;
buffer_size = 0;
}
//调用命令response方法,将结果上报给RILJ
RIL_onRequestComplete(t, result, p_buffer, buffer_size);
freeCardStatus(p_card_status);
break;
}
......
}
二、看看RILD是如何把数据传给modem的
getCardStatus中调用getSIMStatus获取sim状态,getSIMStatus中调用at_send_command_singleline并传入具体的AT命令参数和处理结果的返回地址&p_response。
static SIM_Status
getSIMStatus()
{
ATResponse *p_response = NULL;
......
err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response);
}
接着的调用流程是:at_send_command_singleline–》at_send_command_full–》at_send_command_full_nolock–》writeline,也就是说,RILD最终通过writeline把数据写到了管道中,然后就等待管道的数据更新,更新了就把结果传给p_response
static int at_send_command_full_nolock (const char *command, ATCommandType type,
const char *responsePrefix, const char *smspdu,
long long timeoutMsec, ATResponse **pp_outResponse)
{
......
err = writeline (command);
//开辟一个接受modem反馈的数据块
sp_response = at_response_new();
//循环等待modem把数据写入到fd中,s_tid_reader线程把fd中的数据读出来之后,写入到sp_response->finalResponse,并发出条件信号s_commandcond,s_readerClosed被置为1,循环终止
while (sp_response->finalResponse == NULL && s_readerClosed == 0) {
if (timeoutMsec != 0) {
#ifdef USE_NP
err = pthread_cond_timeout_np(&s_commandcond, &s_commandmutex, timeoutMsec);
#else
err = pthread_cond_timedwait(&s_commandcond, &s_commandmutex, &ts);
#endif
} else {
err = pthread_cond_wait(&s_commandcond, &s_commandmutex);
}
if (err == ETIMEDOUT) {
err = AT_ERROR_TIMEOUT;
goto error;
}
}
//把读取到的数据sp_response交给参数pp_outResponse
if (pp_outResponse == NULL) {
at_response_free(sp_response);
} else {
reverseIntermediates(sp_response);
*pp_outResponse = sp_response;
}
......
return err;
}
static int writeline (const char *s)
{
......
while (cur < len) {
do {
//s_fd在at_open时传入,整个写操作是在s_tid_mainloop线程中进行
written = write (s_fd, s + cur, len - cur);
} while (written < 0 && errno == EINTR);
......
}
//写入结束符
do {
written = write (s_fd, "\r" , 1);
} while ((written < 0 && errno == EINTR) || (written == 0));
return 0;
}
三、看看RILD是如何从modem中读取数据的。
前文的RIL_Init中,定义了线程s_tid_mainloop,线程的循环体mainLoop调用at_open(fd, onUnsolicited)打开通道,在at_open中,又创建s_tid_reader线程,线程的循环体中调用readline从s_fd中读取。
int at_open(int fd, ATUnsolHandler h)
{
//传入的at命令读写通道保存到s_fd,unsolicited命令的回调处理保存到s_unsolHandler
s_fd = fd;
s_unsolHandler = h;
s_readerClosed = 0;
......
//创建s_tid_reader读取fd的线程,创建成功调用readerLoop
ret = pthread_create(&s_tid_reader, &attr, readerLoop, &attr);
return 0;
}
static void *readerLoop(void *arg)
{
for (;;) {
const char * line;
line = readline();
if(isSMSUnsolicited(line)) {
......
//如果是短信,调用onUnsolicited
if (s_unsolHandler != NULL) {
s_unsolHandler (line1, line2);
}
} else {
processLine(line);
}
}
......
return NULL;
}
static void processLine(const char *line)
{
pthread_mutex_lock(&s_commandmutex);
if (sp_response == NULL) {
//处理unsolicited消息
handleUnsolicited(line);
} else if (isFinalResponseSuccess(line)) {
sp_response->success = 1;
//处理solicited消息
handleFinalResponse(line);
}
......
}
static void handleFinalResponse(const char *line)
{
//把结果写入到sp_response->finalResponse,然后发出条件信号s_commandcond,这个条件是在at_send_command_full_nolock中会使用到
sp_response->finalResponse = strdup(line);
pthread_cond_signal(&s_commandcond);
}
static const char *readline()
{
......
do {
//writeline中,向s_fd写入数据,readline中读取数据
count = read(s_fd, p_read,
MAX_AT_RESPONSE - (p_read - s_ATBuffer));
} while (count < 0 && errno == EINTR);
return ret;
}
static void onUnsolicited (const char *s, const char *sms_pdu)
{
......
//如果是时区命令
if (strStartsWith(s, "%CTZV:")) {
if (err != 0) {
RLOGE("invalid NITZ line %s\n", s);
} else {
RIL_onUnsolicitedResponse (
RIL_UNSOL_NITZ_TIME_RECEIVED,
response, strlen(response));
}
//来电等命令
} else if (strStartsWith(s,"+CRING:")
|| strStartsWith(s,"RING")
|| strStartsWith(s,"NO CARRIER")
|| strStartsWith(s,"+CCWA")
) {
RIL_onUnsolicitedResponse (
RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED,
NULL, 0);
#ifdef WORKAROUND_FAKE_CGEV
RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL); //TODO use new function
#endif /* WORKAROUND_FAKE_CGEV */
}else if {
......
}
......
}
RIL_onUnsolicitedResponse是定义在reference-ril.c的宏,指向s_rilenv->OnUnsolicitedResponse,而s_rilenv是在RIL_Init中初始化的,实质指向rild.c中的s_rilEnv ,这样RIL_onUnsolicitedResponse最终调用RIL_onUnsolicitedResponse。
#define RIL_onUnsolicitedResponse(a,b,c) s_rilenv->OnUnsolicitedResponse(a,b,c)
四、接着要看看RILD如何把请求结果反馈给RILJ
RILD得到modem的数据后,就回调到了RIL_onRequestComplete
extern "C" void
RIL_onRequestComplete(RIL_Token t, RIL_Errno e, void *response, size_t responselen) {
//获取相应的socket通道
int fd = s_ril_param_socket.fdCommand;
size_t errorOffset;
RIL_SOCKET_ID socket_id = RIL_SOCKET_1;
pRI = (RequestInfo *)t;
socket_id = pRI->socket_id;
......
#endif
......
if (pRI->cancelled == 0) {
......
if (response != NULL) {
ret = pRI->pCI->responseFunction(p, response, responselen);
......
}
......
//sendResponse调用sendResponseRaw
sendResponse(p, socket_id);
}
done:
free(pRI);
}
static int
sendResponseRaw (const void *data, size_t dataSize, RIL_SOCKET_ID socket_id) {
//获取socket 1的文件描述符
int fd = s_ril_param_socket.fdCommand;
......
//数据最大8K
if (dataSize > MAX_COMMAND_BYTES) {
return -1;
}
//写头,写数据
ret = blockingWrite(fd, (void *)&header, sizeof(header));
ret = blockingWrite(fd, data, dataSize);
}
//最终把数据写入到相应的socket中
static int
blockingWrite(int fd, const void *buffer, size_t len) {
......
while (writeOffset < len) {
ssize_t written;
do {
//往相应的socket中写入数据
written = write (fd, toWrite + writeOffset,
len - writeOffset);
} while (written < 0 && ((errno == EINTR) || (errno == EAGAIN)));
......
return 0;
}