Umut Tosun

I love low level stuffs and cyber security

C ile TCP Server/Client Uygulaması

01 Jun 2017 » Genel, Network

Anlaşılması için ilk önce bazı kavramların üzerinden geçmemiz gerekiyor.

Socket Nedir?

Socket’ler, Applicatin Layer ile Transport Layer arasında bağlantıyı kuran yazılımsal alanlardır. Socketler networkler içerisinde 2 farklı process arasında bağlantı kurabilmemizi sağlarlar. Örneğin tarayıcımızdan bir web sitesine girdiğimizde, arka tarafta uygun socket ler hazırlanır ve o web sitesiyle haberleşme sağlanır. Sadece web sitelerine girdiğimizde socket ler kullanılmaz, ayrıca kendi bilgisayarımızda çalışan iki ayrı uygulamada socket ler yardımı ile haberleşebilir.

Socket’ler aslında Unix File Descriptor‘ üne benzer. Socket oluştuğunda bize file descriptor verilir. Bu file decriptor yardımı ile haberleşmemizi sağlayabiliyoruz.

Binding

Server.c dosyasındaki kodlarda bind komutunu göreceksiniz. Peki nedir binding? Aslında çok basit.

Server ve client‘ın haberleşebilmesi için client ve server’ın birbirlerinin adresine ve portlarına ihtiyaçları vardır. Binding, sockete adres ve port numarasının atanmasına denmektedir. Peki neden “Server.c” de bind komutu kullanılırken, “Client.c” de connect komutu kullanılıyor? Aralarındaki fark nedir?

Bind komutu_ local adresi_ bağlamakta kullanılır, connect komutu remote adresi bağlamakta kullanılır.

Gerekli Header Dosyaları

Bildiğiniz gibi C dilinde include diye bir komut yer alıyor. Bu komut sayesinde ihtiyacımız olan kütüphaneyi projemize dahil edip, o kütüphane içerisindeki ihtiyacımız olan şeyi kullanabiliyoruz.

Socket oluşturmak için gerekli olan struct ları barındıran bazı header dosyalarımız var. Bunları include etmemiz gerekiyor:

  • sys/socket.h
  • netinet/in.h

Bu uygulamanın source code larını daha düzenli olması açısından github hesabımdan paylaşacağım. Buradan “TCP_server.c”, buradan da “TCP_client.c” source code larına ulaşabilirsiniz.

Kodları ve komutları açıklamaya çalışayım.

  • server_fd ve newsocket değişkenlerinin int tanımlanmasındaki sebep bir socket oluştuğunda bize geri döndürüğü değer file descriptor numarasıdır. Bu yüzden geri dönüş değerini “int” veri türünde saklamak gerekiyor.
  • sockaddr_in struct’ı bizim socket i istediğimiz gibi şekillendirmemizi sağlıyor. Örneğin sin_family değişkeni IPv4 la mı yada IPv6 le mi çalışacağımızı belirler.
  • socket() fonksiyonu bizim için socket oluşturur ve geri dönüş değeri olarak file descriptor numarası döndürür.
  • setsockopt() fonksiyonu opsiyonel bir fonksiyondur. “Address already in use” hatasını almamamız için kullanılır.
  • bind() fonksiyonu yukarıda bahsettiğim binding işlemini yapar.
  • listen() fonksiyonu, server ın maksimum kabul edeceği bağlantı sayısını belirler.
  • accept() fonksiyonu, bağlantı kuran clientı kabul edip, client la haberleşebilmesi için yeni bir socket açar.
  • read ve send, socket ten gelen veriyi okumamızı ve göndermemizi sağlar.
  • connect fonksiyonu bağlanmamızı sağlar.

Dikkat edilmesi gereken birkaç yer var.

  • Socket() fonksiyonuna IPv4 ile çalışmak istersek AF_INET, IPv6 ile çalışmak istersek AF_INET6 domainini vermemiz gerekiyor. TCP protokolünü kullanmak istersek SOCK_STREAM, UDP protokolünü kullanmak istersek SOCK_DGRAM.
  • Socket oluştururken başarılı bir şekilde oluşup oluşmadığını kontrol etmemiz gerekiyor, aksi halde sorunlara yol açabilir.
  • Her socket yalnızca bir portu dinleyebildiği için binding yaparken kullanılmayan portlara atama yapmalısınız.
  • accept fonksiyonu yeni bir socket oluşturur ve haberleşme yaparken bu socket üzerinden yapılması gerekir.
  • read komutu socketteki veriyi bizim belirlediğimiz değişkene atar(buffer). Bu yüzden buffer overflow saldırılarına dikkat etmemiz gerekiyor.
  • Eş zamanlı olarak birden fazla client la haberleşmek için multithreading kullanılmalıdır.

Bu yazıyı hazırlamamdaki amaç bir sonraki yazım olacak “C ile Paket Analizi” için önceden hazırlık olması.

Bir sonraki blog yazımda görüşmek üzere.