Pull to refresh

Разработка сетевого клиента на Java

Reading time4 min
Views5.5K
Иногда бывают ситуации когда необходимо использовать сетевое взаимодействие, однако клиента по интересующий протокол не существует, или этот клиент платный(проприетарный), или это вовсе своя разработка. В этой статье я опишу как создать свой клиент для сетевого взаимодействия. Я опишу общие шаги необходимые для этого.


Первым делом конечно необходима спецификация протокола, но поскольку я хочу показать лишь путь решения данной проблемы мне она не понадобится. Спецификация нужна для того, чтобы составить описание сообщений которые будут посылатся через сеть. Самым разумным буде вынести структуру сообщений за пределы кода, чтобы динамически менять их структуру. Лучшим описателем различного рода структур является XML. Чтобы не мучатся с зачиткой XML словаря(так мы будем его называть) давайте создадим описание схемы при помощи XSD:

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" >

	<xsd:complexType name="FieldDescription">
		<xsd:attribute name="name" type="xsd:string"  />
		<xsd:attribute name="defaultValue" type="xsd:string" />		
	</xsd:complexType>

	<xsd:complexType name="SequenceDescription">
		<xsd:sequence>			
			<xsd:element name="fieldDescription" type="FieldDescription" minOccurs="0" maxOccurs="unbounded" />			
			<xsd:element name="sequenceDescription" type="SequenceDescription" minOccurs="0" maxOccurs="unbounded" />
		</xsd:sequence>		
		<xsd:attribute name="sequenceName" type="xsd:string" />					
	</xsd:complexType>

	<xsd:complexType name="MessageDescription">
		<xsd:sequence>			
			<xsd:element name="sequenceDescription" type="SequenceDescription" minOccurs="0" maxOccurs="unbounded" />
			<xsd:element name="fieldDescription" type="FieldDescription" minOccurs="0" maxOccurs="unbounded" />
		</xsd:sequence>		
		<xsd:attribute name="messageName" type="xsd:string" />
	</xsd:complexType>
		
	<xsd:element name="DictionaryDescription">
		<xsd:complexType>
			<xsd:sequence>
				<xsd:element name="messageDescription" type="MessageDescription" minOccurs="1" maxOccurs="unbounded" />
			</xsd:sequence>			
		</xsd:complexType>
	</xsd:element>

</xsd:schema>


Например эта схема позволяет описать сложную, иерархичную структуру сообщений. Теперь необходимо понять, что же представляет из себя сам мессадж. По сути это map филдов и их значений. Объявим следующий интерфейс для работы с сообщениями:

public interface IMessage
{
	
	/**
	 * Add field to message
	 * @param name name of field (not null)
	 * @param value value of field
	 */
	void addField(final String name, final Object value);
	
	/**
	 * Remove field from message
	 * @param name name of field (not null)
	 */
	void removeField(final String name);
	
	/**
	 * Get message field by name
	 * @param name name of field (not null)
	 * @return value of field
	 */
	Object getField(final String name);
	
	/**
	 * Check that field is set 
	 * @param name field name
	 * @return true if field set, else false
	 */
	boolean isFieldSet(final String name);
	
	/**
	 * Get set of message fields
	 * @return set of message fields
	 */
	Set<String> getFieldNames();
	
	/**
	 * Get copy of message
	 * @return new IMessage with copied fields
	 */
	IMessage cloneMessage();
	
	/**
	 * Compare messages by fields
	 * @param message message for comparing
	 * @return true/false
	 */
	boolean compare(final IMessage message);

}


Имплементируя все его методы, мы получим класс который будем использовать в своем коде для получения и отправки сообщений. Но как вы наверное заметили он далек от той структуры, которую мы заложили в схеме, поэтому нам потребуется кодер/декодер.

public interface ICodec {

	/**
	 * Encode list of IMessage to byte array
	 * @param messages input messages
	 * @return array of byte
	 */
	byte[] encode(List<IMessage> messages);

	/**
	 * Decode array of byte to list of IMessage
	 * @param bytes input array
	 * @return list of IMessage
	 */
	List<IMessage> decode(byte[] bytes);

}


Конечная имплементация данного интерфейса должна в конструкторе получать экземпляр интерфейса IDictionary, задача которого зачитать структуру по схеме из XML в экземпляры классов, созданных по схеме. Следующий фрагмент кода необходимо вставить в ant, чтобы сделать это. Этот фрагмент определяет таску создания классов по схеме при помощи jaxb.

<path id="jaxbcp">
	<pathelement location="libs/jaxblibs/jaxb-api.jar" />
	<pathelement location="libs/jaxblibs/jaxb-impl.jar" />
	<pathelement location="libs/jaxblibs/jaxb-xjc.jar" />
	<pathelement location="libs/jaxblibs/jsr173_1.0_api.jar" />
</path>

<taskdef name="xjc" classname="com.sun.tools.xjc.XJCTask" classpathref="jaxbcp" /

<xjc schema="xsd/schema.xsd" destdir="${gen.dir}" package="your.package.name">
	<produces dir="${gen.dir}" includes="*" />
</xjc>


Зачитаем xml словарь при помощи следующего кода:

try 
{
	jc = JAXBContext.newInstance(new Class[]{DictionaryDescription.class});
	u = jc.createUnmarshaller();
	File f = new File (path);
	JAXBElement<DictionaryDescription> root = u.unmarshal(new StreamSource(f), DictionaryDescription.class);
	DictionaryDescription s = root.getValue();
	
} catch (JAXBException e) {
	logger.error("Error during parsing dictionary", e);
}


Теперь все готово, кроме самого клиента. Давайте и для него создадим интерфейс.

interface Client
{
	
	void init(Map parameters);

	void connect();
	
	void disconnect();

	void sendMessage(IMessage msg);

	void addReceiveListener(IReceiveListener listener);
	
	void removeReceiveListener(IReceiveListener listener);

}


Применим паттерн подписчик/наблюдатель для обработки полученных сообщений. IReceiveListener будет является интерфейсом подписчика. Метод onMessageReceived будет содержать обработчик на получение сообщения. При помощи этого интерфейса можно реализованить большой спектр подписчиков. Например: прокси(перенаправитель), сохранение в файл/базу/память, реализовать симулятор, который в автоматическом режиме будет отвечать на входящие сообщения.

public interface WMQReceiveListener {

	/**
	 * Handler of message
	 * @param message message
	 */
	public void onMessageReceived(byte[] message);
	
}


Вот и все, скелет своего connectivity layer создан. Дальше вы можете наращивать его, чтобы имплементировать всю вашу бизнес логику.
Tags:
Hubs:
Total votes 24: ↑15 and ↓9+6
Comments12

Articles