3 Nisan 2017 Pazartesi

Continuous Integration (CI) - Sürekli Entegrasyon


Sürekli Entegrasyon Nedir?

  • Bir proje üzerinde her değişiklik olduğunda, projeyi yeniden deploy->test->report aşamalarından geçiren bir sistemdir.
  • En temel kullanım amacı, işleri otomatikleştirmek ve değişimler ile gelen sorunlardan kurtulmaktır.
    • Projelere commitler geldiğinde bir bölüm diğer bir bölümü acaba bozdu mu diye tahminler yürütmenin önüne geçer. Her değişim direkt olarak test edilir.
CI Hizmeti Veren Yazılımlar?
  • Travis CI
  • Jetkins
  • Bambo

  • Github/Bitbucket gibi version sistemlerini destekler.
    • Projeye gelen her commit sonrası sistem tetiklenir ve testler yeniden çalıştırılıp rapor oluşturulur.
  • C/C++/Java/JS/Ruby ve daha bir çok dil için destek sunar.
  • Açık kaynak projeler için ücretsiz.

Travis CI Github İlişkisi
  • Github ile giriş yapın.
  • Gerekli izinleri onaylayın.
  • Github üzerinden projenize .travis.yml adında ayar dosyasını ekleyin.

.travis.yml 

  • python2.7 ve 3.5 için sürekli olarak pytest'i çalıştırır.

language: python
python:  - "2.7"
  - "3.5"
# command to run tests
script: pytest



Dahası için tıklayın.

25 Şubat 2017 Cumartesi

Asynchronous and Synchronous


Merhabalar, bu blog ta asenkron(eş zamansız) ve senkron(eş zamanlı) terimleri üzerine notlarımı yazacağım.

Önce terimleri haberleşme(serial communication) açısından ele alalım.

Asynchronous Communication

  • Taraflar belirlenen bir zaman aralığında veri gönderip alırlar.(Baud Rate:9600)
  • Taraflar arasındaki haberleşme zaman aralığı farklı ise anlamsız veri transferi olur.
  • Donanımsal olarak extra bir ihtiyaç yoktur.
  • Arduino serial asenkron haberleşmeye olanak sağlamaktadır.
  • Veri gönderim hızı düşüktür.
Synchronous Communication
  • Tasarlanması için donanımsal olarak bir clock gereklidir.
  • Clock sinyaline göre veri gönderilir ve alınır.
  • Design aşaması asenkrondan daha zordur.
  • Yüksek hızda veri gönderilebilir.
  • SPI(Serial Peripheral Interface) ve RS232 destekleyen yapılardır.
*RS232 Her iki bağlantı türünüde destekler.


Şimdide yazılım açısından bu terimlere bakalım.

Synchronous Software
  • Aynı anda sadece bir işlem yürütülür.
    • Örneğin C/C++ gibi dillerde kodlarımız tek tek, sırayla işlenir.
  • Bir işlem yürütülürken diğerleri bloklanır.

Asynchronous Software
  • Belirli zamanda birden fazla iş yapılması
    • Örneğin JavaScript te setTimeOut ile sistemi manipüle ederek asenkron kod çalıştırılabilir. 
    • AJAX(Asynchronous JavaScript and XML) yine bu şekilde çalışan bir kütüphanedir.
  • Bir diğer aşamaya geçmek için anlık durumun bitmesi beklenmez. Bloklama durumu söz konusu değildir.

Source: http://tutorials.jenkov.com/software-architecture/index.html


2 Şubat 2017 Perşembe

Java Properties Dosyası Kullanımı

Merhabalar uzun bir aradan sonra yeni öğrendiğim bir bilgi ile buradayım. Daha önceki yazılarda data source den bahsetmiştim. Hem data source hemde program içinde hard olarak string tutmak yerine tutulması gereken verileri bir x.properties text dosyasında tutarız.

Neden Properties Dosyası?
- Geliştirilen sistemin bağlı oldugu DB bilgilerinin değiştirilmesi durumunda tüm programın yeniden derlenmesinin önüne geçilmesi istenir.
- Run-time esnasında bir dosyadan veriler çekilir ve güncel veriler ile sistem işlemeye devam eder.


