上海高端品牌网站建设/中国电信视频app下载
提到进程间通信(IPC),你可能会想到很多种方式,比如:
1)消息传递(管道、FIFO、消息队列);
2)同步(互斥量、条件变量、读写锁、文件和写记录锁、信号量);
3)共享内存(匿名的和具名的);
4)远程过程调用(Solaris门和Sun RPC)。
没错,你有很多种选择,但提到消息,很多人第一印象就是 发送者(Sender)和 接收者(Receiver)。你最早接触到的消息发送方式 可能是 TCP、UDP,也可能是Socket,亦或者其他,不论哪种,其Base思想都是发送者和接收者。
消息对于软件开发至关重要,对于大型软件来讲,复杂的逻辑决定了 仅仅通过调用远远无法达到设计的目标,消息是模块化设计的一个最重要的组成部分,没有之一,经过多年的软件开发,作者很负责任的告诉你,架构设计第一步:消息及通信协议设计。
下面我们介绍 三种 消息方式:回调函数,Socket,TCP | UDP传输,其它更复杂的协议可能基于这三种方式的扩展(比如 HTTP、RTSP、SIP等),我们不做扩展介绍。
一. 回调函数
初级程序员往往不理解回调函数,回调函数简单理解,就是把一个你自己的 函数指针 A 传给另外一个函数B,在B运行到某个位置执行,由于这个执行是调用A函数来完成的,因此,称为回调。
回调函数常用在定时器,或者 发现某个条件符合的情况(比如循环或结果执行),来看一段代码:
/** 消息回调函数定义*/
typedef void (*MsgCallback)(void* pContext,char* pMsg);/** 消息发送*/
class MsgSend
{
public:/** 注册接收*/void registReceiver(MsgCallback pfnMsg,void* pContext);protected:MsgCallback m_pfnMsg; /**< 回调函数*/void* m_pContext; /**< 回调函数上下文*/
};/** 消息接收*/
class MsgReceiver
{
public:MsgReceiver(MsgSend* pSend){// 注册回调函数pSend->registReceiver(MsgCallback_Func,this);}
public:static void MsgCallback_Func(void* pContext,char* pMsg){// 根据接收的pMsg数据进行处理……}
};
回调函数必须是静态函数或者全局函数,从而确保唯一的入口地址。
二. Socket
Socket一直有着神秘的面纱,初学者望而生畏、不明所以,我们先把握两点:
1. Socket是一种进程间通讯机制;
2. Socket只是一套接口函数而已。
有了这两大利器护身,万法不侵,Socket过程可以描述为:
发送端: 接收端:
1. 初始化Socket环境; 1. 初始化Socket环境;
2. 创建Socket实例; 2. 创建Socket实例;
3. 连接Socket; 3. bind(绑定Socket)、listen(监听);
4. 数据传输 - 发送或接收; 4. Accept;
5. 数据传输 - 发送或接收;
先来看一段 Windows Socket的例子:
//-- 发送端 --
#include <winsock2.h> /** 1.初始化socket环境*/
WSAData wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);/** 2.创建socket实例*/
SOCKET sock_send = socket(AF_INET, SOCK_STREAM, 0); //TCP,字节流 /** 3.设置连接信息*/
sockaddr_in address;
address.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 本地ip 127.0.0.1
address.sin_family = AF_INET;
address.sin_port = hton(5001); // 端口/** 4.连接*/
connect(sock_send, (const struct sockaddr *)&address, sizeof(address));/** 5.发送或接收数据 - 有一组函数可用 recv()/send() recvfrom()/sendto()*/
send(sock_send,"hello linol",12,0); // int send(SOCKET s, char *buf, int len, int flags);
recv(sock_send,_buff,16,0); // int recv(SOCKET s, char *buf, int len, int flags); /** 6.关闭Socket并清理*/
closesocket(sock_send);
WSACleanup();
//-- 接收端 --
#include <winsock2.h> /** 1.初始化socket环境*/
WSADATA wsaData;
WSAStartup(MAKEWORD(1,1),&wsaData);/** 2.创建socket实例*/
SOCKET sock_recv = socket(AF_INET,SOCK_STREAM,0);/** 3.设置连接信息*/
SOCKADDR_IN addr;
addr.sin_family = AF_INET;
addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); // ip地址
addr.sin_port = htons(5001); // 绑定端口/** 4.绑定、监听*/
bind( sock_recv,(SOCKADDR*)&addr,sizeof(SOCKADDR) ); // 绑定完成
listen(sock_recv,5); // 第二个参数代表能够接收的最多的连接数SOCKADDR_IN send_addr;
while(true)
{/** 5. 接收数据,注(如果不是accept而是conection,会不断监听)*/int nLen; // 记录返回的数据长度SOCKET sock_conn = accept(sock_recv,(SOCKADDR*)&send_addr,&nLen); // SOCKET accept(SOCKET s,struct SOCKADDR *addr,int *addrlen);char* sendBuf = "linolzhang";send(sock_conn,sendBuf,strlen(sendBuf)+1,0); // 根据连接地址发送数据char recvBuf[64]; // 接收recv(sock_conn,recvBuf,sizeof(recvBuf),0);/** 6.关闭socket、清理*/closesocket(sock_recv);WSACleanup();return 0;
}
以上是一个基本的Windows下的Socket过程,Linux下Socket稍有不同(但过程大致一样),这里不再介绍。
三. TCP | UDP 传输
在通信协议中,TCP | UDP基本上算是 老掉牙的网红了,作者甚至怀疑放在这里讲有没有意义。
先来看TCP的网络图(经典的三次握手过程)。
TCP 和 UDP的区别在于:
1. TCP是可靠连接,不会丢失数据,而UDP是非可靠连接;
2. TCP的超时重传能保证发送包的顺序,而UDP不保证顺序;
3. TCP是点到点的传输,而UDP是可以一对多的。