C C Cde Programlamaya Giriş

Sıfırdan bir sohbet uygulaması, ağ programlama temellerini geliştirmek için en iyi yollardan biridir. C soketleri kullanarak, her ağ etkileşimi üzerinde doğrudan kontrol sağlar, veri transferine bağlantı kurmaktan ve aynı anda birden çok kullanıcıyla iletişim kurmak için burada iletişim kurmak.Bu proje gerçek zamanlı olarak ağlayan bir müşteri programı oluşturmak için, sohbet sistemleri ve oyun sunucuları oluşturmak için bunları öğrenecektir.

C programlamanın temel bilgilerini (değişik, döngüler, fonksiyonlar, noktalılar) ve C derleyici (GCC) ve POSIX- uyumlu işletim sistemi (Linux, Mac, veya WSL Windows üzerinde) varsayıyoruz. Tüm kod örnekleri BSD soket API'yi takip eder, bu da Unix benzeri sistemlerde ağ programlama standardıdır.

C Soketleri Anlamak

Bir soket, bir ağ üzerinden iki makine arasındaki iletişim için son noktadır. POSIX soketi API'de bir soket tam bir dosya tanımlayıcı tarafından temsil edilir.En yaygın tipler UDP'yi kullanarak [FONT=STREAM) TCP ve TANITI:2dagram soketleri ).

