BG MVC Model View Controller eğitim serisi yayında...

Ana sayfa > Programlama > C MySql Programlama > Gömülü Sunucu

Gömülü Sunucu

Bu bölümde, C programlama dilinde CodeBlocks IDE üzerinde WinAPI kodları ile Gömülü Sunucu (Embedded Server) üzerinde MySQL veritabanı uygulamaları geliştirmeye başlayacağız.

Gömülü sunucu (Embedded Server - Libmysqld) kurulumu

Bu bölümde, C programlama dilinde Bağımsız Sunucu kullanarak geliştireceğimiz MySQL uygulamaları için gerekli dosyaları indirme ve Code:Blocks IDE hazırlama işlemini gerçekleştireceğiz:

Eğer sisteminizde Code:Blocks IDE kurulu değilse öncelikle buradaki talimatları uygulayarak kurunuz.

Gömülü sunucu çalıştırmak için sadece MySQL Community Server'ın zip uzantılı dosyası içinde yer alan aşağıdaki dosyalara ihtiyacımız vardır:

  • libmysqld.lib
  • libmysqld.dll (Programın çalıştığı dizine kopyalanmalıdır.)

1. Öncelikle, MySQL resmi sitesinden MySQL Community Server'ın zip uzantılı dosyalarından sistemimize uygun olan 32 veya 64 bit sürümlerinden birisini indirelim.

İhtiyacımız olan dosyalar MSI Installer paketi içinde yer almamaktadır.

2. İndirdiğimiz .zip uzantılı dosyayı (32 bit sürümünü indirdiğinizi düşünerek - mysql-5.6.11-win32.zip) bilgisayarımızda uygun olan bir sürücüye açalım. Dosyayı C: sürücüsünde açtığımızda elde ettiğimiz dizin yapısı aşağıda gösterilmektedir:

3. Artık, MySQL Gömülü Sunucu kütüphanesini CodeBlocks IDE içine entegre etmeye başlayabiliriz. Code::Blocks IDE'yi çalıştırdığımızda karşımıza gelen ekranda aşağıdaki resimde gösterilen Settings-Compiler... menü seçeneğine tıklayalım:

4. Karşımıza gelen pencereden "Linker settings" bölümünü seçelim ve "Add" butonuna tıklayalım:

5. Karşımıza gelen pencerede butonuna tıklayarak bilgisayarımızdaki mysql-5.6.11-win32 dizini altında yer alan lib dizinindeki libmysqld.lib dosyasını ekleyelim ve "OK" butonuna tıklayalım:

6. Bu durumda, libmysqld.lib dosyası "Link libraries" bölümüne eklenmiş olacaktır.

7. Karşımıza gelen pencereden "Search directories" bölümünü altındaki "Compiler" bölümünü seçelim ve "Add" butonuna tıklayalım:

8. Karşımıza gelen pencerede butonuna tıklayarak, bilgisayarımızdaki mysql-5.6.11-win32 dizini altında yer alan include dizinini ekleyelim ve "OK" butonuna tıklayalım:

9. Bu durumda, include dizini "Compiler" bölümüne eklenmiş olacaktır.

10. Bu defa, "Search directories" bölümünü altındaki "Linker" bölümünü seçelim ve "Add" butonuna tıklayalım.

11. Karşımıza gelen pencerede butonuna tıklayarak, bilgisayarımızdaki mysql-5.6.11-win32 dizini altında yer alan lib dizinini ekleyelim ve "OK" butonuna tıklayalım:

12. Bu durumda, lib dizini "Linker" bölümüne eklenmiş olacaktır.

Böylece, C programlama dilinde WinAPI kodları ile Bağımsız Sunucu kullanarak geliştireceğimiz MySQL veritabanı uygulamaları için sistemimiz hazır hale geldi. Artık, çalışmalarımıza başlayabiliriz.

Gömülü sunucu (Embedded Server - Libmysqld) ile çalışan uygulama geliştirme

