Dalam
 konsep sistem operasi kita mengenal adanya istilah Inter Process 
Communication (IPC), yaitu kemampuan dari suatu sistem operasi untuk 
membuat process yang berjalan diatasnya mampu “berkomunikasi” dengan 
process yang lainnya. Tentunya yang sebenarnya dimaksud komunikasi 
adalah process tersebut saling melakukan pertukaran data. Untuk sistem 
berbasis POSIX dalam melakukan IPC ada beberapa mekanisme yang dapat 
digunakan diantaranya adalah File, Signal, Socket, Message Queue, Pipe, 
Named Pipe, Semaphore, Shared Memory, Memory-mapped File. Di artikel ini
 akan membahas khusus mengenai socket. 
Pembuatan
 socket pada tutorial ini akan menggunakan protocol TCP dan IPv4. Dalam 
membuat aplikasi server terdapat 2 istilah yang sering ditemui yaitu 
iterative server dan concurrent server. Iterative server merupakan 
server yang hanya melayani satu client dalam satu waktu, sementara 
concurrent server adalah server yang dapat menangani banyak client 
secara parallel dalam satu waktu. Tutorial ini akan membahas pembuatan 
iterative server sederhana dimana client akan menerima input string dari
 user kemudian server akan menampilkan kembali isi string yang telah 
diinput oleh user.
Untuk membuat aplikasi server ada beberapa tahapan pembuatan yang harus dilakukan:
- Socket
 - Bind
 - Listen
 - Accept
 - Read/Write
 - Close
 
Sementara untuk aplikasi client yang harus dilakukan adalah membuat:
- Socket
 - Connect
 - Read/Write
 - Close
 
Pembuatan aplikasi diawali dengan membuat aplikasi servernya terlebih dahulu
- Socket.
 
int socket(int domain, int type, int protocol);
fungsi
 socket memiliki 3 parameter dan mengembalikan nilai integer, nilai 
integer inilah yang akan digunakan sebagai identifier file descriptor 
untuk melakukan IPC. fungsi ini juga membutuhkan tambahan library yaitu
#include <sys/types.h>
#include <sys/socket.h>
Parameter pertama kita gunakan AF_INET, ini menunjukan bahwa kita akan membuat aplikasi yang berbasis pada IPv4.
Parameter kedua kita akan menggunakan SOCK_STREAM,
 ini menunjukan bahwa kita akan membuat aplikasi yang menggunakan 
protocol TCP yaitu koneksi yang datanya ditransmisikan secara reliable 
atau bersifat connection oriented, koneksinya terbuat dulu baru data 
bisa dikirim dan diterima.
Parameter ketiga adalah kita menggunakan angka 0, kita gunakan 0 agar penerapan protocol yang digunakan dapat diserahkan pengaturannya pada sistem operasi. Contoh pemakaian:
int socketfd = socket(AF_INET, SOCK_STREAM, 0);
if(socketfd < 0)
{
perror("Error Creating Socket");
        return 1;
}
printf("Socket Created\n");
- Bind
 
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
fungsi
 bind() digunakan untuk mengikat antara informasi alamat IP yang 
digunakan untuk tujuan pengiriman data dengan file descriptor dari 
socket yang telah dibuat.
Parameter pertama yang digunakan adalah integer hasil dari pembuatan fungsi socket sebelumnya.
Parameter
 kedua berisi informasi yang akan diikat dengan file descriptor socket, 
informasi tersebut dibuat dalam satu struct yaitu struct sockaddr.
Parameter ketiga berisi ukuran dari struct sockaddr yang telah dibuat. Contoh pemakaian:
struct sockaddr_in serversockaddr; 
//informasi dirangkum pada struct sockaddr_in
int noport = 8888; //server akan menggunakan port 8888
memset(&serversockaddr, 0, sizeof(serversockaddr));
//sebelum di-assign value struct di set memory pada kondisi 0
serversockaddr.sin_family = AF_INET;
serversockaddr.sin_port = htons(noport);
serversockaddr.sin_addr.s_addr = htonl(INADDR_ANY); //menerima alamat IP address mana saja
if(bind(socketfd, (struct sockaddr*)&serversockaddr, sizeof(serversockaddr)) < 0)
{
        perror("Error Binding");
        return 1;
}
printf("Success Binding\n");
- Listen
 
    int listen(int sockfd, int backlog);