Kod parçası üzerinden gidelim şimdide. getPropertiesFromFile metodumuza önce properties dosyamızın adını vermemiz gerekli. Sade Intellij/Eclipse üzerinde çalışıyorsanız prop dosyasını src dizinine eğer Maven tabanlı proje oluşturduysanız resources dizinine atmanız gerekli.


  1. Bir properties objesi oluşturup daha sonra getClass() ile runtime objemizi çağırıyoruz.
  2. Ardından getClassLoader() ile class loaderimizi çağıralım(önceki yazılarda bahsedildi).
  3. Dosyamızı input stream objesi olarak resource/source dizininden okuyoruz.
  4. Properties dosyasını load metodu ile inputstream ile dolduruyoruz.
  5. Hata olmaması durumunda properties dosyamız artık return edilecektir.
Şimdide properties dosyası için test yapalım.

hmenn.properties dosyası










test kodumuz:


System.out.println("db username:" + getPropertiesFromFile("hmenn.properties").getProperty("database.username"));


Çıktımız ise şu şekilde olacak:
db username:hmenn

** Bu şekilde kodumuz içine herhangi bir bilgiyi girmeden(sadece dosya adı) dinamik bir şekilde istediğimiz verileri elde edebildik. Bir serveri düşünecek olursak bu tarz bilgilerin dosyalardan okunması çok verimli olacaktır.

23 Ekim 2016 Pazar

Reentrancy - Side Effect


Merhabalar, bu yazıda Reentrancy(evrensellik) ve Side effect(yan etki) terimlerine değineceğim.

Reentrance fonksiyon nedir?

Bu fonksiyonlar olası sinyal, hata oluşumlarında sonlansa bile dış dünyaya bir etkisi(side effect) olmayan fonksiyonlardır. Yani sinyal gelse ve bu fonksiyon tekrar çağrılırsa ilk seferki gibi çalışmalı.

Reentrant şartları nelerdir?


  1. Herhangi bir static veya global değişken kullanılmamalı.
  2. Kendi yapısını/kodunu değiştirememeli.(İşletim sistemi tarafında)
  3. Non-reentrant bir fonksiyonu çağırmamalı.