Öncelikle, gömülü sunucu uygulaması geliştirmek için oluşturduğumuz programın altında 2 farklı dizin oluşturmamız gerekir:

language dizini

Bu dizini oluşturmanız ve dizinin içine C:\mysql-5.6.11-win32\share\english dizini içindeki errmsg.sys dosyasını kopyalayalım.

data dizini

Bu dizin veritabanlarının yer alacağı dizindir.

Geliştireceğimiz program çalışır çalışmaz, öncelikle programın bulunduğu dizin altında data adlı bir dizin oluşturur (eğer yoksa). Sonra, MySQL kütüphanesini başlatarak, gömülü bir sunucu oluşturur ve sunucuya bağlanır. Eğer mevcut değilse bir veritabanı oluşturur, veritabanını aktif hale getirerek eğer mevcut değilse 5 kayıtlı bir tablo oluştur ve bağlantıyı kapatır.

Şimdi, programımızı oluşturmaya çalışalım:

1. Öncelikle WinAPI bölümünde hazırladığımız program kalıplarından birini Code:Blocks IDE içinde açalım.

2. main.c dosyası içine aşağıdaki eklemeleri yapalım:

main.c

Aşağıdaki satırlar programın başına global değerler olarak eklenir:


#include <windows.h>
#include <stdio.h>    /* C fonksiyonları için başlık dosyası */ 
#include <mysql.h>    /* MySQL komutları için */
#include "main.h"           /* Fonksiyon bildirimleri başlık dosyası */    

#define IDC_BUTTON01 101    /* Buton kontrolü için tanımlayıcı */
#define IDC_LISTBOX01 201   /* Listbox kontrolü için tanımlayıcı */

char szClassName[ ] = "WinAPIWindowsApp"; /* Sınıf adını global bir değişkene atama */
HINSTANCE ghInst;                         /* HINSTANCE cinsinden global bir değişken bildirimi */ 

/* Program ana pencere ve kontrol pencere bildirimleri */
HWND hwndMain;                
HWND hwndButton01, hwndListBox01;

MYSQL *conn;         /* Veritabanı bağlantısı Handle değeri */
MYSQL_RES *result;   /* Satır bilgilerinin tamamı için */
MYSQL_ROW row;       /* mysql_fetch_row() fonksiyonu ile elde edilen satır */ 

/* mysql_library_init() fonksiyonu için kullanılacak olan sunucu seçenekleri */
/* Veritabanı ve hata dosyası dizinlerinin yol tanımlaması */
static char *server_options[] = { "libmysqld_server", "--datadir=./data", "--language=./language", NULL};
int num_elements = (sizeof(server_options) / sizeof(char *)) - 1;
static char *server_groups[] = { "libmysqld_server", NULL };

#include <mysql.h>

MySQL ile ilgili tüm fonksiyon ve veri bildirimlerinin yapıldığı başlık dosyasıdır.

MYSQL *conn; : mysql_init() fonksiyonu elde edilen bu değeri hemen hemen tüm MySQL fonksiyonları kullanır. mysql_real_connect() fonksiyonu sunucuya bağlanmak için, mysql_query() fonksiyonu bir SQL komutu uygulamak için, mysql_store_result() fonksiyonu mysql_query() ile elde edilen sonuçları aktarmak için bu yapıyı kullanır.

MYSQL_RES *result; : mysql_store_result() fonksiyonu, mysql_query() fonksiyonu tarafından conn bağlantısı üzerinden elde edilen verileri bu yapıya atar.

MYSQL_ROW row; : mysql_fetch_row() fonksiyonu, mysql_store_result() fonksiyonu tarafından elde edilen veride her defasında bir satır olmak üzere satır değerlerini bir karakter dizisi olarak okuyarak bu değişkene atar.

WM_CREATE mesajı ile ana program penceresi ile birlikte programın bulunduğu dizin altında data adlı bir dizin oluşturulur (eğer yoksa). Sonra, bir buton ve Listbox kontrolü oluşturulur.