fungsi listen digunakan untuk membuat socket menjadi passive dan bersiap menerima connection dengan fungsi accept nantinya.
Parameter pertama yang digunakan adalah integer hasil dari pembuatan fungsi socket sebelumnya.
Parameter
 kedua diisi jumlah antrian yang bisa socket terima. Dalam tutorial ini 
yang digunakan adalah 1, karena kita hanya akan membuat iterative 
server. Contoh code:
if(listen(socketfd, 1) < 0)
{
  perror("Error listen");
  return 1;
}
printf("Success create listen\n");
- Accept
 
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
fungsi
 accept ini menerima dari antrian koneksi yang masuk pada server, 
membuat file descriptor baru sebagai media komunikasi yang akan 
digunakan pada pertukaran data nantinya dan merujuk pada socket yang 
telah dibuat sebelumnya. Socket yang baru dibuat ini tidak berada pada 
listening state, dan tidak mempengaruhi file descriptor socket 
sebelumnya.
Parameter pertama yang digunakan adalah integer hasil dari pembuatan fungsi socket sebelumnya.
Parameter kedua adalah adalah pointer ke struct sockaddr. Pointer ini berisi informasi mengenai peer yang terkoneksi dengan server.
Parameter
 ketiga berisi ukuran dari pointer sockaddr yang menampung informasi 
client, nilai ini harus berbentuk pointer structure dari ukuran struct addr. Dengan demikian kita sebelumnya harus membuat penampung hasilnya. Contoh:
struct sockaddr_in clientsockaddr;
int clientsockaddrsize = sizeof(clientsockaddr);
int acceptfd = accept(socketfd, (struct sockaddr*)&clientsockaddr, (socklen_t *)&clientsockaddrsize);
       if(acceptfd < 0)
       {
        perror("Error Accept");
       }
       printf("Accept from %s\n", inet_ntoa(clientsockaddr.sin_addr));
      //memunculkan IP client di server
- Read
 
    ssize_t read(int fd, void *buf, size_t count);
Setelah
 memiliki koneksi dengan client dan memiliki file descriptor yang 
digunakan sebagai medium komunikasi data oleh socket yang telah kita 
buat, kita bisa menggunakan fungsi read untuk mengambil isi data pada 
file descriptor tersebut.
Parameter pertama kita gunakan integer hasil dari fungsi accept() sebagai file descriptornya.
Parameter
 kedua kita gunakan variable array of char sebagai penerima data dikirim
 oleh client. Selain menggunakan char kita juga bisa menggunakan tipe 
data lain seperti integer dan struct.
    Parameter ketiga adalah ukuran dari variabel penerima di parameter kedua. Contoh:
           char message[100];
memset(message, 0, sizeof(message)); //isi variabel dikosongkan terlebih dahulu sebelum dipakai
       if(read(acceptfd, message, sizeof(message)) < 0)
       {
        perror("Error read from client");
       }     
       printf("Message from client %s\n", message);
    Sementara untuk fungsi write juga penggunaanya hampir sama dengan read
memset(message, 0, sizeof(message)); //isi variabel dikosongkan terlebih dahulu sebelum dipakai
         strcpy(message, "Thank You"); //mengisi variabel dengan string
       if(write(acceptfd, message, sizeof(message)) < 0)
       {
        perror("Error Write");
       }
- Close
 
    int close(int fd);
Fungsi
 close berfungsi untuk melepas lock terhadap file descriptor yang telah 
selesai dipakai agar bisa digunakan oleh proses lain.
Setelah
 membuat server saatnya kita membuat client. Untuk penjelasan pembuatan 
socket, read/write, dan close kurang lebih sama dengan yang pembuatan di
 server. Di tutorial ini akan dijelaskan mengenai connect.
- connect
 
    int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Fungsi connect bertugas untuk memanggil socket yang dibuat pada sockfd dengan informasi yang terdapat pada addr.
Parameter pertama menggunakan integer yang telah dibuat sebelumnya oleh fungsi socket.
Parameter kedua adalah structure yang berisi informasi server yang akan dituju. contoh:
struct sockaddr_in serversockaddr;
int noport = 8888;
memset(&serversockaddr, 0, sizeof(serversockaddr)); //dikosongkan dulu sebelum digunakan
serversockaddr.sin_family = AF_INET;
serversockaddr.sin_port = htons(noport);
if(inet_pton(AF_INET, "127.0.0.1", &serversockaddr.sin_addr) <= 0)
{
   perror("Error inet_pton()");
   return 1;
}
Untuk assign pada structure sin_addr saya menggunakan fungsi inet_pton, sedikit penjelasan mengenai inet_pton:
    int inet_pton(int af, const char *src, void *dst);
