31 Mart 2016 Perşembe

DDS ve Ağ Merkezli Muharebe üzerine

DDS Standartları
DDS'in şu anda v1.2 sürümü mevcut. RTPS'in ise v2.1 sürümü mevcut.

DDS Nerede Kullanılmaz
DDS her yerde mutlaka kullanılması gereken her derde deva bir altyapıymış gibi sunuluyor. DDS Silver Bullet değildir. Kullanılması gereken ve gerekmeyen yerler vardır.

Remote Method Invocation
DDS Java'daki terminoloji ile RMI tarzı çağrılar gerektiren işler için uygun değildir. İsteğe karşı cevap bekleniyorsa, cevabı başka bir Topic üzerinden almak gerekir. İstek ve cevap arasında JMS'teki gibi otomatik ilişki kurma yeteneği de sağlamadığı için bu tür kullanım şeklinde sıkıntıya sebep olur.

CORBA da bu tür işler için daha uygun olabilir. CORBA şöyle ilklendirilir.
java.util.Properties props = new java.util.Properties();
props.put("org.omg.CORBA.ORBInitialPort", initialPort);
props.put("org.omg.CORBA.ORBInitialHost", initialHost);
props.put("com.sun.CORBA.giop.ORBGIOPVersion", "1.0");
orb=org.omg.CORBA.ORB.init(args,props);

Wide Area Network
DDS discovery protokolü broadcast yöntemini kullandığı için WAN üzerinde kullanıma uygun değildir. DDS nodlarının birbirlerini bulabilmeleri için tünel açmak gerekir.

DDS
Ağ merkezli muharebenin temel özelliklerinden birisi savaş alanındaki unsurların bir veri aktarım yolunu kullanarak birbirleri ile mümkünse gerçeğe yakın zamanlı ilişki kurabilmesidir.
DDS diğer iletişim altyapıların göre gerçek zamanlı veri aktarımına daha müsait bir yapıya sahiptir. Aşağıdaki şekli buradan aldım ve bu fikri destekliyor.


QoS Parametreleri
DDS QoS Parametreleri Listesi yazısına taşıdım.

DDS Standartları
DDS Interoperability Write Protocol (DDSI) çeşitli DDS üreticilerinin byte seviyesinde aynı dili konuşmasını sağlıyor. DDS API'si ise kod seviyesinde standartlaşmayı sağlıyor. Böylece DDS kullanan uygulama farklı bir DDS ürünü ile entegre edilebiliyor.

DDS ve Nesneye Yönelik Tasarım
DDS ile Nesneye Yönelik Tasarım arasında çok temel bir fark var. Nesneye Yönelik Tasarımda nesne veri + davranıştan oluşur. DDS nesneleri ise sadece veriden ibarettir. Gerçek anlamda nesne olduklarını düşünmüyorum. Bu amaç için DDS nesneleri başka nesneler ise sarmalanarak kullanılabilir.

DDS Temel Sınıfları
DDS ile kullanılan temel sınıfları gösteren bir şekli buradan aldım.

DDS Temel Kavramları
Data Centric Publish Subscribe (DCPS) :
Örnek yaz

Data Local Reconstruction Layer (DLRL) :
Bu kavramda tüm nesnelerin türediği Java'daki gibi bir "Object" sınıfı kullanılıyor. Ben DLRL'nin her yerde kullanılmasını doğru bulmuyorum. DLRL birbiriyle ilişkili verileri, örneğin bir ağaç yapısını oluşturmayı güçleştiriyor. Bu tür yapıların bellekte kendi veri yapımızı içinde tutulması, DLRL kullanmaktan daha iyi diye düşünüyorum.
 

Instance : Aynı topic içinde "key" değerleri farklı olan nesneler.
Sample : Aynı Instance'a ait farklı zamanlardaki veri.
Instance Sample için Resource Limits QoS  başlıklı yazıya göz atabilirsiniz.

Publisher
Bir tane yaratılır. "The publisher acts on behalf of one or several DataWriter objects that belong to it". Yani kısaca DataWriter'lar için factory sınıfı. Burada ilginç olan publisher hem bir factory ama aynı zamanda yarattığı nesnelerin kendisi üzerinden çalışmasını istiyor. Yani ilginç bir tasarım!