SunucuOpr() fonksiyonu, MySQL kütüphanesini başlatır, gömülü bir sunucu oluşturur ve sunucuya bağlanır. Eğer mevcut değilse personel adlı bir veritabanı oluşturur, veritabanını aktif hale getirerek eğer mevcut değilse 5 kayıtlı ogrenci adlı bir tablo oluştur ve bağlantıyı kapatır.


case WM_CREATE:

   if (!VeriDizinOlustur()) MessageBox(NULL, "Yedekleme dizini oluşturma hatası!", "Sistem hatası", MB_OK | MB_ICONWARNING);

   hwndButton01 = CreateWindowEx(0, "BUTTON", "Veritabanı Oku",
                         WS_CHILD | WS_VISIBLE | BS_NOTIFY, 20, 20, 120, 25,
                         hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);
   hwndListBox01 = CreateWindowEx(0, "LISTBOX", NULL,
                         WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 20, 60, 400, 400,
                         hwnd, (HMENU) IDC_LISTBOX01, NULL, NULL);

   if(!SunucuOpr()) MessageBox(NULL, mysql_error(conn), "Sunucu işlem hatası", MB_OK | MB_ICONWARNING);

   break;   


BOOL SunucuOpr(void)
{
  /* MySQL kütüphanesini başlatarak gömülü sunucu oluşturur. */
  if (mysql_library_init(num_elements, server_options, (char **) server_groups)) return 0;

  /* mysql_real_connect() fonksiyonu için uygun bir MYSQL nesnesi tahsis eder. */
  conn = mysql_init(NULL);
  if (conn == NULL) return 0;

  /* Bağlantı için seçenekler belirler, bağlantıda gömülü sunucu kullanımını sağlar.*/
  if (mysql_options(conn, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL)) return 0;

  /* Sunucuya bağlanır. */
  if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0) == NULL) {
      mysql_close(conn);
      return 0;
  }

  /* Veritabanı oluşturma, eğer mevcut değilse */
  if (mysql_query(conn, "CREATE DATABASE IF NOT EXISTS personel DEFAULT CHARACTER SET latin5 DEFAULT COLLATE latin5_turkish_ci")) {
      mysql_close(conn);
      return 0;
  }

  /* Veritabanını aktif hale getirme */
  if (mysql_query(conn, "USE personel")) {
      mysql_close(conn);
      mysql_close(conn);
      return 0;
  }

  /* Türkçe karakter işlemi için */
  mysql_query(conn, "SET NAMES 'latin5'");
  mysql_query(conn, "SET CHARACTER SET latin5");
  mysql_query(conn, "SET COLLATION_CONNECTION = 'latin5_turkish_ci'");

  /* personel veritabanında ogrenci tablosu oluşturma */
  if (mysql_query(conn, "CREATE TABLE IF NOT EXISTS personel.ogrenci (Adi VARCHAR(20) DEFAULT NULL, Soyadi VARCHAR(20) DEFAULT NULL, Memleketi VARCHAR(20) DEFAULT NULL, PRIMARY KEY (Adi)) DEFAULT CHARACTER SET latin5 DEFAULT COLLATE latin5_turkish_ci")) {
      mysql_close(conn);
      return 0;
  }

  /* ogrenci tablosunda kayıt var mı yok mu? Yoksa veri girişi */
  mysql_query(conn, "SELECT * FROM ogrenci");
  result = mysql_store_result(conn);

  if (!mysql_num_rows(result)) {
      /* personel veritabanında ogrenci tablosuna veri girişi */
      if (mysql_query(conn, "INSERT INTO ogrenci (Adi, Soyadi, Memleketi) VALUES ('Mehmet', 'Saygılı', 'Gümüşhane'), ('Salih', 'Uysal', 'Sakarya'), ('Özer', 'Kalem', 'Bursa'), ('Murat', 'Ballıdere', 'Kütahya'), ('Kemal', 'Kara', 'Edirne')")) {
          mysql_close(conn);
          return 0;
      }
  }

  mysql_free_result(result);
  mysql_close(conn);

  return 1;
}

