Alıntı (İbrahim):
>Direk QString olarak niye göndermiyoruz mesajı?
Soket düzeyine indiğimizde dizgi kavramının daha altındayız. Soketler resim gibi metin olmayan bilgi de gönderebildiklerinden baytlardan oluşan bilgi gönderirler. QString'in belgesinde ise Unicode dizgi olduğu yazılı. Dizgilerin kodlanmaları (UTF-8 mi, başka bir şey mi, vs.) gibi kavramlar bu düzeyde bulunmaz.
Alıntı:
>'Connection::sendGreetingMessage()' bu üye fonksiyon neden gerekli?
Tahmin ediyorum: Sanırım o sunucu ile istemci arasındaki bağlantı protokolünün parçası, kullanıcının gönderdiği sohbetin parçası değil. Bir anlamda, istemci sunucuya "merhaba, bağlanmak istiyorum" diyor; sunucu da ona "olur kardeş" diyor. :) İnsanların mesajları ise sendMessage() ile gönderiliyor.
Alıntı:
>Zaten connection.cpp dosyasında yazmadık mı o üye fonksiyonu?
Connection ile Client farklı sınıf türleri; üye işlevleri farklı. Olabildiğinde tek kod yazar ve paylaştırırız ama buradaki sendMessage'lar farklı. Örneğin, birisi değer döndürüyor ama öbürü döndürmüyor. Tasarlayanlar nedense öyle düşünmüşler. :)
Alıntı:
>bu üye fonksiyonun Türkçe karşılığı gelenBağlantı şeklinde fakat bu ne demek?
Sanırım istemci bağlandığında "şu soketten bağlantı geldi" anlamında çağrılıyor.
Alıntı:
>Ayrıca qintptr socketDescriptor parametresine neden gerek var?
Adına bakarak "soket belirticisi". Gelen bağlantı hakkında bilgi taşıyordur.
Alıntı:
>Umarım sorularımla sizi sıkmamışımdır.
Tabii ki değil ama ben bu örneğin soket programlanın kendisini öğrenmeye yarayacağını sanmıyorum. İşin içinde Qt ne yapıyor? Diyalog pencerelerine filan ne gerek var? :) Ben olsam soket programlamadan başka bir şey içermeyen bir örneğe bakardım:
-
Sunucu beklemeye başlasın
-
İstemci bağlansın
-
İstemci "merhaba" desin
-
Sunucu da "merhaba" desin
-
Sonlansınlar
Eğer bu kadar alt düzeyi bilmek gerekmiyorsa (ben olsam uygulamada o kadar alta inmem ve std.socket gibi üst düzey bir modülü kullanırım) o zaman üst düzey modüllere bakmak gerek.
Alıntı:
>Malum soket programlamayı net anlatan bir kaynak bulamadım.
Daha önce yazdığım soket örneğindeki ilgisiz kodları çıkarttım. (Linux ortamında çalışıyor.) main çok basit. Geri kalanında ya Sunucu iş görüyor ya da İstemci.
Sunucu'nun kurucusu bind ve listen yapıyor. Böylece soket hazır hale geliyor. Sonra hizmetEt içinde 'accept' ile bağlantı bekliyor.
İstemci'nin kurucusu ise 'connect' ile bağlanıyor ve send ile mesaj gönderiyor. İstemci "bitti" mesajı gönderdiğinde bağlantıyı kesiyorlar.
import std.stdio;
import std.socket;
import std.getopt;
import std.string;
/* 'Unix domain socket'ler dosya sisteminde dosya olarak
* beliriyorlarmış. Anladığım kadarıyla kullanıcının bu dosyayla doğrudan bir
* işi olmuyor.
*
* Dosya isminin ilk karakterinin '\0' olması da en azından dosya isim
* çakışmalarını önlüyormuş. Hatta, böyle isimdeki dosyaların remove() ile
* silinmeleri de gerekmiyormuş.
*/
enum soketİsmi = "\0/tmp/deneme_soketi";
int main(string[] parametreler)
{
// Sunucu rolünde mi olalım istemci mi
enum Rol { sunucu, istemci }
Rol rol;
try {
// Programın ismi dahil, iki parametre olmalı
enforce(parametreler.length == 2);
// Rol olamayacak değer geldiğinde hata atar
getopt(parametreler, "rol", &rol);
} catch {
stderr.writefln("Kullanım:\n %s --rol={sunucu|istemci}",
parametreler[0]);
return 1;
}
final switch (rol) {
case Rol.sunucu: sunucuOl(soketİsmi); break;
case Rol.istemci: istemciOl(soketİsmi); break;
}
return 0;
}
void sunucuOl(string soketİsmi)
{
auto sunucu = Sunucu(soketİsmi);
sunucu.hizmetEt();
}
void istemciOl(string soketİsmi)
{
auto istemci = İstemci(soketİsmi);
istemci.iste();
}
struct Sunucu
{
Socket dinleyici;
this(string soketİsmi)
{
// Önce bağlantıları karşılayacak olan soketi hazırlıyoruz
dinleyici = new Socket(AddressFamily.UNIX, SocketType.STREAM);
auto adres = new UnixAddress(soketİsmi);
dinleyici.bind(adres);
dinleyici.listen(1);
}
~this()
{
dinleyici.shutdown(SocketShutdown.BOTH);
dinleyici.close();
destroy(dinleyici);
// Dosyanın silinmesinin gerekip gerekmediğinden aslında emin değilim:
remove(soketİsmi.toStringz);
}
/* Sonsuz döngüde istemci bekler ve ona yanıt verir. */
void hizmetEt()
{
while (true) {
// İstemci bekliyoruz
writeln("Bekliyorum...");
// Şimdi o sokette bağlantı kabul ediyoruz.
Socket istemci = dinleyici.accept();
// Bu istemciyle işimiz bittiğinde gereken temizlik işlemi:
scope (exit) destroy(istemci);
writeln("Bir istemci bağlandı.");
// İstemci gidene kadar istemci döngüsündeyiz
while (istemci.isAlive) {
string istek = istemci.tekParçaOlarakOku();
string yanıt = format("(%s)", istek);
istemci.send(yanıt);
if (istek == "bitti") {
break;
}
}
writeln("Bu istemciyle işim bitti.");
}
}
}
/* Socket.receive'in aksine, okunan parçaları birleştirir ve tek string olarak
* döndürür. */
string tekParçaOlarakOku(Socket soket)
{
string okunan;
// Okunan veriyi parça parça bu belleğe alacağız
ubyte[1000] parça;
bool bütünVeriOkundu_mu = false;
while (!bütünVeriOkundu_mu) {
const adet = soket.receive(parça);
if (adet == Socket.ERROR) {
stderr.writeln("OKUMA HATASI");
break;
}
okunan ~= parça[0..adet];
bütünVeriOkundu_mu = (adet < parça.length);
}
return okunan;
}
struct İstemci
{
Socket soket;
this(string soketİsmi)
{
soket = new Socket(AddressFamily.UNIX, SocketType.STREAM);
soket.connect(new UnixAddress(soketİsmi));
}
~this()
{
soket.shutdown(SocketShutdown.BOTH);
soket.close();
destroy(soket);
}
void iste()
{
while (true) {
write("Sunucuya gönderilecek mesajı yazın: ");
const mesaj = chomp(readln());
const adet = soket.send(mesaj);
writefln("%s bayt gönderdim", adet);
string yanıt = soket.tekParçaOlarakOku();
writefln("Yanıt aldım: %s", yanıt);
if (mesaj == "bitti") {
break;
}
}
}
}
Ali
--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]