`
suhuanzheng7784877
  • 浏览: 704254 次
  • 性别: Icon_minigender_1
  • 来自: 北京
博客专栏
Ff8d036b-05a9-33b5-828a-2633bb68b7e6
读金庸故事,品程序人生
浏览量:47748
社区版块
存档分类
最新评论

JAVA的Mina框架传递对象——一劳永逸(转载)

阅读更多

接触java的Mina框架已经有很多时间了,在网上也读过了很多的相关文章,发现Mina框架的确是一个值得拿来好好研究的东西,前些日子写了一个山寨QQ项目,其中的通信部分用到了java中自带的InputStream,OutputStream,Writer,Reader等等,感觉其中的很大的一个问题就是难以将事务处理的逻辑层与解析层分离开来,造成整个项目看起来比较臃肿,繁琐,不够模块化,接触Mina后发现mina在这方面做的很是恰到好处。

 

看到文章标题,你或许会有一些疑惑:

     1、Mina框架传递对象是怎么回事

     2、Mina传递对象可以用来做什么

     3、Mina传递对象是怎么进行的

     4、Mina传递对象过程中会遇到什么问题呢

 

      在用原来的java的InputStream,OutputStream,Writer,Reader等进行通信的时候我们会将信息编码转化成字节流等进行信息传递,InputStream,OutputStream是基于字节流的,而Writer,Reader是基于字符的,我们都知道进行通信的服务器和客户端是事先必须定好通信协议,如果我们将<msg>你好吗?</msg>定义为是一条消息,<request>视频</request>定义为一条视频请求,如果客户端将这条消息和请求发送给了服务器,服务器要想得到消息和请求的真正内容(在这里分别是“你好吗?”和“视频”)并进行处理和应答就必须进行信息的解析,就要一条一条的进行判断:1、如果是信息是<msg>……</msg>格式的就将其看做是一条消息;2、如果是<request>……</request>格式的就将其看作是一条请求;3、如果是其他形式就将其视为无效信息,不予处理。当然这不失为一种办法可以进行信息的提取,但是我们会发现在这个过程中信息的发送、接受、解析、处理、应答等都是一条一条的,很是零散,比较难以统一,没有实现消息定义和解析处理过程的分离,这样写好了一个程序,如果日后想要进行改正其中的一条信息格式,就要在整个项目中Ctrl+F了,比较繁琐,还容易出错。

 

      这是我们会自然的想到要用一种东西将各个格式的信息进行分类统一起来并方便进行一些必要的信息处理,为符合这些特点,我们会想到类这个东东恰恰满足了这些性质,我们可以将信息的格式中的内容定义为类的属性,而对这些属性的处理就可以用类中的方法来予以解决,这样就对信息进行了很好的包装。

 

      这种思想有了,那就是在通信的时候直接进行形式上的对象传递(实际上在通信的时候都是最终以字节流的方式进行传递的),那么我们就要找一种工具进行这种形式的信息传递,对了,这种工具就是Mina框架,我们只看他其中的一个方法

public void messageReceived(IoSession session, Object message),这是进行消息接收是能够被 触发的一个方法,参数session代表当前的会话对象,参数message代表接收的到的信息,这时您会发现message的类型是Object型,而类 Object 是类层次结构的根类,当然可以用对象型的作为message啦!前面提到通信的时候都是最终以字节流的方式进行传递的,这样就要进行:对象(客户端)->字节流(客户端)->发送->接收->字节流(服务器)->对象(服务器)的过程,呵呵不用担心,这些繁琐的过程,Mina都提供了很好的底层默认实现所以你只需稍稍敲点代码就行了。

       光说不练还是不行,先上一个程序实例:

      服务器端(1):

Java代码 复制代码
  1. package Mina.server;   
  2.   
  3. import java.io.IOException;   
  4. import java.net.InetSocketAddress;   
  5.   
  6. import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;   
  7. import org.apache.mina.filter.codec.ProtocolCodecFilter;   
  8. import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;   
  9. import org.apache.mina.transport.socket.SocketAcceptor;   
  10. import org.apache.mina.transport.socket.nio.NioSocketAcceptor;   
  11.   
  12. public class MainServer {   
  13.     private static MainServer mainServer = null;   
  14.     private SocketAcceptor acceptor = new NioSocketAcceptor();   
  15.     private DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();   
  16.     private int bindPort = 8888;   
  17.   
  18.     public static MainServer getInstances() {   
  19.         if (null == mainServer) {   
  20.             mainServer = new MainServer();   
  21.         }   
  22.         return mainServer;   
  23.     }   
  24.   
  25.     private MainServer() {   
  26.         chain.addLast("myChin"new ProtocolCodecFilter(   
  27.                 new ObjectSerializationCodecFactory()));   
  28.         acceptor.setHandler(ServerHandler.getInstances());   
  29.         try {   
  30.             acceptor.bind(new InetSocketAddress(bindPort));   
  31.         } catch (IOException e) {   
  32.             e.printStackTrace();   
  33.         }   
  34.     }   
  35.   
  36.     public static void main(String[] args) throws Exception {   
  37.         MainServer.getInstances();   
  38.     }   
  39. }  
package Mina.server;

import java.io.IOException;
import java.net.InetSocketAddress;

import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.SocketAcceptor;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MainServer {
	private static MainServer mainServer = null;
	private SocketAcceptor acceptor = new NioSocketAcceptor();
	private DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
	private int bindPort = 8888;

	public static MainServer getInstances() {
		if (null == mainServer) {
			mainServer = new MainServer();
		}
		return mainServer;
	}

	private MainServer() {
		chain.addLast("myChin", new ProtocolCodecFilter(
				new ObjectSerializationCodecFactory()));
		acceptor.setHandler(ServerHandler.getInstances());
		try {
			acceptor.bind(new InetSocketAddress(bindPort));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws Exception {
		MainServer.getInstances();
	}
}

 

 服务器端(2):

Java代码 复制代码
  1. package Mina.server;   
  2.   
  3. import org.apache.mina.core.filterchain.IoFilterAdapter;   
  4. import org.apache.mina.core.service.IoHandler;   
  5. import org.apache.mina.core.session.IdleStatus;   
  6. import org.apache.mina.core.session.IoSession;   
  7.   
  8. import Mina.Object.UserInfo;   
  9.   
  10. public class ServerHandler extends IoFilterAdapter implements IoHandler {   
  11.     private static ServerHandler samplMinaServerHandler = null;   
  12.   
  13.     public static ServerHandler getInstances() {   
  14.         if (null == samplMinaServerHandler) {   
  15.             samplMinaServerHandler = new ServerHandler();   
  16.         }   
  17.         return samplMinaServerHandler;   
  18.     }   
  19.   
  20.     private ServerHandler() {   
  21.   
  22.     }   
  23.   
  24.     // 当连接后打开时触发此方法,一般此方法与 sessionCreated 会被同时触发   
  25.     public void sessionOpened(IoSession session) throws Exception {   
  26.     }   
  27.     public void sessionClosed(IoSession session) {   
  28.     }   
  29.     public void messageReceived(IoSession session, Object message)   
  30.             throws Exception {     
  31.         if (message instanceof UserInfo) {   
  32.             UserInfo text = (UserInfo) message;   
  33.             System.out.println("服务器接收到从客户端的姓名:"+text.getName());   
  34.             System.out.println("服务器接收到从客户端的QQ:"+text.getQQNum());   
  35.         }    
  36.     }   
  37.   
  38.     public void exceptionCaught(IoSession arg0, Throwable arg1)   
  39.             throws Exception {   
  40.   
  41.     }   
  42.   
  43.     // 当消息传送到客户端后触发   
  44.     public void messageSent(IoSession arg0, Object arg1) throws Exception {   
  45.            
  46.     }   
  47.   
  48.     // 当一个新客户端连接后触发此方法.   
  49.     public void sessionCreated(IoSession arg0) throws Exception {   
  50.            
  51.     }   
  52.   
  53.     // 当连接空闲时触发此方法.   
  54.     public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception {   
  55.            
  56.     }   
  57.   
  58. }  
package Mina.server;

import org.apache.mina.core.filterchain.IoFilterAdapter;
import org.apache.mina.core.service.IoHandler;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

import Mina.Object.UserInfo;

public class ServerHandler extends IoFilterAdapter implements IoHandler {
	private static ServerHandler samplMinaServerHandler = null;

	public static ServerHandler getInstances() {
		if (null == samplMinaServerHandler) {
			samplMinaServerHandler = new ServerHandler();
		}
		return samplMinaServerHandler;
	}

	private ServerHandler() {

	}

	// 当连接后打开时触发此方法,一般此方法与 sessionCreated 会被同时触发
	public void sessionOpened(IoSession session) throws Exception {
	}
	public void sessionClosed(IoSession session) {
	}
	public void messageReceived(IoSession session, Object message)
			throws Exception {	
		if (message instanceof UserInfo) {
			UserInfo text = (UserInfo) message;
			System.out.println("服务器接收到从客户端的姓名:"+text.getName());
			System.out.println("服务器接收到从客户端的QQ:"+text.getQQNum());
		} 
	}

	public void exceptionCaught(IoSession arg0, Throwable arg1)
			throws Exception {

	}

	// 当消息传送到客户端后触发
	public void messageSent(IoSession arg0, Object arg1) throws Exception {
		
	}

	// 当一个新客户端连接后触发此方法.
	public void sessionCreated(IoSession arg0) throws Exception {
		
	}

	// 当连接空闲时触发此方法.
	public void sessionIdle(IoSession arg0, IdleStatus arg1) throws Exception {
		
	}

}

 

 

 

客户端(1):

Java代码 复制代码
  1. package Mina.client;   
  2.   
  3. import java.net.InetSocketAddress;   
  4.   
  5. import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;   
  6. import org.apache.mina.core.future.ConnectFuture;   
  7. import org.apache.mina.filter.codec.ProtocolCodecFilter;   
  8. import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;   
  9. import org.apache.mina.transport.socket.nio.NioSocketConnector;   
  10.   
  11. public class MainClient {   
  12.     private static MainClient mainClient = null;   
  13.     NioSocketConnector connector = new NioSocketConnector();   
  14.     DefaultIoFilterChainBuilder chain = connector.getFilterChain();   
  15.   
  16.     public static MainClient getInstances() {   
  17.         if (null == mainClient) {   
  18.             mainClient = new MainClient();   
  19.         }   
  20.         return mainClient;   
  21.     }   
  22.   
  23.     private MainClient() {   
  24.         chain.addLast("myChin"new ProtocolCodecFilter(   
  25.                 new ObjectSerializationCodecFactory()));   
  26.         connector.setHandler(ClientHandler.getInstances());   
  27.         connector.setConnectTimeout(30);   
  28.         ConnectFuture cf = connector.connect(new InetSocketAddress("localhost",   
  29.                 8888));   
  30.     }   
  31.   
  32.     public static void main(String args[]) {   
  33.         MainClient.getInstances();   
  34.     }   
  35. }  
package Mina.client;

import java.net.InetSocketAddress;

import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

public class MainClient {
	private static MainClient mainClient = null;
	NioSocketConnector connector = new NioSocketConnector();
	DefaultIoFilterChainBuilder chain = connector.getFilterChain();

	public static MainClient getInstances() {
		if (null == mainClient) {
			mainClient = new MainClient();
		}
		return mainClient;
	}

	private MainClient() {
		chain.addLast("myChin", new ProtocolCodecFilter(
				new ObjectSerializationCodecFactory()));
		connector.setHandler(ClientHandler.getInstances());
		connector.setConnectTimeout(30);
		ConnectFuture cf = connector.connect(new InetSocketAddress("localhost",
				8888));
	}

	public static void main(String args[]) {
		MainClient.getInstances();
	}
}

 

客户端(2):

Java代码 复制代码
  1. package Mina.client;   
  2.   
  3. import org.apache.mina.core.service.IoHandlerAdapter;   
  4. import org.apache.mina.core.session.IoSession;   
  5.   
  6. import Mina.Object.UserInfo;   
  7.   
  8. public class ClientHandler extends IoHandlerAdapter {   
  9.     private static ClientHandler samplMinaClientHandler = null;   
  10.     public static ClientHandler getInstances() {   
  11.         if (null == samplMinaClientHandler) {   
  12.             samplMinaClientHandler = new ClientHandler();   
  13.         }   
  14.         return samplMinaClientHandler;   
  15.     }   
  16.   
  17.     private ClientHandler() {   
  18.   
  19.     }   
  20.   
  21.     public void sessionOpened(IoSession session) throws Exception {   
  22.         session.write("客户端与服务器的会话打开了……");   
  23.         UserInfo text=new UserInfo();   
  24.         text.setName("梅竹寒香");   
  25.         text.setQQNum("972341215");   
  26.         session.write(text);   
  27.     }   
  28.   
  29.     public void sessionClosed(IoSession session) {   
  30.     }   
  31.   
  32.     public void messageReceived(IoSession session, Object message)   
  33.             throws Exception {   
  34.     }   
  35.   
  36.     public void messageSent(IoSession arg0, Object arg1) throws Exception {   
  37.         System.out.println("客户端已经向服务器发送了:"+(String)arg1);   
  38.     }   
  39. }  
package Mina.client;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

import Mina.Object.UserInfo;

public class ClientHandler extends IoHandlerAdapter {
	private static ClientHandler samplMinaClientHandler = null;
	public static ClientHandler getInstances() {
		if (null == samplMinaClientHandler) {
			samplMinaClientHandler = new ClientHandler();
		}
		return samplMinaClientHandler;
	}

	private ClientHandler() {

	}

	public void sessionOpened(IoSession session) throws Exception {
		session.write("客户端与服务器的会话打开了……");
		UserInfo text=new UserInfo();
		text.setName("梅竹寒香");
		text.setQQNum("972341215");
		session.write(text);
	}

	public void sessionClosed(IoSession session) {
	}

	public void messageReceived(IoSession session, Object message)
			throws Exception {
	}

	public void messageSent(IoSession arg0, Object arg1) throws Exception {
		System.out.println("客户端已经向服务器发送了:"+(String)arg1);
	}
}

 

公共类: 

Java代码 复制代码
  1. package Mina.Object;   
  2.   
  3. public class UserInfo implements java.io.Serializable{   
  4.     private String name;   
  5.     private String QQNum;   
  6.     public String getName() {   
  7.         return name;   
  8.     }   
  9.     public void setName(String name) {   
  10.         this.name = name;   
  11.     }   
  12.     public String getQQNum() {   
  13.         return QQNum;   
  14.     }   
  15.     public void setQQNum(String qQNum) {   
  16.         QQNum = qQNum;   
  17.     }   
  18.        
  19.        
  20. }  
package Mina.Object;

public class UserInfo implements java.io.Serializable{
	private String name;
	private String QQNum;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getQQNum() {
		return QQNum;
	}
	public void setQQNum(String qQNum) {
		QQNum = qQNum;
	}
	
	
}

 如下建包即可:



 

以上就是对象的收发的简单示例,如果报错,或许会是一下原因:1、包的引进是否妥当 2、是否引入了mina的第三方包(网上有了很多的相关文章,在此就不在赘述了)

 

通过程序您会看到对象已经成功传递并进行了相关属性的输出,对于这个简单的程序我稍做些相关说明:

      1、进行传递的对象所实例化的类要实现java.io.Serializable序列化接口

      2、您会发现实例中的类尤其是相关的IoHandlerAdapter继承类都采用了单实例模式,为什么这样做呢,原因很简单,那就是要在整个通信过程中做到对象session的等实例的单一防止发生“所托非人”的现象

      3、服务器接收到message在进行类判断时用了instanceof关键字

 

如果你看到上面的实例就觉得对象传递的学习已经成功了,那就错了,细心的博友看到这个包结构:



 

 

是不是有点问题呢。

      例如客户端传了一个userinfo对象到服务器,在服务器端判断如果是userinfo对象后就打印出相关信息,我看源码文档其中有这样的建包方式


 
其中服务器和客户端共用了中间的Mina.Object包,这样在收到对象后就能通过instanceof关键字判断是不是useinfo对象,我看了一下,这个方法是可行的,现在的问题是,我们如果编写通讯软件的时候,肯定是服务器和客户端是要分开的,所以那个Mina.Object包是不能共享的,所以问题来了(1)、如果将userinfo放到客户端中,那么该怎么用instanceof进行判断是不是userinfo呢(这时你已经不能再引入服务器中的userinfo了)(2)、如果在客户端和服务器中都编写一个类定义一样的userinfo,可是他们这两个类是分属不同的包,所以是两个不同的类了,这样在用instanceof进行判断的时候也是行不通的;那么我们该用什么方法来进行判断接收到的类是不是userinfo对象呢?
         这个问题把我纠结了很久,在网上面搜了好久也没有解决,最后想了想那个(2)或许可以改动改动就可以解决,问题的关键在于两个UserInfo分属于两个不同的包,如果可以将包名一致就好了,但是在一个工程里面不能同时建立两个命名一样的包,这样你就会发现何不建立两个工程呢一个是服务器,一个是客户端,这样都可以分别建立名字都是Object的包,这样可不可行呢,经过试验果然可以,这样就就解决了上面的问题工程图如下


 
好啦,有了这个工具,您会有什么想法呢?对象传递还可以做什么?那就是可以用它来进行图片,文件的传递啦,这个只是个小小的提示具体怎么实现,就要看各位博友怎么发挥啦!呵呵
转自http://www.iteye.com/topic/946558
分享到:
评论
1 楼 u013835034 2014-03-29  
高手 有联系方式吗 想请教几个关于mina的问题

相关推荐

    java mina框架全套

    Java Mina框架是一款高度可扩展且高性能的网络应用开发框架,专为开发网络服务和协议处理应用程序而设计。它提供了一种简洁、高效的API,使得开发者可以轻松地创建基于TCP/IP和UDP/IP协议的服务器和客户端应用。Mina...

    java-mina通信框架详解.docx

    它提供了JAVA对象的序列化和虚拟机内部通信的功能,使得开发者能够迅速构建高性能、高可扩展性的网络应用。Mina的核心特性是其事件驱动、异步(基于Java NIO)的编程模型,使得处理网络通信变得更加高效。 Mina分为...

    Java springboot 整合mina 框架,nio通讯基础教程,mina框架基础教程.zip

    Java SpringBoot 整合Mina框架,涉及到的核心技术主要包括Java NIO(非阻塞I/O)、Mina框架以及SpringBoot的集成应用。本教程旨在帮助开发者深入理解和掌握这些技术,并提供了一个可直接使用的基础平台框架。 Java ...

    java mina 通讯框架

    java mina 通讯框架

    JAVA mina 框架源码

    JAVA Mina框架是一款高度可扩展、高性能的网络应用开发框架,专为Java平台设计。它提供了丰富的网络通信API,使得开发者能够轻松地构建基于TCP/IP、UDP/IP以及其他协议的服务器和客户端应用程序。Mina框架的核心设计...

    mina框架资源包

    《mina框架资源包详解——构建即时通讯服务器的关键组件》 Mina框架,全称为Apache Mina,是一款基于Java开发的网络通信应用框架,它提供了一种简单而高性能的方式来构建网络服务,尤其在处理TCP/IP、UDP/IP以及SSL...

    基于Java的mina框架

    基于Java的米娜框架,报告对使用基于Java、websocket协议的网页聊天室的过程和技术做了详细的叙述首先,对现有网页进行了分析与评价。首先, 启动后台服务器,然后连接站点,客户端在pc端输入网站或者在手机端扫...

    JAVA 通信框架MINA(包含心跳)

    Apache MINA 是一个开发高性能和高可伸缩性网络应用程序的网络应用框架。它提供了一个抽象的事件驱动的异步 API,可以使用 TCP/IP、UDP/IP、串口和虚拟机内部的管道等传输方式。Apache MINA 可以作为开发网络应用...

    TestMINA.zip_DEMO_Mina框架_java mina_mina_mina java

    这个“TestMINA.zip_DEMO_Mina框架_java mina_mina_mina java”压缩包包含了使用Java实现的MINA框架的示例代码,旨在帮助开发者快速理解和应用MINA。 MINA的核心概念包括事件驱动和非阻塞I/O模型,这两个特性使得它...

    websocket+java服务器(mina)

    html5协议websocket与java服务器的一个简单聊天应用,服务器使用了mina框架,代码中对websocket数据交互协议进行了注释说明,MinaEncoder类与MinaDecoder类对应数据的编码与解码。

    高性能网络架构Mina框架 下载

    Mina(Multithreaded Internet Network Application)框架是由Apache软件基金会提供的一个高性能、可伸缩的网络编程框架,它主要应用于Java NIO环境下的网络通信开发。Mina通过采用多线程模型和异步I/O技术来提高...

    Mina框架+常用JAR包

    Mina框架是一个高性能、异步事件驱动的网络应用程序框架,主要用Java语言编写,用于简化开发TCP、UDP和SSL/TLS协议服务器和客户端的过程。它广泛应用于构建网络服务,如FTP、SMTP、NNTP等协议的服务器,以及任何基于...

    基于java的开发源码-mina高性能Java网络框架.zip

    基于java的开发源码-mina高性能Java网络框架.zip 基于java的开发源码-mina高性能Java网络框架.zip 基于java的开发源码-mina高性能Java网络框架.zip 基于java的开发源码-mina高性能Java网络框架.zip 基于java的开发...

    MiNA框架新人指导

    MiNA框架是一种基于Java的高性能网络应用程序开发框架,它简化了网络编程中的复杂性,使开发者能够更加专注于业务逻辑而不是底层网络细节。MiNA提供了一套强大的API来处理各种网络通信任务,包括但不限于TCP/IP和UDP...

    通信层使用Mina框架实现双机通讯

    Mina(Mind the Application)是一个高性能、轻量级的Java NIO框架,它为开发者提供了构建网络应用的强大工具。本教程将围绕“通信层使用Mina框架实现双机通讯”这一主题,深入探讨Mina的核心特性和如何利用它来实现...

    java mina组合包

    Java Mina是一个高性能、异步事件驱动的网络应用程序框架,主要用于简化开发服务器端和客户端的网络应用。这个“java mina组合包”看起来包含了Mina库的所有必要组件和可能的扩展,为开发者提供了完整的开发环境。 ...

    在Java中运用mina来实现TCP通信

    这是一个有关Mina在Java通信中运用的简单的入门实例,MIna自带一种触发机制,无需再开线程等待收发数据。这个实例中有客户端和服务端,与软件TCPUDPDbg进行文字通话测试。用的时候注意添加Mina包,此实例中用到的是...

    Java mina实例

    压缩包中的文件很可能是源代码和必要的依赖库,比如Mina库和SLF4J(Simple Logging Facade for Java)日志框架。SLF4J是一个用于各种日志框架的抽象层,允许开发者在部署时选择合适的日志实现。 导入工程到Eclipse...

    Mina框架入门介绍

    Apache Mina 框架是一个强大的网络通信框架,它的核心目标是简化开发高效...学习和使用Mina,开发者需要具备JAVA IO、JAVA NIO、Java Socket以及并发库的基础知识,这将有助于更好地理解和应用Mina框架提供的各种功能。

    高性能Java网络框架 MINA

    Apache MINA(Multipurpose Infrastructure for Network Applications)是一个高性能、异步事件驱动的网络应用程序框架,专为开发基于TCP/IP和UDP/IP协议的应用程序而设计。MINA的目标是简化网络编程,使得开发者...

Global site tag (gtag.js) - Google Analytics