WM_COMMAND altında IDC_BUTTON01 butonuna tıklandığında çalıştırılan VeriOku() fonksiyonu içinde:

mysql_init() fonksiyonu ile mysql_real_connect() fonksiyonunda kullanılmak üzere uygun bir MYSQL nesnesi oluşturulur. mysql_real_connect() fonksiyonu ile sunucu üzerinde çalışan MySQL veritabanına bir bağlantı oluşturulur. Tablodan kayıtlar okunur ve her bir kayıt sıra ile Listbox içine yazılır:


case WM_COMMAND:
   switch(HIWORD(wParam)) {
      case BN_CLICKED:
           switch(LOWORD(wParam)) {
              case IDC_BUTTON01:
                   if(!VeriOku()) MessageBox(NULL, mysql_error(conn), "Sunucu işlem hatası", MB_OK | MB_ICONWARNING);
                   break;
           }
      break;
   }
   break;	


BOOL VeriOku(void)
{
  char cdizi[100];

  conn = mysql_init(NULL);
  if (conn == NULL) return 0;

  if (mysql_options(conn, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL)) return 0;

  /* Sunucudaki personel adlı MySQL veritabanına bir bağlantı oluşturur. */
  if (mysql_real_connect(conn, NULL, NULL, NULL, "personel", 0, NULL, 0) == NULL) {
      mysql_close(conn);
      return 0;
  }

  /* Türkçe karakter işlemi için */
  mysql_query(conn, "SET NAMES 'latin5'");
  mysql_query(conn, "SET CHARACTER SET latin5");
  mysql_query(conn, "SET COLLATION_CONNECTION = 'latin5_turkish_ci'");

  /* Tablodan tüm verileri alma */
  mysql_query(conn, "SELECT * FROM ogrenci");

  result = mysql_store_result(conn);

  /* Kayıtları satır satır Listbox içine yazdırma */
  while((row = mysql_fetch_row(result))) {
     sprintf(cdizi, "%s %s %s", row[0], row[1], row[2]);
     SendMessage(hwndListBox01, LB_ADDSTRING, 0, (LPARAM) cdizi);
  }

  SendMessage(hwndListBox01, LB_SETCURSEL, 0, 0);

  mysql_free_result(result);
  mysql_close(conn);

  return 1;
}

WM_DESTROY altında çalışan mysql_library_end() fonksiyonu ile program sona ermeden önce gömülü sunucuyu kapatır.

mysql_init() fonksiyonu ile mysql_real_connect() fonksiyonunda kullanılmak üzere uygun bir MYSQL nesnesi oluşturulur. mysql_real_connect() fonksiyonu ile sunucu üzerinde çalışan MySQL veritabanına bir bağlantı oluşturulur. Tablodan kayıtlar okunur ve her bir kayıt sıra ile Lİstbox içine yazılır:


case WM_DESTROY:   
     /* Program sona ermeden önce gömülü sunucuyu kapatır. */
     mysql_library_end();
     PostQuitMessage (0);
     break;   

Programın çalışması için libmysqld.dll dosyasını mutlaka programın bulunduğu dizine kopyalayalım!

Programın çalışan en son halinde yer alan dosyalar aşağıdadır:

main.h


#ifndef MAIN_H
#define MAIN_H

LRESULT CALLBACK WindowProcedure (HWND, UINT, WPARAM, LPARAM);
BOOL VeriOku(void);
BOOL SunucuOpr(void);
BOOL VeriDizinOlustur(void);
BOOL DizinVarmi (LPCTSTR);

#endif

main.c


#include <windows.h>
#include <stdio.h>
#include <mysql.h>
#include "main.h"

#define IDC_BUTTON01 101
#define IDC_LISTBOX01 201

char szClassName[ ] = "WinAPIWindowsApp";
HINSTANCE ghInst;
HWND hwndMain;
HWND hwndButton01, hwndListBox01;

MYSQL *conn;
MYSQL_RES *result;
MYSQL_ROW row;

