使用tcp socket进行跨进程/网络通信
来源:互联网 发布:有情有趣软件 编辑:程序博客网 时间:2024/05/16 01:44
看了陈硕的书,说虽然有pipe, msgget, message queue, unix domain socket, 还是建议进程间只用tcp socket来通信。
pipe的缺点是阻塞。msgget缺点是不能select。mq_send可以,但是双向通信要开两个mq。unix domain不能跨网络。tcp socket优点很多,就是处理分包比较麻烦些,不过可以抽象出来。根据我的项目需要,自己设计的数据封包格式为:
MSG -- 3 Bytescmd -- 1 Byteulen -- 4 Bytesclen -- 4 Bytesurl --- ulen Bytescontent -- clen Bytes
陈硕在最后加了adler32 checksum, 我为了简便没有;为了便于找到包的开始位置,我参考了mpeg pes的sync word概念,引入"MSG"作为magic number标识。
附代码:
/*IPC using tcp socket test: gcc -g -Wall stream_buffer.c -DTEST_MSG_BUFFER ./a.out ./a.out 127.0.0.1*/#include <stdio.h>#include <string.h>#include <stdlib.h>#include <errno.h>#include <stdint.h>#include <unistd.h>#include <fcntl.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h>#include <arpa/inet.h>#include <poll.h>#ifndef AV_RB32#define AV_RB32(x) \ (((uint32_t)((const uint8_t*)(x))[0] << 24) | \ (((const uint8_t*)(x))[1] << 16) | \ (((const uint8_t*)(x))[2] << 8) | \ ((const uint8_t*)(x))[3])#endif#ifndef AV_WB32#define AV_WB32(p, darg) do { \ unsigned d = (darg); \ ((uint8_t*)(p))[3] = (d); \ ((uint8_t*)(p))[2] = (d)>>8; \ ((uint8_t*)(p))[1] = (d)>>16; \ ((uint8_t*)(p))[0] = (d)>>24; \ } while(0)#endiftypedef struct{int cmd;int ulen, clen;uint8_t *url, *content;}ctrl_msg_t;typedef struct{int rpos, wpos;int msize;uint8_t *buf;}StreamBuffer;static int ctrl_fd, serv_fd;static StreamBuffer *sb_in, *sb_out;StreamBuffer* sb_init(int msize){StreamBuffer *s = NULL;if(msize < 1){return NULL;}s = malloc(sizeof(*s));if(!s){return NULL;}s->rpos = s->wpos = 0;s->msize = msize;s->buf = malloc(s->msize);if(!s->buf){free(s);return NULL;}return s;}int sb_destroy(StreamBuffer *s){if(s && s->buf){free(s->buf);}if(s){free(s);}return 0;}int sb_write(StreamBuffer *s, uint8_t *data, int len){/*return < 0 means fail.*/int size;if(!s || !data || len < 0){return -1;}if(s->msize - s->wpos >= len){memcpy(s->buf + s->wpos, data, len);s->wpos += len;}else if (s->msize - s->wpos + s->rpos >= len){size = s->wpos - s->rpos;memmove(s->buf, s->buf+s->rpos, size);s->rpos = 0;s->wpos = size;memcpy(s->buf+s->wpos, data, len);s->wpos += len;}else{printf("sb buf full\n");return -1;}return 0;}int sb_read(StreamBuffer *s, uint8_t *data, int len){/*read actual read bytes.*/int size;if(!s || !data || len < 0){return 0;}size = s->wpos - s->rpos;if(size > len){size = len;}memcpy(data, s->buf+s->rpos, size);s->rpos += size;return size;}static int ctl_msg_cb(ctrl_msg_t *msg){printf("%s: %d '%s' '%s'\n", (serv_fd ? "Server" : "Client"), msg->cmd, msg->url, msg->content);return 0;}int ctl_msg_open(int server_fd){ struct sockaddr_in from_addr; socklen_t len; int fd; len = sizeof(from_addr);memset(&from_addr, 0, len); fd = accept(server_fd, (struct sockaddr *)&from_addr, &len); if (fd < 0) { printf("error setup during accept %s\n", strerror(errno)); return -1; }printf("new conn %s:%u\n", inet_ntoa(from_addr.sin_addr), ntohs(from_addr.sin_port)); if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0){ printf("set non-block failed\n"); }ctrl_fd = fd;return fd;}int ctl_msg_recv(void){int len;uint8_t buf[1024];while( (len = recv(ctrl_fd, buf, sizeof(buf), 0)) > 0){sb_write(sb_in, buf, len);}return 0;}int ctl_msg_send(void){int len;uint8_t *ptr = NULL;StreamBuffer *sb = sb_out;if(!sb){return -1;}len = sb->wpos - sb->rpos;if(len <= 0){return 1;}ptr = sb->buf + sb->rpos;len = send(ctrl_fd, ptr, len, 0);if(len > 0){sb->rpos += len;}return 0;}int ff_ctl_open(unsigned short port){ int fd, tmp;struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port); fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror ("socket"); return -1; } tmp = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp))) printf("setsockopt SO_REUSEADDR failed\n"); if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0){ printf("cant bind\n"); close(fd); return -1; } if(listen(fd, 5) < 0){ perror ("listen"); close(fd); return -1; } if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0){ printf("set non block failed\n"); }serv_fd = fd;sb_in = sb_init(8096);sb_out = sb_init(8096); return fd;}int ff_ctl_open2(char *ip, unsigned short port){ int fd;struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port);if(inet_aton(ip, &addr.sin_addr) == 0){ printf("bad ip '%s'\n", ip); return -1; } fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror ("socket"); return -1; } if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0){ printf("cant connect to ip '%s'\n", ip); return -1; } if(fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK) < 0){ printf("set non block failed\n"); }ctrl_fd = fd;sb_in = sb_init(8096);sb_out = sb_init(8096); return fd;}int ff_ctl_recv(void){/*unpack sb_in and dispatch messages in it.*/StreamBuffer *sb = sb_in;uint8_t *ptr, *end;static ctrl_msg_t msg = {0};if(!sb){return -1;}ptr = sb->buf + sb->rpos;end = sb->buf + sb->wpos;while(ptr + 4 < end){if(msg.url)goto content;if(msg.clen)goto url;if(msg.ulen)goto clen;if(msg.cmd)goto ulen;if(memcmp(ptr, "MSG", 3)){ptr += 3;continue;}ptr += 3;msg.cmd = ptr[0];ptr += 1;if(ptr >= end)break;ulen:msg.ulen = AV_RB32(ptr);ptr += 4;if(ptr >= end)break;clen:msg.clen = AV_RB32(ptr);ptr += 4;if(ptr >= end)break;url:if(msg.ulen > 0 && ptr + msg.ulen <= end){msg.url = malloc(msg.ulen+1);memcpy(msg.url, ptr, msg.ulen);msg.url[msg.ulen] = 0;ptr += msg.ulen;}if(ptr >= end)break;content:if(msg.clen > 0 && ptr + msg.clen <= end){msg.content = malloc(msg.clen+1);memcpy(msg.content, ptr, msg.clen);msg.content[msg.clen] = 0;ptr += msg.clen;ctl_msg_cb(&msg);free(msg.url);free(msg.content);memset(&msg, 0, sizeof(msg));}}sb->rpos = ptr - sb->buf;return 0;}int ff_ctl_send(int cmd, uint8_t *url, uint8_t *content){int ulen, clen;uint8_t *ptr, buf[64];StreamBuffer *sb = sb_out;if(!sb || !(0 <= cmd && cmd <= 9)){return -1;}ulen = strlen((char*)url);clen = strlen((char*)content);ptr = buf;ptr += sprintf((char*)ptr, "MSG");*ptr = (uint8_t)cmd;ptr += 1;AV_WB32(ptr, ulen);ptr += 4;AV_WB32(ptr, clen);ptr += 4;sb_write(sb, buf, ptr-buf);sb_write(sb, url, ulen);sb_write(sb, content, clen);return 0;}#if defined(TEST_MSG_BUFFER)static int strip(char *str){int n = strlen(str);while(n > 0 && (str[n-1] == '\r' || str[n-1] == '\n')){str[--n] = 0;}return n;}int main(int ac, char **av){int ret, fd, is_server = 1;int peer_fd = 0;if(ac != 2){printf("start server\n");}else{is_server = 0;}if(is_server){fd = ff_ctl_open(5678);}else{fd = ff_ctl_open2(av[1], 5678);}struct pollfd *entry, table[8] = {{0}};for(;;) {entry = table;if(fd){ entry->fd = fd; entry->events = POLLIN|POLLOUT; entry++;}if(peer_fd){entry = table;entry->fd = peer_fd; entry->events = POLLIN|POLLOUT; entry++;}do { ret = poll(table, entry - table, 1000); } while (ret < 0);for(entry = table; entry->fd; ++entry){if(entry->revents & POLLIN){if(is_server && entry->fd == fd && peer_fd <= 0){peer_fd = ctl_msg_open(fd);}else{ctl_msg_recv();ff_ctl_recv();}}else if(entry->revents & POLLOUT){char line[128] = "";printf("> "); fflush(stdout);fgets(line, sizeof(line), stdin);strip(line);ff_ctl_send(2, (uint8_t*)"cmd", (uint8_t*)line);ctl_msg_send();}}}}#endif
服务端和客户端都会阻塞在fgets,敲了回车后才显示对方的消息。可以修改为开线程输入。不过我最终的应用场景不是处理这个,我自测试的目的达到了。
0 0
- 使用tcp socket进行跨进程/网络通信
- Java TCP使用Socket进行网络通信(3)
- 使用AIDL来进行跨进程通信
- Android中基于TCP协议的网络通信之使用Socket进行通信
- Android中基于TCP协议的网络通信之使用Socket进行通信
- socket网络通信(tcp)
- socket网络通信(tcp)
- Java TCP使用Socket进行网络通信(4)之往返发送
- Java 网络编程(五) 使用TCP/IP的套接字(Socket)进行通信
- Android 使用Socket进行网络通信
- Android:使用AIDL来进行跨进程通信
- 使用Unix域套接字进行跨进程通信
- 【Android学习之路】使用AIDL进行跨进程通信
- 安卓中用TCP跨进程通信
- 使用TCP/IP的套接字(Socket)进行通信
- 使用TCP/IP的套接字(Socket)进行通信
- 使用TCP/IP的套接字(Socket)进行通信
- 使用TCP/IP的套接字(Socket)进行通信
- Eclipse使用
- 《Linux高性能服务器编程》学习笔记——第八章 高性能服务器程序框架
- c++中对象和类的概念以及联系
- 继承--抽象类--接口--子类构造方法代码执行顺序
- Torry的困惑(基本型)
- 使用tcp socket进行跨进程/网络通信
- 第三方支付接口示例代码
- cortex_m3_stm32嵌入式学习笔记(五):独立看门狗实验(IWDG)
- 趣说java处理异常的方式
- 人人都能看懂的工厂方法模式和抽象工厂模式的区别
- 渴就蒲寿允粟狄期秩谔握嗜烂偎坦
- 接口--多态--多态的转型以及方法的调用问题
- 陨接脖着瓮荷备杂植凑吃字允搪耪
- Java入门需掌握的30个基本概念