Fungsi ini bertugas untuk mengkonversi alamat IPv4 yang berbentuk string di pointer src menjadi network address structure pada pointer dst. Sementara pada parameter pertama akan digunakan AF_INET dikarenakan kita menggunakan IPv4.
Contoh penggunaan fungsi connect:
if(connect(socketfd, (struct sockaddr*)&serversockaddr, sizeof(serversockaddr)) < 0)
{
   perror("Error Connect\n");
   return 1;
}
Sisanya mengenai penggunaan read/write kurang lebih sama dengan yang ada di server.
Berikut ini code lengkap untuk server (server.c)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
int main()
{
    //socket
    //bind
    //listen
    //accept
    int socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if(socketfd < 0)
    {
        perror("Error Creating Socketn\n");
        return 1;
    }
    printf("Socket Created\n");
    struct sockaddr_in serversockaddr, clientsockaddr;
    int noport = 8888;
    memset(&serversockaddr, 0, sizeof(serversockaddr));
    serversockaddr.sin_family = AF_INET;
    serversockaddr.sin_port = htons(noport);
    serversockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    if(bind(socketfd, (struct sockaddr*)&serversockaddr, sizeof(serversockaddr)) < 0)
    {
        perror("Error Binding\n");
        return 1;
    }
    printf("Success Binding\n");
    if(listen(socketfd, 1) < 0)
    {
        perror("Error listen\n");
        return 1;
    }
    printf("Success create listen\n");
    int acceptfd = 0;
    char message[100];
    while(1)
    {
        int clientsockaddrsize = sizeof(clientsockaddr);
        acceptfd = accept(socketfd, (struct sockaddr*)&clientsockaddr, (socklen_t *)&clientsockaddrsize);
        if(acceptfd < 0)
        {
            perror("Error Accept\n");
        }
        printf("Accept from %s\n", inet_ntoa(clientsockaddr.sin_addr));
        do
        {
            memset(message, 0, sizeof(message));
            if(read(acceptfd, message, sizeof(message)) < 0)
            {
                perror("Error read from client\n");
            }
            printf("Message from client %s\n", message);
        }while(strcmp(message, "exit")!=0);
        if(strcmp(message, "exit") == 0)
        {
            printf("Server exit: sending message to client\n");
            memset(message, 0, sizeof(message));
            strcpy(message, "Thank You");
            if(write(acceptfd, message, sizeof(message)) < 0)
            {
                perror("Error Write\n");
            }
            close(acceptfd);
            close(socketfd);
            break;
        }
    }
    return 0;
}
Dan ini untuk client (client.c)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netdb.h>
int main(){
    int socketfd = 0;
    char message[100];
    struct sockaddr_in serversockaddr;
    struct hostent *host_serv;
    int noport = 8888;
    host_serv = gethostbyname("127.0.0.1");
    socketfd = socket(AF_INET, SOCK_STREAM, 0);
    if(socketfd < 0)
    {
        perror("Error Socket\n");
        return 1;
    }
    memset(&serversockaddr, 0, sizeof(serversockaddr));
    serversockaddr.sin_family = AF_INET;
    serversockaddr.sin_port = htons(noport);
    //serversockaddr.sin_addr.s_addr
    if(inet_pton(AF_INET, "127.0.0.1", &serversockaddr.sin_addr) <= 0)
    {
        perror("Error inet_pton()\n");
        return 1;
    }
    if(connect(socketfd, (struct sockaddr*)&serversockaddr, sizeof(serversockaddr)) < 0)
    {
        perror("Error Connect\n");
        return 1;
    }
    char c;
    do{
        memset(message, 0, sizeof(message));
        printf("Pesan: ");
        scanf("%[^\n]", message);
        scanf("%c", &c);
        if(write(socketfd, message, sizeof(message)) < 0)
        {
            perror("error write");
        }
    }while(strcmp(message, "exit") !=0);
    if(read(socketfd, message, sizeof(message)) < 0)
    {
        perror("Error read\n");
    }
    printf("Pesan dari server: %s\n", message);
    close(socketfd);
    return 0;
}
Cara compile menggunakan gcc
Contoh screent shot


No comments:
Post a Comment