static char *server_options[] = { "libmysqld_server", "--datadir=./data", "--language=./language", NULL};
int num_elements = (sizeof(server_options) / sizeof(char *)) - 1;
static char *server_groups[] = { "libmysqld_server", NULL };

int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nCmdShow)
{
  MSG messages;
  WNDCLASSEX wincl;

  ghInst = hThisInstance;

  wincl.hInstance = hThisInstance;
  wincl.lpszClassName = szClassName;
  wincl.lpfnWndProc = WindowProcedure;
  wincl.style = CS_DBLCLKS;
  wincl.cbSize = sizeof (WNDCLASSEX);

  wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
  wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
  wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
  wincl.lpszMenuName = NULL;
  wincl.cbClsExtra = 0;
  wincl.cbWndExtra = 0;
  wincl.hbrBackground = GetSysColorBrush(COLOR_3DFACE);

  if (!RegisterClassEx (&wincl)) return 0;

  hwndMain = CreateWindowEx (0, szClassName, "Gömülü Sunucu (Embedded Server) Programı",
                             WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                             800, 600, HWND_DESKTOP, NULL, hThisInstance, NULL);

  if (!hwndMain) return 0;

  ShowWindow (hwndMain, nCmdShow);
  UpdateWindow(hwndMain);

  while (GetMessage (&messages, NULL, 0, 0) > 0)
  {
      if (!IsWindow(hwndMain) || !IsDialogMessage(hwndMain, &messages))
      {
         TranslateMessage(&messages);
          DispatchMessage(&messages);
      }
  }

  return messages.wParam;
}

