在当今的网络时代,我们常常见到的进程间通信方式都是socket,比如Java的EJB调用,Java和C通信,Web Service服务等。socket是最常用的通讯技术,几乎所有的系统、语言都支持,socket也是面向网络的,通信的两方可以跨越IP网络进行传输。
在本地通信中(同一台机器上的进程间通讯),socket的网络特性却成了累赘,组装解析网络报头、报文确认、CRC校验等都是针对网络的,本地通信没有必要,反而会影响传输效率。本地通信的一些传统技术,如管道、FIFO、消息队列等,没有网络功能的负担,传输速度应该高于socket,那到底高多少以至于值得在应用中替换socket技术呢,今天就来一场小测试,就System V消息队列和socket之间,做一次全面的速度比拼。
比拼场地
本人的笔记本:赛扬1.5G 内存1.5G
系统:Ubuntu8.04 Desktop (Linux 2.6.24-24-generic)
JDK:1.6
第一回合: Java测试
先说明一下,Java并不支持System V消息队列,因此特为Java提供了JNI接口,我们使用lajp_9.09提供C源码编译的so动态连接库,lajp的下载地址和文档:http://code.google.com/p/lajp/
首先上场的是System V消息队列。
发送端程序:
-
package test;
-
-
import lajp.MsgQ;
-
-
public
class TestSend
-
{
-
/** 消息队列KEY */
-
static
final int IPC_KEY = 0×20021230;
-
-
static
-
{
-
//JNI
-
System.loadLibrary("lajpmsgq");
-
}
-
-
public
static void main(String[]
args)
-
{
-
//创建或获得现有的消息队列
-
int msqid = MsgQ.msgget(IPC_KEY);
-
//发送字节数组
-
byte[] msg =
new byte[1024];
-
-
for
(int i =
0; i <
1024 * 5000; i++)
-
{
-
//每次发送1204字节到消息队列,9527是消息类型
-
MsgQ.msgsnd(msqid,
9527, msg, msg.length);
-
}
-
-
-
}
-
}
接收端程序:
-
package test;
-
-
import lajp.MsgQ;
-
-
public
class TestRcv
-
{
-
/** 消息队列KEY */
-
static
final int IPC_KEY = 0×20021230;
-
-
static
-
{
-
//JNI
-
System.loadLibrary("lajpmsgq");
-
}
-
-
public
static void main(String[]
args)
-
{
-
//创建或获得现有的消息队列
-
int msqid = MsgQ.msgget(IPC_KEY);
-
//接收缓冲区
-
byte[] msg =
new byte[1024];
-
-
long start =
System.currentTimeMillis();
//开始时间
-
-
for
(int i =
0; i <
1024 * 5000; i++)
-
{
-
//每次从消息队列中接收消息类型为9527的消息,接收1204字节
-
MsgQ.msgrcv(msqid, msg, msg.length,
9527);
-
}
-
-
long end =
System.currentTimeMillis();
//结束时间
-
System.out.println("用时:"
+ (end – start) +
"毫秒");
-
}
-
}
程序很简单,需要说明的是三个JNI方法调用:
msgget()方法: System V消息队列的技术要求,含义是通过一个指定的KEY获得消息队列标识符。
msgsnd()方法: 发送。
msgrcv()方法: 接收。
发送方进行了(1024 * 5000)次发送,每次发送1024字节数据,接收方进行了(1024 * 5000)次接收,每次接收1024字节,共计发送接收5G数据。测试时先启动TestSend程序,再启动TestRcv程序,共进行5轮次测试,测试结果如下:
用时:29846毫秒
用时:29591毫秒
用时:29935毫秒
用时:29730毫秒
用时:29468毫秒
平均速度:29714毫秒
用top命令监控测试期间的CPU、内存的使用:
接下来上场的是socket。
发送端程序:
-
import java.io.IOException;
-
import java.io.OutputStream;
-
import java.net.Socket;
-
-
public
class SocketSend
-
{
-
-
{
-
//Socket
-
-
//输出流
-
-
//发送字节数组
-
byte[] msg =
new byte[1024];
-
-
long start =
System.currentTimeMillis();
//开始时间
-
-
for
(int i =
0; i <
1024 * 5000; i++)
-
{
-
//发送
-
out.write(msg);
-
}
-
-
long end =
System.currentTimeMillis();
//结束时间
-
System.out.println("用时:"
+ (end – start) +
"毫秒");
-
}
-
}
接收端程序:
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.net.ServerSocket;
-
import java.net.Socket;
-
-
public
class SocketRecv
-
{
-
-
{
-
//侦听9527端口
-
-
//Socket
-
Socket
socket = serverSocket.accept();
-
//输入流
-
-
//接收缓冲区
-
byte[] msg =
new byte[1024];
-
-
for
(int i =
0; i <
1024 * 5000; i++)
-
{
-
//每次接收1204字节
-
in.read(msg);
-
}
-
-
-
}
-
}
程序同样很简单,同样发送接收了(1024 * 5000)次,同样5G数据,socket程序必须先启动服务方SocketRecv,然后启动客户方SocketSend,共进行5轮次测试,测试结果如下:
用时:33951毫秒
用时:33448毫秒
用时:33987毫秒
用时:34638毫秒
用时:33957毫秒
平均速度:33996.2毫秒
用top命令监控测试期间的CPU、内存的使用:
测试结果让人对消息队列有点失望,性能优势微弱大约只领先了13%,且程序复杂性要大的多(使用了JNI)。不过重新审视测试过程有一个疑问:消息队列程序调用了自定义的JNI接口,而socket是Java内嵌的功能,是否JVM对 socket有特殊的优化呢?
怀着这个疑问,进行第二场纯C程序的测试。
第二回合: C程序测试
首先上场的还是System V消息队列。
发送端程序:
-
#include <sys/ipc.h>
-
#include <sys/msg.h>
-
#include <stdio.h>
-
-
#define IPC_KEY 0×20021230 /* 消息队列KEY */
-
-
/*消息结构*/
-
struct message
-
{
-
long msg_type; /* 消息标识符 */
-
char msg_text[1024];
/* 消息内容 */
-
};
-
-
int main()
-
{
-
/* 创建或获得现有的消息队列 */
-
int msqid = msgget(IPC_KEY, IPC_CREAT |
0666);
-
/* 消息结构 */
-
struct message msgq;
-
msgq.msg_type =
9527;
/* 消息类型 */
-
-
int i;
-
for
(i =
0; i < 1024 *
5000; i++)
-
{
-
/* 接收 */
-
msgsnd(msqid, &msgq,
1024,
0);
-
}
-
-
printf("msgq发送结束,共发送%d次\n",
i);
-
return
0;
-
}
接收端程序:
-
#include <sys/ipc.h>
-
#include <sys/msg.h>
-
#include <stdio.h>
-
-
#define IPC_KEY 0×20021230 /* 消息队列KEY */
-
-
/*消息结构*/
-
struct message
-
{
-
long msg_type; /* 消息标识符 */
-
char msg_text[1024];
/* 消息内容 */
-
};
-
-
int main()
-
{
-
/* 创建或获得现有的消息队列 */
-
int msqid = msgget(IPC_KEY, IPC_CREAT |
0666);
-
/* 消息结构 */
-
struct message msgq;
-
-
int i;
-
for
(i =
0; i < 1024 *
5000; i++)
-
{
-
/* 接收 */
-
msgrcv(msqid, &msgq,
1024,
9527, 0);
-
}
-
-
printf("msgq接收结束,共接收%d次\n",
i);
-
return
0;
-
}
和第一场一样,发送接收了(1024 * 5000)次,同样5G数据,先启动接收端程序msgrecv,然后以$time msgsend方式启动客户端程序,共进行5轮次测试,time的测试结果如下:
用户 系统 时钟
第一次: 0.992s 7.084s 18.202s
第二次: 0.888s 7.280s 18.815s
第三次: 1.060s 7.656s 19.476s
第四次: 1.048s 7.124s 20.293s
第五次: 1.008s 7.160s 18.655s
用top命令监控测试期间的CPU、内存的使用:
接下来上场的是socket。
发送端程序:
-
#include <stdio.h>
-
#include <string.h>
-
#include <netdb.h>
-
-
char msg[1024];
/* 发送消息 */
-
-
int main()
-
{
-
char *ip =
"127.0.0.1";
/* 发送地址 */
-
int port =
9527;
/* 发送端口 */
-
-
struct hostent *server_host = gethostbyname(ip);
-
-
/* 客户端填充 sockaddr 结构 */
-
struct sockaddr_in client_addr;
/* 客户端地址结构 */
-
bzero(&client_addr,
sizeof(client_addr));
-
client_addr.sin_family = AF_INET;
/* AF_INET:IPV4协议 */
-
client_addr.sin_addr.s_addr =
((struct in_addr *)(server_host->h_addr))->s_addr;
/* 服务端地址 */
-
client_addr.sin_port = htons(port);
/* 端口 */
-
-
/* 建立socket */
-
int sockfd = socket(AF_INET, SOCK_STREAM,
0);
-
/* 连接 */
-
connect(sockfd,
(struct sockaddr *)(&client_addr),
sizeof(client_addr));
-
-
int i;
-
for
(i =
0; i < 1024 *
5000; i++)
-
{
-
/* 发送 */
-
send(sockfd, msg,
1024,
0);
-
}
-
-
-
return
0;
-
}
接收端程序:
-
#include <stdio.h>
-
#include <string.h>
-
#include <netdb.h>
-
-
char msg[1024];
/* 接收缓冲区 */
-
-
int main()
-
{
-
int listen_port =
9527;
/* 侦听端口 */
-
int listenfd = socket(AF_INET, SOCK_STREAM,
0);
/* 建立侦听socket */
-
-
/* 服务端填充 sockaddr 结构 */
-
struct sockaddr_in server_addr;
/* 服务端地址结构 */
-
bzero(&server_addr,
sizeof(server_addr));
-
server_addr.sin_family = AF_INET;
/* AF_INET:IPV4协议 */
-
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
/* INADDR_ANY:通配地址,表示内核选择IP地址 */
-
server_addr.sin_port = htons(listen_port);
/* 端口 */
-
-
/* 绑定端口 */
-
bind(listenfd,
(struct sockaddr *)(&server_addr),
sizeof(server_addr));
-
/* 侦听 */
-
listen(listenfd,
5);
-
int sockfd = accept(listenfd,
NULL, NULL);
-
-
int i;
-
for
(i =
0; i < 1024 *
5000; i++)
-
{
-
/* 接收 */
-
recv(sockfd, msg,
1024,
0);
-
}
-
-
-
return
0;
-
}
C语言中,socket程序复杂了不少。测试标准和Java相同,发送接收了(1024 * 5000)次,5G数据,先启动接收端程序,然后以time方式启动发送端,测试结果如下:
用户 系统 时钟
第一次: 0.524s 9.765s 20.666s
第二次: 0.492s 9.825s 20.530s
第三次: 0.468s 9.493s 21.831s
第四次: 0.512s 9.205s 20.059s
第五次: 0.440s 9.605s 21.888s
用top命令监控测试期间的CPU、内存的使用:
C语言的socket程序系统用时多一些,消息队列程序用户用时多一些,这和他们的实现方式相关,从时钟比较看,消息队列比socket快10%左右,和Java测试结果相似。比较Java和C,C只领先了三分之一,看来当前的Java效率已经相当高了。
还不能忙于下结论,socket的通信方式一般有两种:长连接和短连接。长连接指发送端和接收端建立连接后,可以保持socket通道进行多次消息传输,在这种场景基本不用计算socket建立和关闭的时间,前面的测试都是基于长连接方式;短连接一般在建立socket通道后,只进行一次通信,然后就关闭 socket通道,这种场景必须考虑socket建立和关闭的时间(socket建立连接需要三次握手,关闭连接要四次通信)。
第三回合: Java测试(短连接)
将第一回合中的Java程序稍作修改,先看socket的:
发送端程序:
-
import java.io.IOException;
-
import java.io.OutputStream;
-
import java.net.Socket;
-
-
public
class SocketSend2
-
{
-
-
{
-
long start =
System.currentTimeMillis();
//开始时间
-
//发送字节数组
-
byte[] msg =
new byte[1024];
-
-
for
(int i =
0; i <
1024 * 1000; i++)
-
{
-
//建立Socket连接
-
-
//输出流
-
-
//发送
-
out.write(msg);
-
-
//关闭输出流
-
out.close();
-
//关闭socket连接
-
socket.close();
-
}
-
-
long end =
System.currentTimeMillis();
//结束时间
-
System.out.println("用时:"
+ (end – start) +
"毫秒");
-
}
-
}
建立socket的语句放在了循环内部,这样每次发送都是新建的连接,025行的关闭语句是必须的,因为socket是系统的有限资源,支持不了这么大规模的申请。
接收端程序:
-
import java.io.IOException;
-
import java.io.InputStream;
-
import java.net.ServerSocket;
-
import java.net.Socket;
-
-
public
class SocketRecv2
-
{
-
-
{
-
//侦听9527端口
-
-
//接收缓冲区
-
byte[] msg =
new byte[1024];
-
-
for
(int i =
0; i <
1024 * 1000; i++)
-
{
-
//接到客户端Socket连接请求
-
Socket
socket = serverSocket.accept();
-
//输入流
-
-
//每次接收1204字节
-
in.read(msg);
-
-
//关闭输入流
-
in.close();
-
//关闭socket连接
-
socket.close();
-
}
-
-
-
}
-
}
接收端也做了相应的改动,发送和接收次数降低到(1024 * 1000)次,测试结果:431280毫秒,不要吃惊,没错是431.280秒,这也是书本上为什么总在强调使用数据库连接池的原因。
消息队列没有像socket那样的连接概念,为了做个参考,将第一回合中的消息队列程序也修改一下:
发送端程序:
-
package test;
-
-
import lajp.MsgQ;
-
-
public
class TestSend2
-
{
-
/** 消息队列KEY */
-
static
final int IPC_KEY = 0×20021230;
-
-
static
-
{
-
//JNI
-
System.loadLibrary("lajpmsgq");
-
}
-
-
public
static void main(String[]
args)
-
{
-
//发送字节数组
-
byte[] msg =
new byte[1024];
-
-
for
(int i =
0; i <
1024 * 1000; i++)
-
{
-
//创建或获得现有的消息队列
-
int msqid = MsgQ.msgget(IPC_KEY);
-
-
//每次发送1204字节
-
MsgQ.msgsnd(msqid,
9527, msg, msg.length);
-
}
-
-
-
}
-
}
将024行的msgget()方法放在循环内部,作为和socket比较的“连接”。
接收段程序:
-
package test;
-
-
import lajp.MsgQ;
-
-
public
class TestRcv2
-
{
-
/** 消息队列KEY */
-
static
final int IPC_KEY = 0×20021230;
-
-
static
-
{
-
//JNI
-
System.loadLibrary("lajpmsgq");
-
}
-
-
public
static void main(String[]
args)
-
{
-
long start =
System.currentTimeMillis();
//开始时间
-
//接收缓冲区
-
byte[] msg =
new byte[1024];
-
-
for
(int i =
0; i <
1024 * 1000; i++)
-
{
-
//创建或获得现有的消息队列
-
int msqid = MsgQ.msgget(IPC_KEY);
-
-
//每次接收1204字节
-
MsgQ.msgrcv(msqid, msg, msg.length,
9527);
-
}
-
-
long end =
System.currentTimeMillis();
//结束时间
-
System.out.println("用时:"
+ (end – start) +
"毫秒");
-
}
-
}
测试结果:6617毫秒。
总结:
在能够使用socket长连接的应用中,建议使用socket技术,毕竟很通用熟悉的人也多,而消息队列能够提高的效率有限;在只能使用socket短连接的应用中,特别是并发量大的场景,强烈建议使用消息队列,因为能够极大的提高通信速率。
分享到:
相关推荐
本资源包含四个文件,一个makefile,一个头文件,一个发送端一个接收端。发送端读取指定的文件,并且按照环境变量中设置的消息队列...两个程序都能在linux下 跑通,要编译 直接make就行。 只想要1积分,CSDN不让我改。
这是在linux下用socket,子进程,消息队列编写的简单的客户端和服务器间通信的程序,客户端发送数据给服务器,服务器使用消息队列把消息存到文件中,用make运行Makefile编译程序
linux下消息队列例程源码
linux下的消息队列编程函数指南,详细说明了消息队列建立的过程,发送,接受,和控制
Linux消息队列 ---------学习linux下 c编程之消息队列
在Linux下通过消息队列机制实现双方通信
简单实现消息队列,能够做到创建消息队列,获取消息队列,对于像我一样的萌新,可以快速的学习Socket,并对他的应用有一定的认识,方便快速上手消息队列,本人技术有限,可能不够完善,欢迎留言补充,欢迎大佬指教。
linux环境下消息队列的实现
这资源是在linux下用C语言开发的模拟ATM机开户、取款与款的源代码(这是服务器端代码,客户器比较简单,...主要应用了Linux多线程,Select监听,还有Linux与Window之间应用Socket与消息队列并行通行的交互基础框架...
使用linux消息队列实现进程间双向通信。本接口将消息接收封装在一个独立线程中,方便使用。
Linux消息队列分析及应用
自己编写,测试过的linux消息队列,可以直接使用。
linux消息队列信息交互,终端下实现的信息发送与接收
linux消息队列linux消息队列linux消息队列linux消息队列linux消息队列进程间通信进程间通信
Unix环境下利用Socket和消息队列构建应用通信平台
Linux 环境下利用消息队列消息机制,多线程通信,字符串处理,链表操作,信号简单处理等知识用C语言编写多人聊天室实现: 服务器实现各用户之间聊天的消息转发,在用户注册或者登录时对各用户进行消息提醒,客户端从...
Linux下的消息队列程序,helloworld MessageTest
Linux下的MSG操作,测试程序。可以供刚学习的参考,希望对大家有帮助!!!
redis 案例。包含, 队列操作, socket通信, 以及 socket 和 redis 配合 redis 案例。包含, 队列操作, socket通信, 以及 socket 和 redis 配合
Linux消息队列分析及应用.pdf