/* test_ipc_harness.c — 宿主 IPC 服务端模拟(等价 launcher ipc_dispatch 的媒体子集). * * 用 meow_media.c + meow_ipc.h 复刻 launcher 的 IPC 服务器对媒体消息的处理, * 供「qemu-mipsel meow_ipc listen/meta/playctl」端到端验证: * listen → HELLO(MF_PLAYBACK)+META 注册源并等待下发; * meta → 上报 META(作为新源/更新); * playctl → 上传控制,服务端转发给「最近活跃会话」fd。 * 运行:gcc test_ipc_harness.c meow_media.c -o /tmp/meow_srv ; /tmp/meow_srv /tmp/e2e.sock */ #include #include #include #include #include #include #include #include #include #include #include #include "meow_ipc.h" #include "meow_media.h" #define MAXCLI 8 typedef struct { int fd; uint8_t hdr[MEOW_IPC_HDR_LEN]; int hdr_n; uint8_t pay[MEOW_IPC_MAX]; uint32_t pay_tot, pay_n; char app_id[MEOW_APP_ID_MAX]; } conn; static conn g_c[MAXCLI]; static size_t srv_send(int fd, uint16_t type, uint32_t req, const void *payload, uint32_t len) { if (fd < 0) return 0; uint8_t h[MEOW_IPC_HDR_LEN]; uint32_t magic = MEOW_IPC_MAGIC; uint16_t ver = MEOW_IPC_VERSION; memcpy(h+0,&magic,4); memcpy(h+4,&ver,2); memcpy(h+6,&type,2); memcpy(h+8,&req,4); memcpy(h+12,&len,4); size_t off=0; while (offhdr+6, 2); memcpy(&req, c->hdr+8, 4); switch (type) { case MSG_HELLO: { m_hello_t h; memset(&h,0,sizeof h); memcpy(&h,c->pay,c->pay_npay_n:sizeof h); snprintf(c->app_id, sizeof c->app_id, "%s", h.app_id); m_ack_t a; a.code=MEOW_OK; a.status=1; srv_send(c->fd,MSG_ACK,req,&a,sizeof a); printf("SRV: HELLO app=%s flags=%u -> ACK\n", c->app_id, h.flags); break; } case MSG_META: { m_meta_t m; memset(&m,0,sizeof m); memcpy(&m,c->pay,c->pay_npay_n:sizeof m); m.app_id[MEOW_APP_ID_MAX-1]=0; m.title[127]=0; m.artist[127]=0; m.album[127]=0; m.artwork[191]=0; m.lyric[191]=0; int rc = meow_media_meta(&m, c->fd); m_ack_t a; a.code=MEOW_OK; a.status=(uint32_t)rc; srv_send(c->fd,MSG_ACK,req,&a,sizeof a); const meow_media_session_t *cur = meow_media_current(); printf("SRV: META app=%s state=%u rc=%d | current=%s\n", m.app_id, m.state, rc, cur?cur->app_id:"(none)"); break; } case MSG_PLAYCTL: { m_playctl_t p; memset(&p,0,sizeof p); memcpy(&p,c->pay,c->pay_npay_n:sizeof p); int rc = meow_media_playctl(p.cmd, p.seek_ms); m_ack_t a; a.code=MEOW_OK; a.status=(uint32_t)rc; srv_send(c->fd,MSG_ACK,req,&a,sizeof a); printf("SRV: PLAYCTL cmd=%u seek=%lld rc=%d (target current)\n", p.cmd, (long long)p.seek_ms, rc); break; } default: { m_err_t e; memset(&e,0,sizeof e); e.code=MEOW_ERR_INVAL; e.msg_len=5; memcpy(e.msg,"nope",5); srv_send(c->fd,MSG_ERR,req,&e,sizeof e); printf("SRV: unsupported type 0x%04x\n", type); break; } } } /* 读一个连接可用字节并组装帧(>=1 完整帧即 dispatch)。返回 0=ok,-1=连接关闭。 */ static int conn_read(conn *c) { for (;;) { if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) { ssize_t r = read(c->fd, c->hdr + c->hdr_n, MEOW_IPC_HDR_LEN - c->hdr_n); if (r < 0) { if (errno==EAGAIN) return 0; return -1; } if (r == 0) return -1; c->hdr_n += (int)r; if ((unsigned)c->hdr_n < MEOW_IPC_HDR_LEN) return 0; uint32_t magic, len; uint16_t ver; memcpy(&magic,c->hdr,4); memcpy(&ver,c->hdr+4,2); memcpy(&len,c->hdr+12,4); if (magic!=MEOW_IPC_MAGIC||ver!=MEOW_IPC_VERSION||len>MEOW_IPC_MAX) return -1; c->pay_tot=len; c->pay_n=0; if (len==0){ handle(c); c->hdr_n=0; continue; } } if (c->pay_n < c->pay_tot) { ssize_t r = read(c->fd, c->pay + c->pay_n, c->pay_tot - c->pay_n); if (r < 0){ if (errno==EAGAIN) return 0; return -1; } if (r==0) return -1; c->pay_n += (uint32_t)r; if (c->pay_n < c->pay_tot) return 0; } handle(c); c->hdr_n=0; c->pay_n=0; } } int main(int argc, char **argv) { const char *path = (argc>1)?argv[1]:"/tmp/meow_ipc_e2e.sock"; for (int i=0;i=0){ fds[n].fd=g_c[i].fd; fds[n].events=POLLIN; fds[n].revents=0; n++; } nfds=n; int r=poll(fds,nfds,400); if (r<0){ if(errno==EINTR)continue; break; } if (fds[0].revents&POLLIN){ for(;;){ int c=accept(lfd,NULL,NULL); if(c<0)break; fcntl(c,F_SETFL,O_NONBLOCK); if(!alloc_slot(c)){ close(c); printf("SRV: reject conn (full)\n"); } else printf("SRV: accept fd=%d\n", c); } } for (int i=1;ifd); close(c->fd); c->fd=-1; } } } fflush(stdout); } close(lfd); unlink(path); return 0; }