LRESULT CALLBACK WindowProcedure (HWND hwnd,UINT message,WPARAM wParam,LPARAM lParam)
{
  switch (message) {

    case WM_CREATE:

       if (!VeriDizinOlustur()) MessageBox(NULL, "Yedekleme dizini oluşturma hatası!", "Sistem hatası", MB_OK | MB_ICONWARNING);

       hwndButton01 = CreateWindowEx(0, "BUTTON", "Veritabanı Oku",
                             WS_CHILD | WS_VISIBLE | BS_NOTIFY, 20, 20, 120, 25,
                             hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);
       hwndListBox01 = CreateWindowEx(0, "LISTBOX", NULL,
                             WS_CHILD | WS_VISIBLE | LBS_NOTIFY, 20, 60, 400, 400,
                             hwnd, (HMENU) IDC_LISTBOX01, NULL, NULL);

       if(!SunucuOpr()) MessageBox(NULL, mysql_error(conn), "Sunucu işlem hatası", MB_OK | MB_ICONWARNING);

       break;

     case WM_COMMAND:

       switch(HIWORD(wParam)) {
          case BN_CLICKED:

             switch(LOWORD(wParam)) {
                case IDC_BUTTON01:
                    if(!VeriOku()) MessageBox(NULL, mysql_error(conn), "Sunucu işlem hatası", MB_OK | MB_ICONWARNING);
                    break;
             }
          break;
       }

        break;

    case WM_DESTROY:
        mysql_library_end();
        PostQuitMessage (0);
        break;

    default:
        return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

BOOL SunucuOpr(void)
{
  /* MySQL kütüphanesini başlatmak için */
  if (mysql_library_init(num_elements, server_options, (char **) server_groups)) return 0;

  /* mysql_real_connect() fonksiyonu için uygun bir MYSQL nesnesi tahsis eder. */
  conn = mysql_init(NULL);
  if (conn == NULL) return 0;

  /* Bağlantı için seçenekler belirler, bağlantıda gömülü sunucu kullanımını sağlar.*/
  if (mysql_options(conn, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL)) return 0;

  /* Sunucuya bağlanır. */
  if (mysql_real_connect(conn, NULL, NULL, NULL, NULL, 0, NULL, 0) == NULL) {
      mysql_close(conn);
      return 0;
  }

  /* Veritabanı oluşturma, eğer mevcut değilse */
  if (mysql_query(conn, "CREATE DATABASE IF NOT EXISTS personel DEFAULT CHARACTER SET latin5 DEFAULT COLLATE latin5_turkish_ci")) {
      mysql_close(conn);
      return 0;
  }

  /* Veritabanını aktif hale getirme */
  if (mysql_query(conn, "USE personel")) {
      mysql_close(conn);
      return 0;
  }

  /* Türkçe karakter işlemi için */
  mysql_query(conn, "SET NAMES 'latin5'");
  mysql_query(conn, "SET CHARACTER SET latin5");
  mysql_query(conn, "SET COLLATION_CONNECTION = 'latin5_turkish_ci'");

  /* personel veritabanında ogrenci tablosu oluşturma */
  if (mysql_query(conn, "CREATE TABLE IF NOT EXISTS personel.ogrenci (Adi VARCHAR(20) DEFAULT NULL, Soyadi VARCHAR(20) DEFAULT NULL, Memleketi VARCHAR(20) DEFAULT NULL, PRIMARY KEY (Adi)) DEFAULT CHARACTER SET latin5 DEFAULT COLLATE latin5_turkish_ci")) {
      mysql_close(conn);
      return 0;
  }

  /* ogrenci tablosunda kayıt var mı yok mu? Yoksa veri girişi */
  mysql_query(conn, "SELECT * FROM ogrenci");
  result = mysql_store_result(conn);

  if (!mysql_num_rows(result)) {
      /* personel veritabanında ogrenci tablosuna veri girişi */
      if (mysql_query(conn, "INSERT INTO ogrenci (Adi, Soyadi, Memleketi) VALUES ('Mehmet', 'Saygılı', 'Gümüşhane'), ('Salih', 'Uysal', 'Sakarya'), ('Özer', 'Kalem', 'Bursa'), ('Murat', 'Ballıdere', 'Kütahya'), ('Kemal', 'Kara', 'Edirne')")) {
          mysql_close(conn);
          return 0;
      }
  }

  mysql_free_result(result);
  mysql_close(conn);

  return 1;
}

BOOL VeriOku(void)
{
  char cdizi[100];

  conn = mysql_init(NULL);
  if (conn == NULL) return 0;

  if (mysql_options(conn, MYSQL_OPT_USE_EMBEDDED_CONNECTION, NULL)) return 0;

  /* Sunucu üzerinde çalışan MySQL veritabanına bir bağlantı oluşturur. */
  if (mysql_real_connect(conn, NULL, NULL, NULL, "personel", 0, NULL, 0) == NULL) {
      mysql_close(conn);
      return 0;
  }

  /* Türkçe karakter işlemi için */
  mysql_query(conn, "SET NAMES 'latin5'");
  mysql_query(conn, "SET CHARACTER SET latin5");
  mysql_query(conn, "SET COLLATION_CONNECTION = 'latin5_turkish_ci'");

  mysql_query(conn, "SELECT * FROM ogrenci");

  result = mysql_store_result(conn);

  while((row = mysql_fetch_row(result))) {
     sprintf(cdizi, "%s %s %s", row[0], row[1], row[2]);
     SendMessage(hwndListBox01, LB_ADDSTRING, 0, (LPARAM) cdizi);
  }

  SendMessage(hwndListBox01, LB_SETCURSEL, 0, 0);

  mysql_free_result(result);
  mysql_close(conn);

  return 1;
}

BOOL VeriDizinOlustur(void)
{
  int don=1;
  char buffer[MAX_PATH];

  if (GetCurrentDirectory(MAX_PATH, buffer)) {
      strcat (buffer, "\\data");
      if (!DizinVarmi(buffer)) {
          if (!CreateDirectory(buffer, NULL)) {
              don=0;
          }
      }
  }
  else don=0;

  return don;
}

BOOL DizinVarmi (LPCTSTR szPath)
{
  DWORD dwAttrib = GetFileAttributes(szPath);

  return (dwAttrib != INVALID_FILE_ATTRIBUTES && (dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
}

Program çalıştığında karşınıza gelecek ekran görüntüsü aşağıdadır: