Tuesday, September 2, 2014

Membuat Concurrent Server Dengan Socket Dalam Bahasa C

Pada artikel sebelumnya telah dijelaskan cara membuat aplikasi client server menggunakan socket dalam bahasa C dan sebagai contoh yang dibuat adalah iterative server, server yang hanya bisa menangani satu client dalam satu kesempatan waktu. Dalam tutorial ini akan dijelaskan untuk membuat concurrent server yaitu server yang mampu menangani banyak client dalam satu waktu.
Hampir sama seperti tutorial sebelumnya kita harus memenuhi langkah-langkah dasar pembuatan aplikasi server yaitu:
  1. Socket
  2. Bind
  3. Listen
  4. Accept
  5. Read/Write
  6. Close
Namun untuk concurrent server, ketika aplikasi melakukan listen untuk client baru kemudian menerima koneksi (accept) server harus mengalokasikan koneksi tersebut dalam sebuah thread.
Thread merupakan instruksi program dalam sebuah proses yang bertugas untuk mengeksekusi beberapa perintah secara independent oleh scheduler sistem operasi (http://en.wikipedia.org/wiki/Thread_%28computing%29).

Cara membuat thread dalam bahasa C menggunakan fungsi
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
dan membutuhkan library #include <pthread.h>

Parameter pertama digunakan sebagai pointer untuk merujuk setiap thread yang dibuat.
Parameter kedua digunakan untuk mengirim fungsi yang akan berisi proses yang digunakan dalam memproses koneksi yang masuk nantinya.
Parameter ketiga digunakan untuk mengirim data yang dibutuhkan oleh fungsi di parameter kedua.

Contoh penggunaannya sebagai berikut:
Sebelumnya kita harus membuat fungsi yang akan memproses koneksi yang masuk
void *thread_proc(void *arg)
{
 struct DataThread data = *((struct DataThread*)arg);
 char message[255];
 printf("Client baru masuk dari %s\n", inet_ntoa(data.sockaddr_client.sin_addr));
 while(1)
 {
    memset(&message, 0, sizeof(message));
    read(data.clientsockfd, message, sizeof(message));
    printf("pesan dari %s: %s\n", inet_ntoa(data.sockaddr_client.sin_addr), message);
    if(strcmp(message, "exit")==0)
    {
     memset(&message, 0, sizeof(message));
     strcpy(message, "thank you");
     write(data.clientsockfd, message, sizeof(message));
     break;
    }
    else if(strcmp(message, "hai")==0)
    {
     memset(&message, 0, sizeof(message));
     strcpy(message, "hai juga");
     write(data.clientsockfd, message, sizeof(message));
    }else
    {
     memset(&message, 0, sizeof(message));
     strcpy(message, "wkwkwk");
     write(data.clientsockfd, message, sizeof(message));
    }
 }
 close(data.clientsockfd);
}
di dalam fungsi tersebut terdapat struct DataThread, structure tersebut dibuat sendiri bukan dari bawaan library.
struct DataThread
{
 int clientsockfd;
 struct sockaddr_in sockaddr_client;
};

sementara untuk penggunaanya dalam body fungsi main pembuatan fungsi thread dilakukan setelah server melakukan fungsi accept.
int sizeofsockaddr_client, clientsockfd;
struct sockaddr_in sockaddr_client;
pthread_t thread_id;
struct DataThread data;

sizeofsockaddr_client = sizeof(sockaddr_client);
clientsockfd = accept(sockfd, (struct sockaddr *)&sockaddr_client, &sizeofsockaddr_client); //proses accept seperti biasa
if(clientsockfd < 0)
{
 perror("error accepting");
}
//memasukkan data yang dibutuhkan oleh fungsi dalam thread nantinya
data.clientsockfd = clientsockfd;
data.sockaddr_client = sockaddr_client;
thread_result = pthread_create(&thread_id, NULL, thread_proc, &data);
if(thread_result < 0)
{
 perror("error create thread");
}
//thread di detach dengan tujuan ketika setelah selesai prosesnya, resource yang dipakai bisa dikembalikan lg pada sistem operasi
pthread_detach(thread_id);

Berikut full code nya (server.c)
#include <stdio.h>
#include <netinet/in.h>
#include <string.h>
#include <pthread.h>
struct DataThread
{
int clientsockfd;
struct sockaddr_in sockaddr_client;
};
void *thread_proc(void *arg)
{
struct DataThread data = *((struct DataThread*)arg);
char message[255];
printf("Client baru masuk dari %s\n", inet_ntoa(data.sockaddr_client.sin_addr));
while(1)
{
    memset(&message, 0, sizeof(message));
    read(data.clientsockfd, message, sizeof(message));
    printf("pesan dari %s: %s\n", inet_ntoa(data.sockaddr_client.sin_addr), message);
    if(strcmp(message, "exit")==0)
    {
    memset(&message, 0, sizeof(message));
    strcpy(message, "thank you");
    write(data.clientsockfd, message, sizeof(message));
    break;
    }
    else if(strcmp(message, "hai")==0)
    {
    memset(&message, 0, sizeof(message));
    strcpy(message, "hai juga");
    write(data.clientsockfd, message, sizeof(message));
    }else
    {
    memset(&message, 0, sizeof(message));
    strcpy(message, "just another message from server \\m/");
    write(data.clientsockfd, message, sizeof(message));
    }
}
close(data.clientsockfd);
}
int main(int argc, char **argv)
{
if(argc < 2)
{
    printf("invalid argument\n");
    return 1;
}
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
    perror("error create socket");
    return 1;
}
/*
 bind(int sockfd, const struct sockaddr *addr,
       socklen_t addrlen);
 */
struct sockaddr_in sockaddr_server, sockaddr_client;
memset(&sockaddr_server, '0', sizeof(sockaddr_server));
sockaddr_server.sin_family = AF_INET;
sockaddr_server.sin_port = htons(atoi(argv[1]));
sockaddr_server.sin_addr.s_addr = htonl(INADDR_ANY);
int bindid = bind(sockfd, (struct sockaddr *)&sockaddr_server, sizeof(sockaddr_server));
if(bindid < 0)
{
    perror("Error binding\n");
    return 1;
}
//   socket
//   bind
//   listen
//   accept
//   read write
int listenid = listen(sockfd, 10);
if(listenid < 0)
{
    perror("error listen");
    return 1;
}
int clientsockfd, sizeofsockaddr_client, thread_result;
pthread_t thread_id;
struct DataThread data;
printf("waiting for new client\n");
while(1)
{
    sizeofsockaddr_client = sizeof(sockaddr_client);
    clientsockfd = accept(sockfd, (struct sockaddr *)&sockaddr_client, &sizeofsockaddr_client);
    if(clientsockfd < 0)
    {
    perror("error accepting");
    }
  
    data.clientsockfd = clientsockfd;
    data.sockaddr_client = sockaddr_client;
    thread_result = pthread_create(&thread_id, NULL, thread_proc, &data);
    if(thread_result < 0)
    {
    perror("error create thread");
    }
    pthread_detach(thread_id);
}
close(sockfd);
return 0;
}

Full code untuk client (client.c)
#include <stdio.h>
#include <netinet/in.h>
#include <pthread.h>
#include <string.h>

int main(int argc, char **argv)
{
 //socket
 int sockfd = socket(AF_INET, SOCK_STREAM, 0);
 if(sockfd < 0)
 {
    perror("Error socket");
 }
 struct sockaddr_in serversockaddr;
 memset(&serversockaddr, 0, sizeof(serversockaddr));
 serversockaddr.sin_family = AF_INET;
 serversockaddr.sin_port = htons(atoi(argv[2]));
 inet_pton(AF_INET, argv[1], &serversockaddr.sin_addr);
 if(connect(sockfd, (struct sockaddr *)&serversockaddr, sizeof(serversockaddr)) < 0)
 {
    perror("connect");
 }
 char message[255];
 char c;
 do
 {
    memset(message, '0', sizeof(message));
    printf("Pesan: ");
    scanf("%[^\n]", message);
    scanf("%c", &c);
    if(write(sockfd, message, sizeof(message)) < 0)
    {
     perror("error write");
    }
    if(read(sockfd, message, sizeof(message)) < 0)
    {
     perror("error read");
    }
   
    printf("Pesan dari server: %s\n", message);
 }while(strcmp(message, "exit")!=0);
 memset(message, '0', sizeof(message));
 if(read(sockfd, message, sizeof(message)) < 0)
 {
    perror("error read");
 }
 printf("Pesan dari server: %s\n", message);
 close(sockfd);
 return 0;
}

untuk compile server.c
compile_server.png
untuk compile client.c
compile_client.png

screenshot contoh program
server.c -> cara menjalankan ./server <nomor port>
server.png

screenshot client.c -> cara menjalankan ./client <ip komputer server> <nomor port>
client1.png
client2.png

No comments:

Post a Comment