soket API'sini oluşturan temel fonksiyonlar şunlardır:

  • [0]- Yeni bir soket oluşturun ve bir dosya tanımlayıcı döndürür.
  • [[Düzg: Belirli bir IP adresi ve port numarası ile bir soket.
  • [FONT:2] – Marks a soket as pasif, gelen bağlantıları kabul etmeye hazır.
  • [[DÜDÜDÜ: 1) Bir müşteri birbirine bağlı olana kadar Bloklar, o zaman bu özel müşteri için yeni bir soket döndürür.
  • [[Dört:) Bir müşteri tarafından bir sunucuya bağlantı kurmak için kullanılır.
  • [[DÜŞÜNÜŞÜNÜŞÜŞÜNÜŞÜŞÜŞÜŞÜŞÜN veÜŞÜŞÜŞÜŞÜŞÜŞÜŞÜŞÜŞÜŞÜNÜ: 6.

Tüm bu fonksiyonlar, [[Döneticileri]] ve IP adresi üzerinden sipariş edilen ve ağ üzerinden sipariş edilen veya kullanım alanlarının gerekli olduğunu anlamanız gerekir.(veya onların tersaneleri).

Daha derin bir referans için, danışma:0)Beej'in Network Programlama Kılavuzu[Dönetici: 1 ) - her işlevi açık örneklerle açıklayan klasik bir kaynak.

Ayarlayın Server

Sunucu sohbet uygulamasının merkezi merkezidir. Müşteri bağlantıları için dinliyor, herhangi bir müşteriden mesaj alıyor ve diğer bağlantılı müşterilere bu mesajları yayınlar. Sağlam bir sunucu inşa etmek her sistem çağrısını ve potansiyel hataları dikkatli bir şekilde kullanmak gerektirir.

Use the Use

İlk adım, [[DÜŞT:14 ile bir soket oluşturmaktır:

int server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd == -1) {
 perror("socket failed");
 exit(EXIT_FAILURE);
}
  • [FONT:0)AF INET - IPv4 için aileye hitap edin.
  • [FONT=0) SOSK STREAM[Dönetici: 1 ) – Güvenilir, iki yönlü, bağlantı tabanlı akış (TCP).
  • [FONT:0]0[Dönetici: 0 )[Dönetici: Protokol; SOSK STREAM kullanılırken TCP'ye varsayılanlar.

Her zaman geri dönüş değerini kontrol edin; bir soket oluşturma başarısızlığı mükemmel bir şekilde ele alınmalıdır.ŞUYUÇLAR: işlev, stderr'a hata açıklamasına baskı yapar.

Binding the Use

Sonraki, soketi yerel bir adrese ve limana bağlarız. Müşteriler bu adrese bağlanır.

struct sockaddr_in server_addr;
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY; // Bind to all local interfaces
server_addr.sin_port = htons(8080); // Port number

if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
 perror("bind failed");
 close(server_fd);
 exit(EXIT_FAILURE);
}

[FONT ANY[[DDRT:1], sunucunun ev sahibine atanan herhangi bir IP adresiyle bağlantılarını kabul etmesine izin verir. port numarası (8080 bu örnekte) iyi bilinen hizmetlerden kaçınmak için seçilir; 1024'ün üzerindeki değerler kullanıcı seviyesindeki programlar için tipiktir.

Bağlantılar için dinlemek

Bağlanmadan sonra, sunucu gelen bağlantıları dinlemelidir.TheETHFLT:19) soketi gelen bağlantı istekleri sıralayacak pasif bir modda yer alan yerlere çağırmalıdır.

if (listen(server_fd, 5) < 0) {
 perror("listen");
 close(server_fd);
 exit(EXIT_FAILURE);
}

İkinci argüman (5) gerilogdur - bekleyen bağlantıların kuyruklarının maksimum uzunluğu. Daha yüksek bir değer meşgul sunucular için kullanılabilir, ancak sistem üst limiti (oftenİLMİŞT:21) uygular.

Müşteri Bağlantılarını Kabul Etmek

Bir müşteriyi kabul etmek için, bir döngüde “Ücretsiz” diyoruz. Bu özel müşteri için yeni bir soket dosyası tanımlayıcı döndürür, ancak orijinal sunucu soketi dinlemeye devam eder.

struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len);
if (client_fd < 0) {
 perror("accept");
 // In a real server, handle the error without exiting
 continue;
}

char client_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
printf("New connection from %s:%d\n", client_ip, ntohs(client_addr.sin_port));

Her kabul edilen müşteri için, müşterinin IP ve port elde ediyoruz. Bu bilgi, giriş ve potansiyel erişim kontrolü için yararlıdır.Müşteri soketi daha sonra veri aktarımı için hazır.

Bir Müşteriden Veriye Hazırlayın

Bir müşteri bağlantılı olduğunda, sunucu mesajları okuyabilmeli ve yayınlayabilir.Bir seferde bir müşteriyi idare eden basit bir sunucu için, tipik desen müşteri soketi üzerinde kullanılmaktadır. ancak, birden fazla müşteriyi gerçekten desteklemek için.

Müşteriyi Geliştirmek

Müşteri daha basit: bir soket yaratır, sunucuya bağlanır ve sonra kullanıcının girişi okuduğu ve sunucuya gönderirken, aynı anda sunucudan gelen mesajlar alırken.

Yaratmak ve Bağlamak

İlk olarak, müşteri soketi sunucunun yaptığı gibi oluşturulur. Sonra sunucunun IP adresini ve portını kullanarak bağlanır:

int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0) {
 perror("socket creation failed");
 exit(EXIT_FAILURE);
}

struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(8080);

// Convert IP address from text to binary
if (inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr) <= 0) {
 perror("invalid address / address not supported");
 close(sock);
 exit(EXIT_FAILURE);
}

if (connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
 perror("connection failed");
 close(sock);
 exit(EXIT_FAILURE);
}

İşte bu yüzden, insan tarafından hazırlanabilir IP adresi dizesini ikili forma dönüştürür. Üretim kodu için, hostnameleri ile 03:27 ile çözebilirsiniz.

Exchange

Bağlantıdan sonra, müşteri, kullanıcı tarafından mesaj göndermeli ve sunucudan aynı anda mesaj almalı. Ortak bir yaklaşım iki thread kullanmak: stdin ve göndermeden bir tane, soketten almak için başka bir şey. Alternatif olarak, I/O veya [[ŞUYGÜNDÜRÜÇÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜŞÜNÜ

Aşağıdaki iskelet, gelen mesajları ele alan bir çocuk süreci oluşturmak için kullanır, ebeveynin yol açtığı sırada.

// Fork to handle send and receive separately
if (fork() == 0) {
 char buffer[1024];
 while (1) {
 memset(buffer, 0, sizeof(buffer));
 int n = recv(sock, buffer, sizeof(buffer) - 1, 0);
 if (n <= 0) {
 printf("Server disconnected\n");
 break;
 }
 printf("%s", buffer);
 }
 close(sock);
 exit(0);
} else {
 char message[1024];
 while (fgets(message, sizeof(message), stdin) != NULL) {
 send(sock, message, strlen(message), 0);
 memset(message, 0, sizeof(message));
 }
 close(sock);
}

Bu model çalışır, ancak gerçek bir uygulamada mesajı düzgün bir şekilde işlemek gerekir, çünkü TCP bir akış protokolüdir - mesajlar bölünmüş veya koncatene edilebilir. Basit bir çözüm, ilk önce mesajın uzunluğu gibi bir kasıtlı kullanmaktır.

Birden fazla Müşteriye Yönelik Tutulma

Sunucu bir zamanlar birçok müşteriyi yönetmelidir. İki popüler yöntem şöyledir:0)multi-threading) ve [[bir müşteri başına bir konu) ve [[I/O birdenxing) (tekrarlı) veya [[Dışmanlık) vardır.

Threads (pthread) kullanarak

Bu konuyla ilgili olarak, ana döngü bir müşteri kabul eder ve bu müşteriye adanmış yeni bir iplik yaratır. Bu, mesajları alır ve yayınlar. Bu kavramsal olarak basit ve onlarca müşteri için iyi çalışır, ancak işbaşı binlerce bağlantıyla ilgili bir konu olabilir.

void *handle_client(void *arg) {
 int client_fd = *(int*)arg;
 free(arg); // Free the heap‑allocated int
 char buffer[1024];
 while (1) {
 memset(buffer, 0, sizeof(buffer));
 int n = recv(client_fd, buffer, sizeof(buffer) - 1, 0);
 if (n <= 0) {
 // Client disconnected
 break;
 }
 // Broadcast to all other clients
 // (requires a mutex‑protected list of client fds)
 }
 close(client_fd);
 return NULL;
}

while (1) {
 int *pclient = malloc(sizeof(int));
 *pclient = accept(server_fd, ...);
 pthread_t tid;
 pthread_create(&tid, NULL, handle_client, pclient);
 pthread_detach(tid); // Automatically clean up on exit
}

Paylaşılan verileri (bağlantılı soketlerin listesi gibi) bir mutex ile korumayı unutmayın. Ayrıca, thread-güvenlik ile dikkat edin: #bu genellikle güvenlidir, ancak aynı sokete eş zamanlı olarak yazmalısınız.

Seçici()

[FONTNT: 9) Sistem çağrısı, bir dosya tanımlayıcılarını izlemek için tek bir ipe izin verir (server soketi + tüm istemci soketlerini izlemek için).Bu, hangi tanımlayıcının hazır olduğunu bulmak için setten kaçınır.

fd_set readfds;
int max_fd = server_fd;

while (1) {
 FD_ZERO(&readfds);
 FD_SET(server_fd, &readfds);
 for (int i = 0; i < client_count; i++) {
 FD_SET(clients[i], &readfds);
 if (clients[i] > max_fd) max_fd = clients[i];
 }

 int activity = select(max_fd + 1, &readfds, NULL, NULL, NULL);
 if (activity < 0) { perror("select"); break; }

 if (FD_ISSET(server_fd, &readfds)) {
 // Accept new client
 int new_socket = accept(server_fd, ...);
 clients[client_count++] = new_socket;
 }

 for (int i = 0; i < client_count; i++) {
 int sd = clients[i];
 if (FD_ISSET(sd, &readfds)) {
 char buffer[1024];
 int n = recv(sd, buffer, sizeof(buffer), 0);
 if (n <= 0) {
 // Remove client
 close(sd);
 clients[i] = clients[--client_count];
 i--;
 } else {
 // Broadcast buffer to all other clients
 }
 }
 }
}

One avantajı ofFL::40) portability, ancak tam ayrıntıları sağlar. (genellikle 1024). daha yüksek ölçeklenebilirlik için, kullanım [[LinuxT:42) veya [[FONTD|Linux-özel))|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

Basit bir Sohbet Protokolü Uygulamayın

Sohbet koherentini yapmak için, bir protokol olmadan, farklı müşterilerden gelen mesajlar tele geçebilir. minimumda, bir mesaj formatına karar verin. Basit bir yaklaşım: Her mesaj, sunucunun adı veya kimlikleri yayınlamadan önce tek bir çizgi sonlanır.

[Alice] Hello everyone!
[Bob] Hi Alice!

Bir müşteri bir mesaj gönderdiğinde, sunucu hattı okur (tekrarlı bir şekilde) ve sonra diğer tüm müşterilere gönderir, önceden bir ekle. Mesaj framing kritik. Güçlü bir yöntem mesajın uzunluğu içeren sabit bir başlık göndermektir, bu da kısmi okumalarla ilgili sorunları önler.

struct chat_message {
 uint32_t length; // in network byte order
 char data[1024]; // up to 1024 bytes
};

Sunucu tam olarak 4 tane uzunluk almak için okur, sonra veriler için birçok tane oyuncak olduğunu okur.Bu garanti mesajların bir parçada alındığı garanti.

Hata işleme ve En İyi Uygulamaları

Ağ programlama kenar vakaları ile doludur. Birkaç temel uygulama:

  • Her zaman geri dönüş değerini kontrol edin.(), [[Dint|Dint|s.), [[Dint|s.) ve son derece düşük soketler.
  • [FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=FONT=
  • Sunucu soketi üzerinde yeniden başlatma sırasında “Address zaten kullanımda” hataları önlemek için soket seçeneğine bakınız.
  • Kapalı bir sokete yazarken veya onları görmezden gelen sinyallerin (örneğin,) tarafından belirlenen sinyallerin (örneğin,) işlenmesi.
  • Ben / O veya zamanouts-blocking I/O veya zamanouts- ile 03.30.
  • Okumadan önce her zaman sıfırlar sol veri göndermediğinizden emin olmak için.
  • Temiz kaynaklar – yakın soketler ve ücretsiz ayrılmış hafıza – sızıntıları önlemek için.

Daha derin bir hata işleme ve ileri tekniklere atılması için, bağlantı için [[0)POSIX spesifikasyonunu okuyun () ve ilgili fonksiyonlar.

Uygulamayı genişletin

Çok değerli bir sohbetiniz olduğunda, daha kullanışlı hale getirmek için özellikleri ekleyebilirsiniz:

  • [[Kategori doğrulama[Dönetici:0) Kullanıcı doğrulama[Döneticiler bağlantıda bir kullanıcı sağlar; sunucu bunu doğrular ve depolar.
  • [FONT:0]Özel mesajlaşma[DÜT:1) - “ / kullanıcı adı metnine izin verin” sunucu rotalarının yalnızca hedef müşteriye yol açtığını komutlar.
  • [[Dönetici:0)Oda / kanal desteği[Döneticiler odaya katılabilirler; mesajlar sadece bir odada yayınlanır.
  • [FONT:0]File transfer[[DÜDÜT:1) - Ayrı bir veri kanalı uygulamak veya dosya metadata ve aynı soket üzerinde ham bytes göndermekle ikili dosyaları gönderin.
  • [FONT:0)[[[Döncükler) – AçıkSSL veya TLS’yi güvenli iletişim için kullanın.
  • [FONT=0]Graphical user arayüzü[[[Dönetici: 1 ) – Bir GUI müşterisi yaz ( GTK, Qt, hatta bir web ön uçu bir arka köprü ile birlikte.
  • [FONT:0)Persistent depolama[Dönetici: 1) – Daha sonra tekrar tekrar tekrar tekrar bir dosya veya veritabanına giriş.

Her genişleme değerli kavramlar öğretir: protokol tasarımı, koncurrency, güvenlik ve kullanıcı deneyimi. Şimdi inşa ettiğiniz temel bu geliştirmelerin herhangi birini destekleyecektir.

Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç Sonuç

C soketlerini kullanarak basit bir sohbet uygulaması oluşturmak, herhangi bir öğrenme ağ programlama için mükemmel bir el-on projesidir.Projeyi genişletip müşterileri, ipleri veya eksileri ile birden fazla bağlantıyı nasıl yöneteceğinizi öğrendiniz:60. API iletişim ağının sabitliğini koruyor ve uygulamaktadır.

Ek uygulama için basit bir HTTP sunucusu veya akran-topeer dosya paylaşım aracı uygulamayı düşünün. beceriler doğrudan transfer edilebilir. - her zaman belgeyi okuyun. [0] soket (7) erkek sayfası) sürekli arkadaşınızdır.