Sohbet programları haberleşmek için Winsock API�leri kullanır. Fakat sohbet programları gibi ağ uygulamaları geliştirmek için saf API kullanmak zahmetlidir. Bunun yerine MFC�nin CSocket sınıfını kullanacağız. CSocket sınıfı, CAsyncSocket sınıfından türemiştir ve CAsyncSocket�e göre WinsockAPI�den daha fazla soyutlanma sağlar. Genelde uygulamalarda doğrudan CSocket�i kullanmak yerine, CSocket�ten türetilmiş özel sınıflar kullanılır. CAsyncSocket�e ait olan Accept, Receive, Send gibi sanal işlevler amaca göre değiştirilir.
Sohbet programlarında iletişim TCP/IP tabanlı olduğu için bir sunucuya ve istemciye ihtiyaç vardır. İstemciler, dinleme durumundaki sunucunun IP adresi bilgisini kullanarak sunucuya bağlanır ve bilgi aktarımı sağlanır. Bundan dolayı sunucu ve istemci programımızı ayrı ayrı yazacağız.
Uygulama
Sunucu ve istemci programlarımızı geliştirirken aşağıdaki adımlar ortaktır. Öncelikle Visual C++ Projects kısmından MFC Application seçilmeli, proje özelliklerinden de uygulamanın dialog tabanlı olduğu ve Windows Sockets kullandığı belirtilmelidir.
Şekil 1 - Proje türü
Proje oluşturulduktan sonra kendi soket sınıfımızı oluşturmak için, ana menüde Project�e tıklayarak Add Class seçilmeli. Bunu yaptıktan sonra, eklemek istediğimiz sınıfın adını, türetileceği taban sınıfı ve hangi dosyanın içine kaydedileceğini belirtebileceğimiz bir dialog açılacaktır. Soket sınıfımızı CSocket�ten türeteceğimizi şu şekilde belirtebiliriz:
Şekil 2 - CSocket�ten türemiş sınıf oluşturma
Türetilen bu sınıfın istemci için tasarımı:
class sohbetSoket : public CSocket
{
public:
sohbetSoket();
virtual ~sohbetSoket();
virtual **** OnAccept(int nErrorCode);
virtual **** OnClose(int nErrorCode);
virtual **** OnConnect(int nErrorCode);
virtual **** OnReceive(int nErrorCode);
virtual **** OnSend(int nErrorCode);
**** alinanlariAt(CEdit *sAlinan);
CEdit *alinan;
CString buf;
}; şeklinde olacak, ve sunucu versiyonunda fazladan sadece
sohbetSoket *kabul;
satırını içerecektir. Bu yeni türettiğimiz sınıfı ana dialog sınıfı içinde kullanacağımız için başlık dosyasına:
#include"sohbetSoket.h"
satırını eklemeliyiz. Programımızın tasarımını Şekil 3�deki gibidir. Şekil 3�deki kontrollerin üzerine tıkladığınızda, tıkladığınız kontrolün ID�sini ve atanacak değişkeni görebilirsiniz.
Şekil 3 - Projenin görünümü ve kontrollere atanan değişkenler
Her iki formda da ortak olan değişkenlerden port isimli değişken, haberleşmenin hangi porttan yapılacağını belirtir. alinan isimli metin kutusu karşı taraftan alinanlari barindirmak için vardır. Gönder düğmesine basılınca metin isimli metin kutusundaki yazılanlar gönderilir.
Sunucu ve istemci sınıfının üye değişkenleri şunlardır:
bool bagli; //sadece istemci sınıfında
bool dinlemede; //sadece sunucu sınıfında
TSoket soket;
//kontrollere atanan otomatik oluşturulmuş değişkenler:
CString adres;
UINT port;
CString alinan;
CString metin; Öncelikle hem sunucu hem de istemci için TSoket sınıfına, Receive işlevi ile aldığı paketleri hangi kontrole atacağını, ana dialogun OnInitDialog işlevinde şu şekilde bildirmeliyiz:
soket.alinanlariAt((CEdit*)Get DlgItem(IDC_ALINAN)) ; TSoket sınıfının alinanlaraAt işlevini daha sonra açıklayacağız. Sunucu tarafında Dinle düğmesine tıklandığında işletilecek kodu yazarak devam edelim:
//Bağlan/Kop
if(!dinlemede)
{
UpdateData();
soket.Create(port);
soket.Bind(port);
soket.Listen();
dinlemede = true;
SetWindowText("Sunucu - Aktif");
SetDlgItemText(IDC_DINLE,"Vazg eç");
}
else
{
soket.Close();
SetWindowText("Sunucu - Aktif değil");
bagli = false;
dinlemede = false;
SetDlgItemText(IDC_DINLE,"Dinl e");
} UpdateData işlevi port için belirtilen değeri kontrolden değişkene atmak için kullanıldı. Sunucu tarafı soketi oluşturmak için kullanılan Create işlevi, parametre olarak port numarasını alır. İstemci için soketin yaratılması sırasında parametre verilmez. Bind işlevi, uygulamamızın, aktif olarak çalışılan makineye -parametre olarak verilen porttan yapılan- bütün bağlantıları üstlenmesini sağlar. Yani Bind(x) işlevinin çağrılması ile x portunda yapılan bütün bağlantıları programımız ele alacaktır. Son olarak Listen işlevi ile dinleyeme, belirtilen porttan yapılacak bağlantıları izlemeye başlayabiliriz. Fakat uygulamamız dinlemede ise Close işlevi ile soketi durdurmuş ve kendisine yapılan referansları geçersiz kılmış oluyoruz. CAsyncSocket sınıfının yokedicisi bu işlevi bizim için otomatikman çağırır. Kodda geçen diğer işlevler ise dinlemede olup olmadığımız bilgisini görüntülemek için kullanılmaktadır. TSoket sınıfının tasarımına geçmeden önce, sınıfın sunucuya özel olan OnAccept işlevini de açıklayalım:
kabul = new TSoket();
kabul->alinanlariAt(alinan);
Accept(*kabul);
CSocket::OnAccept(nErrorCode); Sunucu soketlerde OnAccept işlevi sunucuya bir bağlantı yapıldığında otomatik olarak çağrılır. Gelen bağlantıyı kabul etmek için Accept işlevini çağırmalı ve parametre olarak da bu bağlantıyla yapılacak olan bütün transferleri kontrol edecek bir soket verilmelidir. Bu soketin kalıcı olması için TSoket sınıfına üye bir değişken olarak tanımlanmalıdır.
Gönder düğmesine tıklandığında yapılacaklar ise şu şekildedir:
UpdateData();
if(!metin.IsEmpty())
{
for(UCHAR a=0; a<256-metin.GetLength();a++ )metin+=�%�;
soket.kabul->Send(metin.GetBuffer(),metin. GetLength());
} Burda yapılan şey, öncelikle UpdateData işlevi ile gönderilecek metnin kontrolden, metin isimli değişkene aktarılması , ardından da metni 256 bayta % karakterleri ile tamamlanması ve Send işlevini metin ve boyutu ile çağrılmasıdır. Programımız temel bi uygulama olduğu için göndereceği bütün paketler 256 bayttan oluşmaktadır. Daha ileri uygulamalarda boyut, dinamik olarak belirlenmeli ve paketin boyut bilgisi önden gönderilmelidir. Send işlevi gönderme işlemini başaramadığı takdirde SOCKET_ERROR döndürecektir.
İstemci uygulamamızın Bağlan düğmesine tıklayınca belirtilen IP adresine bağlanması için yazılması gereken kod:
if(!bagli)
{
UpdateData();
soket.Create();
if(!soket.Connect(adres,port))
{
SetWindowText("İstemci - Bağlanamadı");
soket.Close();
soket.ShutDown();
}
else
{
SetWindowText("İstemci - Bağlı");
SetDlgItemText(IDC_BAGLAN,"Kop ");
bagli = true;
}
} İstemci soketin bağlanma girişimi port ve adres bilgisini gerektir. Başarısızlık durumunda Connect işlevi 0 döndürür. İstemciye spesifik gönderme işlevi:
UpdateData();
if(!metin.IsEmpty())
{
for(UCHAR a=0; a<256-metin.GetLength();a++ )metin+=�%�;
soket.Send(metin.GetBuffer(),m etin.GetLength());
} Sunucu uygulamanın gönderme işlevi, transferi kabul edilen bağlantı üzerinden yaparken, istemci bağlantı soketini kullanmaktadır. TSoket sınıfının istemci ve sunucu için ortak olan ve açıklanması gereken tek işlev; alma işini yapan OnReceive işlevidir:
alinan->GetWindowText(buf);
char *tampon=new char[256];
UINT okunan = Receive(tampon,256);
tampon[okunan]=0;
CString str;
str.Format("%sMuhatabım:\r\n %s",buf,tampon);
str.Remove(�%�);
str+="\r\n";
alinan->SetWindowText(str);
CSocket::OnReceive(nErrorCode) ; Öncelikle aldığımız mesajları alınan kutusunda görüntülemek için, alinanlaraAt işleviyle daha önceden adresi bildirilen alinan kontrolünün içeriğini Başlık kısmında üye değişken olarak tanımlanmış buf isimli değişkene atmalıyız. bunu GetWindowText işlevi yapmaktadır. Daha sonra alınan paketleri içine atmak için, tasarım gereği 256 baytlık olan bir karakter dizisi tanımladık. Receive işlevi, parametre olarak bu tampon belleğin adresini ve kaç karakter okunacağını verip çağrıldığında, alınan bayt sayısını döndürür. Alinan bayt sayisini alinan isimli değişkene atmamızın nedeni ise tampon karakter dizisinin içerdiği metnin sonuna, sonlandırıcı karakter eklemektir. Sonraki aşamada alınan mesajları uygun bir biçimde formatlama işlemi yapılmaktadır. Paket boyunu 256�ya tamamlamak için eklenmiş olan % karakterlerini alınan mesajdan attıktan sonra, mesajın son halini SetWindowText işlevi ile alinan isimli kontrole atarak paket alma işlemini tamamlamış oluyoruz.
Çok temel bir haberleşme uygulaması yapmış olduk. İleri düzey bir sohbet yazılımı isterseniz, çoklu kullanıcı desteği ve ekonomik bir paket yönetimi için kolları sıvamalısınız.
Şu an 1 kullanıcı var. (0 üye ve 1 konuk)

Paylaş