int
g_var = 1; int f() { g_var = g_var + 2; // global degisken kullanmis return g_var; } int g() { return f() + 2; } Yuk


Yukarıdaki f fonksiyonunda hata olursa ve bir daha çağırılırsa bu fonksiyon eskisi gibi çalışamaz. Çünkü global bir değişkeni değiştirmiş olabilir.

int f(int i)
{
    return i + 2;
}

int g(int i)
{
    return f(i) + 2;
}


Bu örnekte ise global değişim kullanımı yok. Her çağırmada aynı çalışıp, side effect oluşturmayacaktır.

13 Ağustos 2016 Cumartesi

DTO, DAO, POJO/Bean ve JavaBean Nedir Nerelerde Kullanılır?

Merhabalar, bu yazdımda başlıktaki terimler üzerine yazacağım. Öncelikle temel amaç java ve veritabanı arasındaki ilişklileri daha iyi anlamak, ilerde görebileceğimiz terimlerin olduğu hakkında ufak bilgi sahibi olmak. Gelelim terimlerimize:

DAO(Data Access Object): Genellikle veri tabanı üzerinde yapılacak işlemleri yüklenen sınıflardır. DAO içine database işlemlerimizi yazıp programda karışıklık ve tekrar tekrar kullanmanın(code reuse) önünü açarız. Örneğin:

public class UserDAOService{
    public boolean addUser(){
     // some magic here
    }

   ... // some magic here
}

UserDAOService sınıfımız artık tüm user işlemlerini yükleniyor. Gidipte başka işlemler yaptığımız kod parçacıkları arasına SQL komutları sıkıştırıp işimizi zorlaştırmayacağız.

DTO(Data Transfer Object): Adından anlaşılacağı üzere bu objeler katmanlar, birimler arasında sadece veri aktarımı için kullanılırlar. (DB ve JVM arasında örneğin , JVM-JVM arasında)

  1. Depolama dışında herhangi bir yükümlülükleri yoktur.
  2. Setter/getter kullanmına gerek yoktur. Tüm veriler public olarak tutulabilir.
JavaBean: JavaBean dediğimiz objeler veri tutma amaçlı kullanılırlar. Bir objenin JavaBean olarak sayılabilmesi için 3 şartı sağlaması gereklidir.

  1. Serializable olmalı.
  2. Parametresi yapıcı metodu olmalı.(No parameter constructor)
  3. Public setter/getter metodlarına sahip olmalı
Teknolojinin gelişmesiye verilerimizi artık dosyalarda(text dosyasında örneğin) değilde veri tabanları üzerinde tutuyoruz. Bunun için Serializable olarak implement etme yöntemi pek kullanılmıyor. Bu durumlarda Bean/POJO kullanımı daha yaygın görülür. 


POJO/Bean(Plain Old Java Object): Bu obje de veri depolamak için kullanılırlar. JavaBean den farklı herhangi bir zorlamaya sahip değildir. Örneğin kullanıcı bilgilerini tutacağımız bir objeye UserBean diyebiliriz.

public class UserBean{
    private String name;
    private String password;

    public UserBean(){} // no parameter constructor
    // add other constructors

    public String getName(){return name;}
    public void setName(String name){this.name = name};

    // some magic here
}

Terimleri olabildiğince açık anlatmaya çalıştım. Özellikle java tabanlı projelerde bu isimleri yada paketleme isimlerini bolca görmekteyiz. Benim artık uyguladığım paketlemeler ise şu şekilde:

com.hasanmen.app.dao -> tüm daolar burada
com.hasanmen.app.bean -> tüm bean classları burada
...

Böylelikle hem karışmaları engelleriz. İstediğimizi daha çabuk ayırt ederiz hemde kod tekrarlarının önüne bir nebze geçmiş oluruz.


9 Ağustos 2016 Salı

32Bit Yaz Stajı

Merhabalar arkadaşlar bu yazımda yaz stajımın nasıl geçtiğini anlatacağım.


Öncelikle 32Bit firması Tübitak MAM Teknoketinde JEE, Entegrasyon, Mobil uygulama gelişirme gibi birçok alanda çalışmalar yürüten bir kurum. Tercih etmemdeki en büyük etken okuduğum okula(This is GTÜÜÜÜ) ve yurda yakın olmasıydı.

Başvuru ve Mülakat: İnternet sitesi üzerinden CV yollarak başvuruda bulundum. Açıkçası 3. ve 4. sınıflara öncelik verirler diye olumlu bir haber beklemiyordum ama 1-2gün geçmeden 32Bit İnsan kaynaklarından mail gelmişti. Mülatak için gelmemi istemiştiler. Belirtilen zamanda gittim. İlk mülakat görüşmem olduğu için heyecan vardı. Genel olarak CV üzerine yazdığım dersler hakkında sorular soruldu. Daha sonra tahtada 3tane soru vardı ve bunları herhangi bir dilde yazmamızı istedi. O aşamayıda geçtik. İkinci bir aşama olarak verilen bir projeyi yamamız gerekliydi. Daha sonra projeyi yapanlar arasında eleme olacak staj başlıyacak.

Yaptığım projeye buradan erişebilirsiniz.

Bu arada yaz döneminde her ay staj programı var.

Staj Başlangıcı: 
Staj sorumlumuz olarak Burak abi bize yardımcı olacakmış. Staj döneminde neler yapılacağını karalaştırdık. Tüm stajda yapılanları anlatmam çok uzun sürer ama Staj defterimi daha sonra paylaşacağım. Kısaca:

1.Hafta - Kurulumlar ve Java SE
Linux, MySQL, SQuireL, Tomcat, Eclipse-Mars, Sublime ve daha birçok uygulama. İlk hafta özetle kurulumlar ve ardından SQL çalışmalarıyla geçti. Yeni yeni terimler öğrendim.

2.Hafta - Java WEB
Bu hafta artık Tomcat Server üzerine servletler ile çalışmalar yaptık. Bean, DAO, DTO gibi terimleri öğrendik. ContextListener falan filan. Çok yoğun çook.

3.Hafta - JavaScript
Loglamalar, datasource üzerinde çalışmalar yaptık ve sonradan JS, JQuery, Bootstrap ile arayüz oluşturmaya yönelik çalışmalarla devam ettik.

4.Hafta - JavaWEB Extra
Jersey Service, JSON, Ajax ve web güvenliği üzerine çalışmalar yürüttük.

Ve daha bir sürü şey. Çok yoğun bir staj programı var. Ve 4hafta bitince staj bitmiyor. Birde bitirme projesi oluyor. Buda stajda öğrenilen konuların pekiştirilmesi açısından çok faydalı.

Evet dediğim gibi şuan staj projemiz üzerinde çalışıyoruz. 2 kişi yapıyoruz şuan projeyi. Ben ve Sinem(takım arkadaşım, kendisi İTÜ lü, çok iyi biridir, hey mate :D).

Projeyi yapıp bitirdikten sonra sunarak defteri imzalatıyorsunuz ve staj bitiyor.

YEMEKLER VE ULAŞIM:
İstanbul ve Kocaeli'nin her yerine Tübitak servisi var. Ayrıca öğlen yemekhanede çıkan yemeklerde çok güzel. ÖZELLİKLE TATLILAR EFSANE.

29 Temmuz 2016 Cuma

Tomcat üzerinde Connection Pool nasıl kullanılır?

Merhaba, bu yazımda Connection Pool nedir, Tomcat üzerinde nasıl connection pool oluşturulur, neden oluşturulur gibi başlıklara değineceğim.

Connection(Bağlantı) nedir?
Connection bir veritabanı üzerinde işlem yapabilmek için açtığımız bağlantıdır.

Bağlantı açarken nelere dikkat etmemiz lazım?
Kaynakların verimli kullanılması açısından DB(database) ye bağlanıp işlem yaptıktan sonra bağlantıyı kapatmamız lazım. Tüm program döngüsü boyunca ortalıkta boş boş takılan bağlantılar olmasını istemeyiz. Bu durumdada her işlem için bağlantı açmamız gerekecektir. Bağlantı açma ise biraz masraflıdır (yaklaşık 10ms), özellikle geniş trafiğe sahip sistemleri düşündüğümüzde sürekli bağlantı açma-kapama db mizi sıkıntıya sokabilir.

Peki en ideal bağlantı açma yöntemi nedir?
Connection Pool dediğimiz kavram burada devreye giriyor. Çevirisidende anlaşılacağı üzere bağlatılarımızın depolandığı bir havuz. Bu havuzun çalışma mantığıda şöyle.

  1. Tomcat serverimiz çalıştırıldığında belirtilen sayı kadar bağlantı açar. (20,30,40...)
  2. Client işlem yapmak istediğinde bağlantıları artık db den değilde tomcat üzerinden ister.
  3. İşlemi bitince bağlantıyı tekrardan havuza yollar ve kaynakların verimli kullanılmasını sağlar.

 
Bağlantı istekleri Pool üzerinden yapılır.


Anlattığım gibi server ayağa kalktığında 1 defaya mahsus olarak gerekli bağlantılar açılır ve depolanır. Sonraki erişimler yaklaşık 3ms sürecektir. Kabaca işlem başına 7ms kardayız. 

Tomcat Server üzerinden pool nasıl ayarlanır?

1. tomcat/contex.xml dosyamıza datasource tanımlaması yapılır. 


    <ResourceLink type="javax.sql.DataSource" name="jdbc/poolTest" global="jdbc/poolTest"/>
    // verdiğimiz name üzerinden erişim sağlayacağız.

2. tomcat/server.xml dosyamıza pool bilgilerini gireriz. 
// GlobalNamingResources tag ı arasına eklenecek.

 <Resource type="javax.sql.DataSource"
       auth="Container"
        name="jdbc/poolTest"
        driverClassName="com.mysql.jdbc.Driver"
       factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
        url="jdbc:mysql://127.0.0.1:3306/testDB"  // db adresimiz
        username="testUser" // db username
        password="testUser" // db password
  initialSize="10"  // standart bağlantı sayısı
        maxActive="20"  // max bağlantı sayısı
        maxIdle="10"  // max boş bağlantı sayısı
        minIdle="5"/>  // min boş bağlantı sayısı

3. Projemizin web.xml dosyasına poolu tanıtalım.

  <resource-ref>
<description>DB Connecion Pool Source</description>
<res-ref-name>jdbc/testDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>

Bu eklemelerden sonra server çalıştığı esnada bağlantılar açılacaktır. 



 ** Benim Veritabanım üzerinde açık bağlantılar. Ve gördüğünüz gibi kurtlar hep uyku modunda ve atıl kurt dememizi bekliyorlar. Mysql Workbench ile bakılmıştır. İsimler değişiklik gösterebilir.

Son olarak bu bağlantıları nasıl kullancağımıza bakalım. Java projelerimizde bu bağlantıları çağırabilmek için verileri serverden okuyup bir datasource içinde tutacağız. Daha sonra bu kaynaktan bağlantılarımızı isteyip kullanabiliriz.


private Connection getConnectionFromPool() throws NamingException, SQLException {
Context envCtx = (Context) new InitialContext();
// Look up our data source
DataSource ds = (DataSource) envCtx.lookup("java:comp/env/jdbc/testDB");
Connection conn = ds.getConnection();
logger.debug("Connection taken");
return conn;
}

Son olarak bağlantılar ile işiniz bittikten sonra havuza geri göndermeyi unutmayın. conn.close() ile kapatabilirsiniz. Sonraki yazımda görüşmek üzere :D






28 Temmuz 2016 Perşembe

JVM Classloader Nasıl Çalışır?




Classloader java klaslarının Java sanal makinesine(JVM) yüklenmesini sağlar. 3 farklı classloader bulunmaktadır. Peki bunlar nelerdir?

1. Bootstrap(ön yükleyici) Classloader :
  • İlk olarak çalışan classloader dir.
  • Yüklemeye \jre\lib\rt.jar konumundan başlar ve temel java klaslarını yükler.(util, lang).
  • Native olarak yazılmış, çoğunluğunda C dili kullanılmıştır.
  • Diğer tüm classloaderlerin atasıdır(Parent). Root olarak düşünebiliriz.
  • Primordial Classloader olarakta bilinir.
2. Extension Classloader :
  • İkinci olarak çalışır ve class dosyalarını yüklemeye jre/lib/ext klasöründen yada java.ext.dirs tarafından belirtilen kısımlardan başlar.
  • Atası (Parent) bootstrap classloader dir.
  • Java ile yazılmıştir.
3. Applicaton/System Classloader :
  • Son olarak system classloader çalışır.
  • Atası(Parent) extension classloader dir.
  • Genellikle uygulama bağımlı klas dosyalarını yüklemek için kullanılırlar.
  • Yüklemeye CLASSPATH ten başlar. Eğer -cp parametresi ile klas eklenmiş ise onlarıda yükler.

Classloader ler 3 temel prensibe dayanarak çalışırlar
  1. Delegation (Delegasyon-Yetkilendirme)
  2. Visibility
  3. Uniqueness (Eşsizlik)
Delegation: Bir loader eğer yükleme yapacaksa atalarına bildirmeden yükleme yapamaz.
Sözde kod ile anlatacak olursak :  
  1. Extension classloader hmenn klasini yüklemek ister
  2. Parenti olarak bootstrapa aynı klasın önceden yüklenme durumunu sorar.
  3. Eğer yüklenmemişse yükler, yüklendiyse yükleme işlemini iptal eder.
Bu durum system classloader içinde geçerlidir. Klasın daha önceden yüklenme durumunu parentlerine sırayla sorar.

Visibility: Bir loader parenti tarafından yüklenen klasları görebilir ama parent(ata) child(çocuk) ların yüklediğini göremez.

Uniqueness: Aynı isimde 2 klas yüklenemez. Delegasyondandaki sorgulama evresi ile bunun önüne geçilir.

Kendimiz RUNTIME esnasında nasıl klas yükleriz ?

Java klas yüklememiz için bize bir API sağlar. Bu API aracılığıyla Class.forName(klasAdi) metodu ile klas yükleyebiliriz. Bu işlem sırasıyla şöyle gerçekleşir.

  1. loadClass() metodu çalışır ve jar dosyaları arasında klası arar.
  2. Eğer klas var ise yüklemeyi gerçekleştirir.
  3. Eğer yok ise ClassNotFoundException fırlatır.




17 Temmuz 2014 Perşembe

Boolean Algebra Calculator V1















Kaynak .c dosyası

#include <stdio.h>

int conj(int x,int y);

int disj(int x,int y);

int not(int x);

int mate(int x,int y);

int excl(int x,int y);


int main(void)
{
    int x,y;
    printf("Enter X = ");
    scanf("%d",&x);

    printf("Enter Y = ");
    scanf("%d",&y);

    printf("\nConj X - Y     = %d\n",conj(x,y));

    printf("\nDisj X - Y     = %d\n",disj(x,y));

    printf("\nNot X          = %d\n",not(x));

    printf("\nMate X - Y     = %d\n",mate(x,y));

    printf("\nExcl X - Y     = %d",excl(x,y));

    return 0;


}

int conj(int x,int y)
    {
        if(x==1){
            if(y==1)
                return 1;
            else
                return 0;}
           else
                return 0;
    }

int disj(int x,int y)
    {
        if(x==0){
            if(y==0)
                return 0;
            else
                return 1;
        }   else
                return 1;
    }



int not(int x)
    {
        if(x==1)
            return 0;
        else
            return 1;
    }

int mate(int x,int y)
    {
       return disj(not(x),y);
    }

int excl(int x,int y)
    {
        return conj(disj(x,y),not(conj(x,y)));
    }

16 Temmuz 2014 Çarşamba

Deitel How to C - Example 5.45(koordinatlar arası mesafe )

5.45 mesafe isminde, verilen iki noktanın, (x1, y1) ve (x2, y2), arasındaki mesafeyi bulan bir fonksiyon yazınız. Kullanacağınız bütün sayılar ve fonksiyonun döndüreceği değer float türünde olsun.

İsteyen Drive'den .c kaynak dosyayı buradan indirebilir.

/*        created by hmenn          */
/*----------------------------------*/
/*     Time : 16.07.2014 / 03.00    */
/*----------------------------------*/
/*Iki koordinat arasındaki mesafeyi */
/*float türü değişkenlerle bulmak.  */
/*----------------------------------*/

/* Kullanýlacak kütüphaneler */

#include <stdio.h>
#include <math.h>

/* Fonksiyonun tanıtılması */

float mesafe(float x1, float y1,float x2,float y2);

int main()
{

/* Deðer türlerinin belirtilmesi */
    float x1,y1;
    float x2,y2;

/*Kullanýcýdan deðerleri alma */
    printf("1.Koordinatlari giriniz.\n");
    printf("X1 = ");
    scanf("%f",&x1);

    printf("Y1 = ");
    scanf("%f",&y1);

    printf("2.Koordinatlari giriniz.\n");

    printf("X2 = ");
    scanf("%f",&x2);

    printf("Y2 = ");
    scanf("%f",&y2);

/* Sonuç ve fonksiyonun çaðýrýlmasý */

    printf("Koordinatlar arasi mesafe = %.2f",mesafe(x1,y1,x2,y2));

/* Programdan çýkýþ */
    return 0;

}

/* Fonksiyon içeriði */

float mesafe(float x1, float y1,float x2,float y2)
    {
        float mesafe = sqrt(pow(x1-x2,2)+pow(y1-y2,2));

/* Sonucun ana fonksiyona gönderilmesi */
        return mesafe;
    }




14 Temmuz 2014 Pazartesi

Deitel How to C - Example 5.32(Çarpma İşlemi) -

Bilgisayarların eğitimde sürekli artan bir rolü vardır. Bir ilkokul öğrencisine çarpma işleminde yardımcı olacak bir program yazınız. rand fonksiyonunu kullanarak rasgele 2 adet 1 basamaklı sayı üretin ve ekrana şu şekilde bir soru yazdırın:
4 kere 7 kaçtır ?
Daha sonra öğrenci cevap versin. Program cevabı kontrol etsin. Eğer cevap doğruysa "Çok güzel" yazdırsın ve yeni bir soru sorsun. Eğer cevap yanlışsa "lütfen tekrar deneyin." yazdırsın ve aynı soruyu öğrenci doğru cevap verene kadar sorsun.

İsteyen Drive'den .c kaynak dosyayı buradan indirebilir.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    int a,b,sonuc;
    srand(time(NULL));

    a = rand()%10;
    b = rand()%10;

    printf("%d * %d kaçtir? =  ",a,b);
    scanf("%d",&sonuc);

    if(sonuc== a*b)
        {printf("Aferin dogru!\n");

        }
    else
    {
        printf("Tekrar deneyin\n");
        while(a*b!=sonuc)
        {
            printf("%d * %d kaçtir? =  ",a,b);
            scanf("%d",&sonuc);

    if(sonuc== a*b)
        {
            printf("Aferin dogru!\n");
        }
    else
        printf("Tekrar deneyin\n");

        }
    }


}