DataListener

Gelen veriyi dinlemek için örnek:
DDS::DataReaderListener* pListener = //...
DDS::DataReader* pDataReader = //...
DDS::StatusMask listenerMask = DDD::DATA_AVAILABLE_STATUS;
DDS::ReturnCode_t result = pDataReader->set_listener (pListener, listenerMask);

Listener şöyle yazılmalı

void MyListener::on_data_available(DDS::DataReader* reader)
{
   MsgSeq received_data; //IDL'den üretilen sınıf
   SampleInfoSeq sample_info; //DDS sınıfı
   MsgDataReader reader = MsgDataReader::narrow(reader);//IDL'den üretilen reader
   reader->take (received_data,sample_info, ...);
}

Dinlemeyi durdumak için örnek:
pDataReader->set_listener (pListener, 0);

DataWriter
DataWriter yazısına taşıdım.

DataReader
Temel olarak take() ve read() şeklinde iki metod sunar. take() şeklindeki metodlarla sample arakatman belleğinden silinir. read() şeklindeki metodlarla sample arakatmanda yaşamaya devam eder.
Örnek:

Sequence<Msg> seq;
reader->take (seq,sample_info_seq,1,
                      DDS:NOT_READ_SAMPLE_STATE,
                      DDS::ANY_VIEW_STATE,
                      DDS::ANY_INSTANCE_STATE);

WaitSet
WaitSet'e bir Condition verilir. DataReader WaitSet üzerinde bekler.Condition'lar ReadCondition, StatusCondition, QueryCondition, GuardCondition olarak ayrılırlar.

Filtreler

DDS ile iki çeşit filtre tanımlama imkanı bulunmaktadır.

Birinci çeşit filtreler içerik filtrelerdir (content filter). Bu filtreler ile gönderilen bilgi taşıdığı veriye göre uygulama seviyesine çıkmadan filtrelenebilir.

İkinci çeşit filtre ise zamana bağlı filtrelerdir (time based filter). Bu filtreler ile gönderilen verilen tümü değil de bizim ayarladığımız periyotlarda güncellemeler okunur. Yayın yapan birime göre daha yavaş kalan abonelerin fazlasıyla istifade edebilecekleri bir özellik olduğunu düşünüyorum. Aşağıda buradan aldığım bir şekil ile filtre konusu görülebilir.

İçerik Filtresi

Zaman Filtresi


Filtreler DataReader sınıfına uygulamanın ilgilendiği ve okumak istediği bilgileri bildirir .DataReader ağ üzerinden gelen bilgilerden sadece belirtilen kriterlere uyanları uygulamaya geçirir, uymayanları ise kaale almaz.

Örneğin ben okuyan taraf olarak hızı belli bir değerin üzerinde olan ve teşhis bilgisi "dost" olan izleri görmek isteyebilir. Bu durumda yapmam gereken DataReader'ıma bu filtreleri vermek ve sadece verdiğim parametrelere uyan bilgiler geldiği zaman haberdar edilmeyi beklemek olacaktır.

Aşağıdaki kısım sayın Ertan D.'nin uyarısı ile değiştirildi. Aslında RTPS 2.1 versiyonunda halen "DataWriter side filtering" kısmı isteğe bağlı olarak geçiyor ancak bu işlevi yerine getirmeyen ara katman yazılımı en azından büyükler arasında kalmadı sanırım. Yine de eski sürümlerde üstü çizili olan cümlelere dikkat etmek gerekebilir.

Ancak dikkat edilmesi gereken bir nokta filtrelerin sadece DataReader tarafından bilindiği hususudur. Yani bize bilgi yollayan birim, bizim ilgilendiğimiz kriterlerden haberdar değildir. Yani filtreler bant kullanımını azaltmak için işe yaramaz. Sadece uygulamanın kendi içinde fazladan kod yazarak filtreleme yapma işleminin ara katmanının üzerine yıkar.

Kanımca eğer filtreleme işlemi de DataWriter tarafından yapılıyor olsaydı bant genişliği problemi bir nebze daha halledilmiş olurdu.

