Основи на мрежовото програмиране. TCP сокети и IP протоколи.

През далечната 1962г. J.C.R. Licklider от университета MIT, за първи път в труда си “Galactic Network” развива идеята за създаване на единна глобална мрежа. Идеята бързо набира огромна популярност и през 1968г. в ARPANET (Advanced Research Projects Agency Network) е създадено първото мрежово устройство за комутация на пакети, наречено Interface Message Processors (IMP’s).

Същата година е изпратено и първото текстово съобщение между два комютъра от лабораторията  Kleinrock’s до Stanford Research Institute. През 1970г. е създаден и първият  Host-to-Host протокол, наречен  Network Control Protocol (NCP), който донася със себе си идеята за създаване на група протоколи, оказващи правилата за пренос на информация през мрежата, позната днес като TCP/IP.

Според световно възприетите стандарти за компютърни мрежи на организацията IEEE (Institute of Electrical and Electronics Engineers), комуникацията във всяка мрежа се осъществява на базата на седем слойния OSI ( Open Systems Interconnection) модел. Целта на този модел е преносът на информация да бъде разделен на различни слоеве, независими един от друг. С други думи казано, всеки един протокол, работещ на даден слой, използва  слоевете под него без да се интересува от тяхната реализация.
mreji1

Както добре се вижда от фигурата, най-долният слой служи за чисто физическа връзка, на базата на сигнали. Data Link слоят служи за връзка между чисто физическия слой и мрежовия слой, който от своя страна използва IP протокола за осъществяване на връзка между две крайни точки (компютри, сървъри, маршрутизатори). За тази цел протоколът използва йерархична сруктура от IP адреси. Transport слоят използва TCP и UDP протоколите за осъщестяване на връзка между отделните приложения, като основната разлика между двата протокола е, че при TCP се прави проверка за успешното получаване на всеки един пакет. Това прави връзката по-сигурна, но и по-бавна. Поради тази причина повечето улуги в реално време използват UDP протокола. Сесийният слой осъществява сесията между приложенията, като следи за коректното предаване на данни. Presentation слоят се грижи за представяне на данните във вид, разбираем за получателя, като осигурява общия им формат за различни платформи. Извършва конвертиране и “превеждане” на данните компресиране/декомпресиране, както и криптиране/декриптиране на информацията. Към протоколите, отнасящи се към представителния слой, спадат XDR, NFS и др. На последния – Application слой, работят всички приложения.

TCP протоколът осигурява надеждна връзка на клиент-сървър приложенията в Интернет. За да комуникира една клиентска програма с една програма сървър е необходимо те да осъществят връзка по между си. За целта Java предоставя пакет java.net package с два основни класа: Socket и ServerSocket.

Сокети

Сокетите представляват крайна точка на двупосочна комуникация между две програми-сървърна програма и клиентска програма.
Обикновено един сървър работи въру специална машина, която в Интернет пространството се индентифицира с име или IP адрес. Сървърът чака(“слуша”) на даден порт клиет да се свърже с него. Порт- представлява число от 0 до 65536 и е уникален идентификатор на дадена услуга или приложение. За да се свърже даден клиент с даден сървър е необходимо клиентът да знае името или IP адреса на дадения сървър и порта, на който сървърът „слуша”. След като клиентът се свърже със сървъра е необходимо той да се индентифицира със собствен порт – номер, който той ще използва по време на дадената връзка.
mreji2

Ако всичко премине успешно, сървърът приема връзката с клиента като запазва неговия адрес и порт номер.
mreji3

След изграждане на дадената връзка, сървърът се връща към първоначалното си състояние, в което слуша за нови клиенти.
За по-голяма яснота, нека разгледаме един пример:
В примерната програма клиентът изпраща на сървъра едно число и сървърът проверява дали даденото число е четно или нечетно. След това връща отговор на клиента.
сървърна част:

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

public class ServerDemo {

	public static void main(String[] args) throws IOException {
		int number;
		ServerSocket server = new ServerSocket(1211);
		Socket socket = server.accept();
		Scanner scan = new Scanner(socket.getInputStream());
		number = scan.nextInt();
		PrintStream printout = new PrintStream(socket.getOutputStream());
		if ((number % 2) == 0) {
			printout.println("your number is even");
		} else {
			printout.println("your number is odd");
		}
		server.close();// with this line server would be stoped;
		scan.close();
	}
}

Първата стъпка в сървърната част е отваряне на ServerSocket на порт 1211, след което сървърът започва да „слуша”. Когато даден клиент се свърже към сървъра, той приема заявката чрез извикване на метода accept(). Той е блокиращ метод- това означава, че докато някой не се свърже, програмата няма да продължи. Методът връща променлива от тип Socket, в която се съхраняват IP адреса и порта на дадения клиент. След това създаваме обект от тип Scanner, на който в конструктора подаваме входния поток от клиента чрез метода getInputStream(). Записваме подаденото число в локална променлива. Проверяваме дали числото е четно. Изпращаме резултата чрез метода getOutputStream(). След изпращане на дадения резултат, нашият сървър ще спре чрез метода close(). Трябва да се отбележи, че този метод е извикан, за да се покаже неговото приложение, докато в реална ситуация един истински сървър не трябва никога да прекратява своята дейност.
клиентска част:

import java.io.IOException;
import java.io.PrintStream;
import java.net.Socket;
import java.util.InputMismatchException;
import java.util.Scanner;

public class ClientDemo {
	public static void main(String args[]) throws IOException {
		int number;
		Socket s = new Socket("127.0.0.1", 1211);
		Scanner scan = new Scanner(System.in);
		Scanner scan2 = new Scanner(s.getInputStream());
		PrintStream printout = new PrintStream(s.getOutputStream());
		System.out.println("Enter some Value");
		try {
			number = scan.nextInt();
			printout.println(number);
			String input = scan2.nextLine();
			System.out.println(input);
		} catch (InputMismatchException e) {
			System.out.println("Enter a correct value");
		} finally {
			if (s != null)
				s.close();
			if (scan != null)
				scan.close();
			if (scan2 != null)
				scan2.close();
		}
	}
}

В клиентската част, отваряме сокет с IP адрес 127.0.0.1 – адрес на текущата машина(може да бъде замененот от “localhost”) и порт 1211. Създаваме два обекта от тип Scanner. Първият чете от стандартната клавиатура, а вторият чете поток, асоцииран със създадения обект от тип сокет. Проверката за невалидни символи е направена в клиентската част. По-добрият подход е това да се направи в сървърната част, от гледна точка на сигурността.

Явор Томов, Даниел Джолев

Leave a Reply

Your email address will not be published. Required fields are marked *