Esasen DDS'in çıkış noktası yerel ağlara sahip gemi sistemleri olduğu için (ve gemilerde bant genişliği uzak mesafe iletişimde olduğu kadar ciddi problem olmadığı için) bant genişliği problemi henüz daha gündeme gelmemiş olabilir.

DDS'in kullanımı yaygınlaştıkça filtreleme işleminin daha dağıtık bir yapıya dönüşüp, bu problemin de bir şekilde halledileceğini umuyorum.

27 Mart 2016 Pazar

Javascript

Javascript bir client side scripting teknolojisi. Buradan aldığım şekilde mavi renkle gösterilen daireye denk geliyor.

Javascript ve DOM
Javascript her zaman bana karışık geldi. Aşağıya bazı işime yarayacak şekilleri ekliyorum.
Buradan aldıklarımda temel Document Object Model (DOM) nesnelerine erişimi gösteriyor.

Aşağıdaki şekli ise buradan aldım.

Buradan aldığım şekilde ise DOM'un aslında çok daha kapsamlı olduğunu görebiliyoruz.
window.history
window.history.back(); ile bir önceki sayfaya gidilir.

Javascript ve sayılar
Javascript'te sayı olarak sadece number veri tipi mevcut. 

toFixed
Sayıyı string'e çevirmek için kullanılır. Örnek:
Javascript ve String
replace
Bu metod ilk eşleşen kısmı değiştirir. String'in eşleşen her kısmını değiştirmek için /g seçeneği ile kullanmak lazım. Örnek:
Array Sınıfı
Javascript Array sınıfı yazısına taşıdım.

Javascript ve Dictionary
Dictionary aşağıdaki gibi tanımlanır.
a = {"staticData":['Blue','Green']}//name-to-array dictionary
a.staticData [0] şeklinde erişilebilir.

setTimeout
Javascript ve Timer yazısına taşıdım.

24 Mart 2016 Perşembe

Endian

Endian Nedir?
Endianness Nedir yazısına taşıdım

Sistemin Endian Tipini Anlamak
Sistemin Endian Tipini Anlamak yazısına taşıdım.

Big Endian'a Çevirerek Yazma
C
htonl veya htons ile verilen 32 bit veya 16 bitlik sayılar Big Endian'a çevirilebilir. Örnek:
uint32_t tmp = htonl(any32bitint);
send(socket, (char *) &tmp, sizeof(tmp), 0);
64'bitlik sayıları BigEndian'a çevirmek için aşağıdaki gibi yapılabilir.

int32_t data [2];
data[0] = htonl((int32_t) from);
data[1] = htonl((int32_t) (from >> 32));

int64_t mynum;
from = (int64_t) ntohl(data[1]);
from = (from << 32);
from = from | (int64_t) ntohl(data[0]);
C#
C#'ta BigEndian ile ilgili bir stream sınıfı yok. BinaryWriter sınıfından türeyen ve çok işe yarayan BEBinaryWriter sınıfı burada.

Java
DataOutputStream  
DataOutputStream yazısına taşıdım.

Big Endian'dan Okuma
C
ntohl veya ntohs ile verilen 32 veya 16 bitlik sayılar Big Endian'dan okunabilir. Bu fonksiyonlar unsigned döndürdükleri için, sonucu cast etmek gerekebilir.

ntohl
Şöyle kullanırız:
uint32_t data = ...;
int32_t value = (int32_t) ntohl(data);
Kendi ntohl gerçekleştirimimiz
ntolh'yi kendimiz yazmak isteseydik şöyle yapardık
char b[4] = //...

uint32_t result;
uchar *p = (uchar *)(&result);
*p++ = b[3];
*p++ = b[2];
*p++ = b[1];
*p     = b[0];
Ya da şöyle yapardık:
char* bp = ...;
*(uint32_t*)bp = (uint32_t)(
                (*bp       << 24) |
                (*(bp + 1) << 16) |
                (*(bp + 2) <<  8) |
                (*(bp + 3)      ));
Ya da şöyle yapardık. Bu kod ilk örnek ile aynı aslında.
long NTOHL(long value)
{

    long test = 0x01020304;
    char* ptr = (char*)&test;
    int isSystemAlreadyBigEndian = (ptr[0] == 0x01);

    char* ptrValue = (char*)&value;
    long result = value;
    char* ptrResult = (char*)&result;

    if (!isSystemAlreadyBigEndian)
    {
        // do the byte swap
        ptrResult[0] = ptrValue[3];
        ptrResult[1] = ptrValue[2];
        ptrResult[2] = ptrValue[1];
        ptrResult[3] = ptrValue[0];
    }

    return result;

}


8 byte'lık double okuma da benzer bir mantığa sahip.
char b[8] = //...

double result;
uchar *p = (uchar *)(&result);
*p++ = b[7];
*p++ = b[6];
*p++ = b[5];
*p     = b[4];
*p++ = b[3];
*p++ = b[2];
*p++ = b[1];
*p     = b[0];
C++ ile şöyle yaparız.
int64_t size;
recv(client, (char *)&size, sizeof int64_t, 0);
char *start = (char *)&size, *end = start + sizeof(size);
std::reverse(start, end);
Büyük Endian'dan küçük endian'a çevirmek için template ta kullanabiliriz. Örnek:


Java
ByteBuffer
getDouble
ByteBuffer sınıfının getDouble() vs. metodları ile BigEndian formatı okunabilir. Örnek:

flip
Bu konudan çok emin olmamakla beraber ByteBuffer herhangi bir mevcut buffer'ı wrap etse bile okumaya başlamadan önce fliıp() metodu ile okumaya hazır hale getirmek lazım.

network byte order to host byte order in java sorusunda verilen cevapta ByteBuffer sınıfı okumaya başlarken flip() metodu ile okumaya hazır hale getiriliyor.

flip() metodu aşağıdaki gibi kodlanmış ve amacı ise ByteBuffer sınıfına verilmiş olan limiti en son yazma pozisyonuna eşitlemek çünkü okuma işleminde sınıfın tamponunun sonuna gelip gelinmediği limit değerine bakarak kontrol ediliyor.


Yani netty ile ByteBuffer kullanmak için aşağıdakine benzer bir kod lazım.
ByteBuffer buf = ByteBuffer.allocate(1);
buf.put(0x1c);
buf.flip();//Okumaya hazırla
e.getChannel().write(ChannelBuffers.wrappedBuffer(buf));

DataInputStream
DataOutputStream ve DataInputStream yazısına taşıdım.

QT
QDataStream
QDataStream sınıfıyla ilgili olarak STL,StreamBuffer nesnesi ve QT başlıklı yazıya göz atabilirsiniz. QT ile QIODevice stream sınıflarının temeli gibi düşünülebilir. Qt serialization - how to exclude a field? sorusunda QDataStream sınıfı kullanılarak bir nesneyi binary olarak yazma örneği bulunabilir. Bu iş için aşağıdaki gibi operatörler tanımlamak yeterli.
friend operator << (QDataStream& stream, const MyClass& myclass)

Little Endian'dan Okuma
Java
ByteBuffer
order
Normalde JVM BigEndian çalışır ama Little Endian çalışılmak istenirse order metodu kullanılabilir. Örnek:
  
Integer.reverseBytes
Örnekte küçük endian'dan float okuma ve float yazma gösterilmiş.
Float.intBitsToFloat(Integer.reverseBytes(dataInputStream.readInt()));

C# 
BitConverter
BitConverter sınıfı ile LittleEndian veri okunabilir. 


ToUInt16

Örnek:
byte[] buffer = new byte[2];
ushort value = BitConverter.ToUInt16(buffer, 0);
Küçük Endian Yazma
Java
Örnekte küçük endian yazma gösterilmiş.
dataOutputStream.writeInt(Integer.reverseBytes(Float.floatToRawIntBits(float)));

21 Mart 2016 Pazartesi

TCP Sliding Window

TCP Sliding Window
TCP Sliding Window protokolü gönderen ve alan tarafların Send Window ve Receive Window kullanarak, paketleri takip etmeleri kuralına dayanır. Bu konuyu en iyi anlatan yazı TCP/IP Guide. Özellikle Send ve Receive yazılarına mutlaka bakmak lazım.

Programlama kitaplarında Send Window yerine Send Buffer kelimesi kullanılıyor. Sanırım ikisi de aynı şey.

Sliding Window sayesinde TCP şu işlevleri sağlayabilir.

1. Guaranteed delivery. If a packet is lost, TCP will detect and resend it.
2. Packet ordering. If packets arrive out of order, TCP can reassemble them in the correct order.
3. Transmission rate control. TCP allows the receiver to tell the sender to slow down or speed up as necessary.


Kablonun Çekilmesi
Eğer ethernet kablosunu çekersek ve bir müddet beklersek bile TCP Sliding Window tekrar gönderim kurallarını işlettiği için bağlantı kopmaz.

Sliding Window Büyüklüğü
Sliding Window'un ne kadar büyük olması gerektiği yani kaç paketi takip etmesi gerektiği teorik olarak hesaplanabilir.

Send Window
Şekilde Send Window'un nasıl çalıştığı görülebilir.

TCP Output Queue - Category #2 + Category #3

TCP socketinde bir "output queue" bulunur. Linux üzerinde bu kuyrukta kaç byte veri beklediğini görmek için bir ioctl komutu bulunmakta.
int value;
error = ioctl(tcp_socket, SIOCOUTQ, &value);
SIOCOUTQ parametresinin açıklaması burada . Aslında buradaki header dosyasına bakınca açıklama olarak şunu görmek mümkün.

#define SIOCOUTQ        TIOCOUTQ        /* output queue size (not sent + not acked) */

Yani benim anladığım yukarıdaki şekildeki Category#2 + Category#3  'ün toplamını veriyor.

Send Buffer Büyüklüğü

Buradan aldığım aşağıdaki şekilden de görülebildiği gibi output kuyruğunun büyüklüğünü ayarlayabilmek mümkün. Bunun için SO_SNDBUF seçeneği kullanılıyor.

Soketin Send Buffer büyüklüğü şöyle öğrenilebilir.
int sockfd, sendbuff;
socklen_t optlen;

 // Get buffer size
optlen = sizeof(sendbuff);
res = getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, &optlen);
Yine aynı şekilde Send Buffer büyüklüğü şöyle atanabilir.
// Set buffer size
sendbuff = 98304;
setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &sendbuff, sizeof(sendbuff));
Linux bir şekilde Send Buffer büyüklüğünü verilen değerin iki katı olarak kullanıyor.
SO_SNDBUF
     Sets or gets the maximum socket send buffer in bytes.  The  ker-
     nel doubles this value (to allow space for bookkeeping overhead)
     when it is set using setsockopt(), and  this  doubled  value  is
     returned  by  getsockopt().   The  default  value  is set by the
     wmem_default sysctl and the maximum allowed value is set by  the
     wmem_max sysctl.  The minimum (doubled) value for this option is
     2048.

Send buffer'ın büyüklüğünü ayarlamak yerine, aynı dosya sistemindeki gibi Buffered I/O yapabilme fikrinin uygulandığını da gördüm.
FILE *f_recv = fdopen(sock, "wb");
FILE *f_send = fdopen(sock, "rb");

// some IO here.

close(sock);
fclose(f_recv);
fclose(f_send);

Receive Window
Receive Buffer Büyüklüğü
SO_RCVBUF ile socket receive buffer büyüklüğü ayarlanabiliyor.

Büyüklüğü almak için şöyle yapılır.
int recvBufSize;
int err = getsockopt(socket, SOL_SOCKET, SO_RCVBUF,
                     (char*)&recvBufSize, sizeof(recvBufSize));
Büyüklüğü atamak için ise
int recvBufSize = /* usage specific size */;
int err = setsockopt(socket, SOL_SOCKET, SO_RCVBUF,
                     (const char*)&recvBufSize, sizeof(recvBufSize));





17 Mart 2016 Perşembe

IP-String Adres Dönüşüm Metodları

Not:
Bazen DNS sunucusunu kullanarak sorgulama yapmak gerekir. Konuyla ilgili DNS Metodları başlıklı yazıya bakabilirsiniz.

Giriş
Adres dönüşüm metodları, DNS sunucusuna istek göndermeden yapılabilen basit hesaplamalar. Noktalı IP adreslerini 4 byte'a veya 4 byte'tan tekrar noktalı gösterime çevirmeye yararlar. Socket programlamada bu konu illaki karşımıza çıkar. Java da InetAddress sınıfı ile bu tür dönüşümleri yapmaya gerek kalmıyor. En fazla kullanmamız gerek kod aşağıdakine benziyor.
InetAddress addr = InetAddress.getByName("127.0.0.1");

C dilinde ise bir çok metodu ve farkını bilmek gerekiyor. Bu yüzden bu yazıyı yazdım.

1. Metin IP Adresini Long'a Çevirme
inet_xxx ile başlayan metodlar "Internet Address Manipulation Routines" cümlesini ima ederler.

inet_pton - En yeni ve kullanmamız gereken POSIX API'si
inet_pton metodu yazısına taşıdım.

inet_aton - deprecated. Use inet_pton
inet_aton metodu yazısına taşıdım.

inet_addr - deprecated. Use inet_pton
inet_aton ile aynıdır. Hatta aralarında ne fark var bilmiyorum. Verilen IP metnini network byte order'ta bir 32 bitlik sayıya çevirir. Metodun içi şuna benzer:
unsigned int inet_addr(char *str)
{
    int a, b, c, d;
    char arr[4];
    sscanf(str, "%d.%d.%d.%d", &a, &b, &c, &d);
    arr[0] = a; arr[1] = b; arr[2] = c; arr[3] = d;
    return *(unsigned int *)arr;
}

//use it as follows:
//inet_addr() returns address in Network Byte Order, so no need of htonl()

thesockaddr_in.sin_addr.s_addr = inet_addr(str);
Aynı işlemi C#'ta şöyle yapardık

String ipString = "192.168.0.1";
String[] ipBytes = ipString.Split('.');

uint byte1 = Convert.ToUInt32(ipBytes[0]);
uint byte2 = Convert.ToUInt32(ipBytes[1]);
uint byte3 = Convert.ToUInt32(ipBytes[2]);
uint byte4 = Convert.ToUInt32(ipBytes[3]);

uint IP =   (byte1 << 24) 
            | (byte2 << 16) 
            | (byte3 <<  8) 
            |  byte4 ;

Örnek'te socketi connect işlemine tabi tutmak için doldurulan sockaddr görülebilir:

struct sockaddr_in  sin;
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(1234);
if((sin.sin_addr.s_addr = inet_addr(g_szIp)) == INADDR_NONE)
{
//Error 
}


2. Long'u Metin IP adresine çevirme

inet_ntoa - deprecated. use inet_ntop
Bu metod IPv4 için çalışır. Verilen binary IP adresini "192.168.1.2" örneğindeki gibi metne çevirir. İmzası
char *inet_ntoa(struct in_addr in); 
şeklindedir.

inet_ntop
İmzası şöyledir.
const char * inet_ntop(int af, const void *src, char *dst, size_t size)
IPV4 için verilen 4 byte değeri string'e çevirir. Örnek:
// IPv4 demo of inet_ntop() and inet_pton()

struct sockaddr_in sa;
char str[INET_ADDRSTRLEN];

// store this IP address in sa:
inet_pton(AF_INET, "192.0.2.33", &(sa.sin_addr));

// now get it back and print it
inet_ntop(AF_INET, &(sa.sin_addr), str, INET_ADDRSTRLEN);

printf("%s\n", str); // prints "192.0.2.33"
IPv6 için verilen 16 byte değeri string'e çevirir.Bir code golf yarışmasında dünyadaki tüm IPv6 adresleri göstermek için şöyle bir kod kullanılmış. inet_ntop()' geçilen 10 değeri aslında AF_INET6 anlamına geliyor.
__uint128_t i;main(){char s[50];for(;inet_ntop(10,&i,s,49),puts(s),++i>0;);}

İki Long Arasındaki Fark Kaç Tane IP Olduğunu Bulmaya Yarar
Örnek:
IP addresses are a 32 bit integer which we typically express as four octets for human purposes. You could get the number of addresses in a range by turning both into their 32 bit integer representations and subtracting.