Computer-Science

select

1. Usage

A server program can handle multiple sockets using select().

select(maxfd, &rset, NULL, NULL, NULL);

2. bit vector handling

fd_set  rset;   // rset is a bit vector of size 1024 bit
FD_ZERO(&rset); // initialize rset with 0
FD_SET(3, &rset); // set bit 3 of rset to 1
FD_CLR(3, &rset); // reset bit 3 of rset to 0
FD_ISSET(3, &rset); / return true if bit 3 of rset is 1

3. select() system call

s2=accept(s1, ...);   // waiting on socket s1 only (for SYN packet)
read(s2, ...);         // waiting on socket s2 only (for data packet)

==>

select(..., &rset, ......);  // waiting on all sockets marked in rset
for each socket "x" that has packet arrived(SYN, or data packet)
    if it is SYN packet
        s2 = accept(x, .....);    // handle SYN packet
    or if it is data packet
        read(x, .....);           // read the data and handle appropriately
        .........
       FD_SET(3, &rset);
       FD_SET(5, &rset);
       FD_SET(21, &rset);
       select(maxfd, &rset, NULL, NULL, NULL);

The server will wait for client packets from either one or multiple of socket 3, 5, and 21 in select(). When packets from one or multiple of these sockets arrive, the select will return with active sockets (ones with packets arrived) marked 1 and non-active sockets (ones with no packet) reset to 0 in rset.

Therefore, after select(), we can check which sockets had packets.

To use select():

   set pset; // step 1
   for(;;){
     copy pset to rset;  // step 2
     Call select();       // step 3
     //The active sockets (those where a packet has arrived) will be marked as 1 in
     // rset; non-active sockets (where no packet has arrived) will be reset to 0.
     for(all marked sockets){ // step 4
        read the arrived data and handle them appropriately.
     }
   }

Without select(), it is hard to read from multiple sockets at the same time.

        read(3, buf, n);       // reading packets from socket 3
        read(5, buf, n);       // reading from socket 5
        read(21, buf, n);      // reading from socket 21

We have to read each socket one by one, not at the same time.

4. Code example

   fd_set  rset, pset;   // rset used in select(). pset remembers the sockets to monitor
   FD_ZERO(&rset);
   FD_ZERO(&pset);   // all bits are zerorized in the beginning

   // step 1. prepare pset
   FD_SET(3, &pset);
   FD_SET(5, &pset);
   FD_SET(21, &pset); // we want to monitor socket 3, 5, and 21
   for(;;){
        // step 2. copy pset to rset
        rset = pset;    // pset remembers target socket numbers.
        // setp 3. call select()
        select(maxfd, &rset, NULL, NULL, NULL);
        // step 4. now we check which sockets had packets
        for(x=0; x<maxfd; x++){ // check each socket
           if (FD_ISSET(x, &rset)){ // this socket has some packet
               ........handle this packet .........
           }
        }
   }

5. ping-pong-pang-pung Example

A server communicates with multiple clients. When connected, the client and the server exchange messages as follows:

cli=>serv: ping
serv=>cli : pong
cli=>serv: pang
serv=>cli: pung

client:

    s = socket(...);
    connect(s, server, ...);
    write(s, "ping", 4);
    read(s, buf, n);  // read "pong"
    write(s, "pang", 4);
    read(s, buf, n); // read "pung"
    close(s);

server:

    s1 = socket(...);  // to accept connection
    ......bind, listen, .......
    FD_SET(s1, &pset);  // step 1. we have to monitor connection request packet

    for(;;){
       rset = pset;                               // step 2
       select(maxfd, &rset, NULL, NULL, NULL); // step 3
       for(x=0; x<maxfd; x++){                 // step 4
          if (FD_ISSET(x, &rset)){ // found active socket
              if (x == s1){  // connection request on s1. new client has arrived.
                 s2 = accept(s1, .........); // create a new socket, s2, for this client
                 FD_SET(s2, &pset);   // and remember s2 in pset for future monitoring
              } else{ // must be a data packet from a client. follow the protocol.
                 handle_protocol(x, &pset);
               }
             }
         }
      }
................
void handle_protocol(int x, fd_set * pset){
   ...........
   y=read(x, buf, n);  // read data from socket x
   buf[y]=0;         // make it a string
   if (strcmp(buf, "ping")==0)
      write(x, "pong", 4);
   else if (strcmp(buf, "pang")==0){
      write(x, "pung", 4);
      write(x, "protocol ended ok", 17);
      close(x);
      FD_CLR(x, &pset);  // don't monitor this socket anymore
   }
}

4. Example

1) Copy cliping.c and servping.c into your directory, modify IP and port number appropriately, and compile them. Run the server first and run client 3 times each in different window. Check if the server can handle multiple clients at the same time.

$ cp ../../linuxer1/cliping.c  .
$ cp ../../linuxer1/servping.c  .

2) The server in Prob 1) cannot give error message to clients even when the client doesnโ€™t follow the protocol. Run server and run client and let the client send โ€œpangโ€ instead of โ€œpingโ€ as the first message. The server gives โ€œpungโ€ instead of error message as below.

client์—์„œ ์ฒซ๋ฒˆ์งธ ์ž…๋ ฅ์— โ€˜pingโ€™์ด ์•„๋‹ˆ๋ผ โ€˜pangโ€™์„ ์ž…๋ ฅํ•จ์œผ๋กœ์จ ํ”„๋กœํ† ์ฝœ์„ ์ง€ํ‚ค์ง€ ์•Š์•˜์„ ๋•Œ, server๊ฐ€ ์˜ค๋ฅ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  โ€˜pongโ€™์„ ๋ฐ˜ํ™˜ํ•˜์˜€๋‹ค.

Modify servping.c so that it can send error message when the client sends something other than โ€œpingโ€ for the first message. But make sure the server still sends โ€œpungโ€ when the client sends โ€œpangโ€ as the second message.

state ๋ฐฐ์—ด์„ ์„ ์–ธํ•ด ๊ฐ ์†Œ์ผ“๋ณ„ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜์˜€๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์†Œ์ผ“์ด ์ƒ์„ฑ๋  ๋•Œ, ์ƒํƒœ๋ฅผ 1๋กœ ์ดˆ๊ธฐํ™”ํ•ด์ฃผ์—ˆ๋‹ค.

    ......
    int maxfd = 50;   // just monitor max 50 sockets
    int state[maxfd]; // ex2 : ๊ฐ ์†Œ์ผ“๋ณ„ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด ์„ ์–ธ

    ......

    // step 1. monitor conn req packet
    FD_SET(s1, &pset);
    for (i = 0; i < 20; i++)
    {
        ......
        for (x = 0; x < maxfd; x++)
        {
            if (FD_ISSET(x, &rset))
            {
                if (x == s1)
                {
                    s2 = accept(s1, (struct sockaddr *)&cli_addr, &xx);
                    state[s2] = 1; // ex2 : ํ•ด๋‹น ์†Œ์ผ“์˜ ์ƒํƒœ๋ฅผ 1๋กœ ์ดˆ๊ธฐํ™”
                    ......
                }
                ......
            }
        }
    }

state์˜ ์ƒํƒœ์— ๋”ฐ๋ผ ์–ด๋–ค ํ”„๋กœํ† ์ฝœ์„ ์‹คํ–‰์‹œํ‚ฌ์ง€๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ณ , ๊ฐ๊ฐ์˜ ์ƒํ™ฉ์„ ํ•จ์ˆ˜๋กœ ๊ตฌํ˜„ํ•˜์˜€๋‹ค. (handle_state_1(), handle_state_2()) ๊ทธ๋ฆฌ๊ณ  handle_protocol() ํ•จ์ˆ˜๋ฅผ ์ˆ˜์ •ํ•ด์ฃผ์—ˆ๋‹ค.

void handle_state_1(int x, fd_set *pset, char *buf, int state[])
{
    if (strcmp(buf, "ping") == 0)
    { // if it is a ping
        printf("received ping from socket %d\n", x);
        write(x, "pong", 4); // send pong
        printf("sent pong to socket %d\n", x);
    }
    else
    {
        printf("received other message from socket %d\n", x);
        write(x, "Error! You should enter \"ping\". Try again!", 44); // send pong
        printf("sent error message to socket %d\n", x);
    }
    state[x] = 2; // ex 2 : ์ƒํƒœ ๊ฐ’์„ 2๋กœ ์„ค์ •
}

void handle_state_2(int x, fd_set *pset, char *buf, int state[])
{

    if (strcmp(buf, "pang") == 0)
    { // it it is a pang
        printf("received pang from socket %d\n", x);
        write(x, "pung", 4); // send pung
        printf("sent pung to socket %d\n", x);
        close(x);        // and stop the protocol
        FD_CLR(x, pset); // no more monitoring on this socket
    }

    else
    {
        printf("received other message from socket %d\n", x);
        write(x, "Error! You should enter \"pang\". Try again!", 44); // send pong
        printf("sent error message to socket %d\n", x);
    }
}

void handle_protocol(int x, fd_set *pset, int state[])
{
    // we have a data packet in socket x. do protocol
    int y;
    char buf[50];
    y = read(x, buf, 50); // read data
    buf[y] = 0;           // make it a string
    if (state[x] == 1)    // ex2 : ์ƒํƒœ๊ฐ€ 1์ธ ๊ฒฝ์šฐ
        handle_state_1(x, pset, buf, state);
    else if (state[x] == 2) // ex2 : ์ƒํƒœ๊ฐ€ 2์ธ ๊ฒฝ์šฐ
        handle_state_2(x, pset, buf, state);
}

์‹คํ–‰๊ฒฐ๊ณผ๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.

์˜ค๋ฅธ์ชฝ์˜ ๋‘ client๋ฅผ ์ฃผ๋ชฉํ•˜์ž. ์œ„์ชฝ์˜ client์—์„œ ๋จผ์ € โ€œpingโ€ ๋Œ€์‹  โ€œhelloโ€๋ฅผ ์ž…๋ ฅํ–ˆ์„ ๋•Œ error message๋ฅผ ๋Œ๋ ค๋ฐ›์•˜๋‹ค. ๊ทธ ํ›„, ์ •์ƒ์ ์œผ๋กœ โ€œpangโ€์„ ์ž…๋ ฅํ–ˆ์„ ๋•Œ๋Š” ์ •์ƒ์ ์œผ๋กœ โ€œpungโ€์„ ๋Œ๋ ค๋ฐ›์•˜๋‹ค.

์•„๋ž˜์ชฝ์˜ client์—์„œ๋Š” ๋จผ์ € โ€œpingโ€์„ ์•Œ๋งž๊ฒŒ ์ž…๋ ฅํ–ˆ์„ ๋•Œ, ์ •์ƒ์ ์œผ๋กœ โ€œpongโ€์„ ๋Œ๋ ค๋ฐ›์•˜๋‹ค. ๊ทธ ํ›„, โ€œpangโ€ ๋Œ€์‹  โ€œhiโ€๋ฅผ ์ž…๋ ฅํ–ˆ์„ ๋•Œ error message๋ฅผ ๋Œ๋ ค๋ฐ›์•˜๋‹ค.

2-1) Modify the server such that it disconnects the connection if the client doesnโ€™t follow the protocol. You need to keep track of the state of each client to do this. (The client will act strange when the server disconnects it. You donโ€™t have to change the client code for this since we donโ€™t care about what happens to the client when it does not follow the protocol.)

client๊ฐ€ protocol์„ ์ค€์ˆ˜ํ•˜์ง€ ์•Š์•˜์„ ๊ฒฝ์šฐ ์—ฐ๊ฒฐ์„ closeํ•˜์˜€๋‹ค.

       int state[50]; // state of each client (state of each client socket)
                     // 1: the server is waiting for "ping" from this client
                     // 2: the server is waiting for "pang" from this client
       ...........
       for(x=0;x<maxfd;x++){ // check all fd
          if (FD_ISSET(x, &rset)){ // if we have packet in socket x
             if (x==s1){ // if x is the connection accepting socket, we have a new client
                        // and we must have the connection request packet(SYN) at x
                 s2=accept(s1, ........); // now s2 is this client's socket
                 state[s2]=1;  // init the state of this client.
                               // the server is expecting "ping" from this client
                 .............
             }else{ // we must have the data packet at socket x
                 handle_protocol(x, &pset, state);
             }
       ................

void handle_protocol(int x, fd_set *pset, int state[])
{
    // we have data packet in socket x. state[x] shows the state of socket x.
    // handle the protocol.
    int y;
    char buf[50];
    y = read(x, buf, 50); // read the data
    buf[y] = 0;           // make it a string
    if (state[x] == 1)
    { // the state of this socket is 1 meaning we are
      // expecting "ping" from this socket
        handle_state_1(x, pset, buf, state);
    }
    else if (state[x] == 2)
    { // expecting "pang"
        handle_state_2(x, pset, buf, state);
    }
}
void handle_state_1(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 1. Expecting "ping" in buf. if we have ping, send "pong" and
    // just update state[x]=2; otherwise send error message and disconnect the connection
    if (strcmp(buf, "ping") == 0)
    {                        // yes we have "ping"
        write(x, "pong", 4); // send pong to this client
        state[x] = 2;        // now we are waiting for "pang" from this client
    }
    else
    {                                   // no we didn't receive "ping"
        write(x, "protocol error", 14); // send err message to the client
        close(x);                       // end the connection
        FD_CLR(x, pset);                // remove from the watch list.
                                        // we don't monitor socket x any more
    }
}
void handle_state_2(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 2. we are expecting "pang" in buf. If we have "pang", send "pung"
    // and close the connection. If we didnโ€™t receive โ€œpangโ€, send โ€œprotocol errorโ€ to the
    // client and disconnect.
    ....................
}

3) Modify the protocol such that the server expects a final โ€œpingโ€ again from the client. Make sure the server give error message and disconnect the client if the client doesnโ€™t follow the protocol.

    cli=>serv: ping
    serv=>cli: pong
    cli=>serv: pang
    serv=>cli: pung
    cli=>serv: ping (final ping)
    serv=>cli: protocol completed

Ex2์ฒ˜๋Ÿผ handle_state_3 ํ•จ์ˆ˜๋ฅผ ์ถ”๊ฐ€๋กœ ์ •์˜ํ•˜์—ฌ 3๋ฒˆ์งธ ์ƒํƒœ๋ฅผ ๋‹ค๋ค„์ฃผ์—ˆ๋‹ค.

servping.c

cliping.c

result

4) Modify the protocol such that the server relays a message from a client to all other clients after the โ€œping-pong-pang-pungโ€ sequence is completed. The clients should fork itself after the โ€œping-pong-pang-pungโ€ sequence so that the parent part keeps reading while the child part keeps writing. The server does not fork since it doesnโ€™t do the chatting by itself; it just relays a message from one client to all other clients. The server checks state[] array to see which socket is ready to receive message.

cli at socket 3 => serv: ping
    serv => cli at socket 3 : pong
cli at socket 3 => serv: pang
    serv => cli at socket 3 : pung. Protocol completed. Start chatting.

cli at socket 4 => serv: ping
     serv => cli at socket 4 : pong
cli at socket 4 => serv: pang
     serv => cli at socket 4 : pung. Protocol completed. Start chatting.

cli at socket 5 => serv: ping
     serv => cli at socket 5 : pong
cli at socket 5 => serv: pang
     serv => cli at socket 5 : pung. Protocol completed. Start chatting.

cli at socket 3 => serv: hello
    serv => cli at socket 4, 5 : hello
cli at socket 4 => serv: hi
    serv => cli at socket 3, 5: hi
     .................

servping.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

void handle_protocol(int x, fd_set *pset, int state[]);
void handle_state_1(int x, fd_set *pset, char *buf, int state[]);
void handle_state_2(int x, fd_set *pset, char *buf, int state[]);
void handle_state_3(int x, fd_set *pset, char *buf, int state[]);

int main()
{
    int s1, s2, i, x, y;
    struct sockaddr_in serv_addr, cli_addr;
    char buf[50];
    socklen_t xx;

    printf("Hi, I am the server\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", s1);

    // bind ip
    x = bind(s1, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (x < 0)
    {
        printf("binding failed\n");
        exit(1);
    }
    printf("binding passed\n");
    listen(s1, 5);
    xx = sizeof(cli_addr);

    // now start ping-pong-pang-pung
    // pset remembers all sockets to monitor
    // rset is the copy of pset passed to select
    int maxfd = 50;   // just monitor max 50 sockets
    int state[maxfd]; // ex2 : ๊ฐ ์†Œ์ผ“๋ณ„ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด ์„ ์–ธ

    fd_set rset, pset;
    FD_ZERO(&rset); // init rset
    FD_ZERO(&pset); // init pset

    // step 1. monitor conn req packet
    FD_SET(s1, &pset);
    // and loop on select
    for (i = 0; i < 20; i++)
    {                                           // should be infinite loop in real life
        rset = pset;                            // step 2
        select(maxfd, &rset, NULL, NULL, NULL); // step 3
        // now we have some packets
        for (x = 0; x < maxfd; x++)
        { // check which socket has a packet
            if (FD_ISSET(x, &rset))
            { // socket x has a packet
                // s1 is a special socket for which we have to do "accept"
                // otherwise do ping-pong-pang-pung
                if (x == s1)
                { // new client has arrived
                    // create a socket for this client
                    s2 = accept(s1, (struct sockaddr *)&cli_addr, &xx);
                    state[s2] = 1; // ex2 : ํ•ด๋‹น ์†Œ์ผ“์˜ ์ƒํƒœ๋ฅผ 1๋กœ ์ดˆ๊ธฐํ™”
                    printf("new cli at socket %d\n", s2);
                    FD_SET(s2, &pset); // and include this socket in pset
                }
                else
                { // data packet. do ping-pong-pang-pung protocol
                    handle_protocol(x, &pset, state);
                }
            }
        }
    }
}

void handle_state_1(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 1. Expecting "ping" in buf. if we have ping, send "pong" and
    // just update state[x]=2; otherwise send error message and disconnect the connection

    if (strcmp(buf, "ping") == 0)
    { // if it is a ping
        printf("client at socket %d => serv: ping\n", x);
        write(x, "pong", 4); // send pong
        printf("serv => client at socket %d: pong\n", x);
    }
    else
    {
        printf("client at socket %d => serv: other message\n", x);
        write(x, "Error! You should enter \"ping\". Try again!", 44); // send pong
        printf("serv => client at socket %d: Error! You should enter \"ping\". Try again!\n", x);
        close(x);        // ex 2-1 : and stop the protocol
        FD_CLR(x, pset); // ex 2-1 : no more monitoring on this socket
    }
    state[x] = 2; // ex 2 : ์ƒํƒœ ๊ฐ’์„ 2๋กœ ์„ค์ •
}

void handle_state_2(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 2. we are expecting "pang" in buf. If we have "pang", send "pung"
    // and close the connection. If we didnโ€™t receive โ€œpangโ€, send โ€œprotocol errorโ€ to the
    // client and disconnect.
    if (strcmp(buf, "pang") == 0)
    { // it it is a pang
        printf("client at socket %d => serv: pang\n", x);
        write(x, "pung. Process completed. Start chatting.", 40); // send pung
        printf("serv => client at socket %d: pung. Process completed. Start chatting.\n", x);
    }

    else
    {
        printf("client at socket %d => serv: %s\n", x, buf);
        write(x, "Error! You should enter \"pang\". Try again!", 44); // send pong
        printf("serv => client at socket %d: Error! You should enter \"pang\". Try again!\n", x);
        close(x);        // and stop the protocol
        FD_CLR(x, pset); // no more monitoring on this socket
    }

    state[x] = 3; // ex 3 : ์ƒํƒœ ๊ฐ’์„ 3์œผ๋กœ ์„ค์ •
}

void handle_state_3(int x, fd_set *pset, char *buf, int state[]) // ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ
{
    int i;
    char msg[100];
    sprintf(msg, "msg from cli %d : %s\n", x, buf); // ๋ฉ”์„ธ์ง€์— ์†ก์‹ ์ž ๊ธฐ์žฌ (์†Œ์ผ“๋ฒˆํ˜ธ ์ด์šฉ)
    for (i = 0; i < 50; i++)                        // ์ตœ๋Œ€ 50๊ฐœ ์†Œ์ผ“๊นŒ์ง€ monitor
    {
        if (state[i] >= 3 && i != x) // ์†Œ์ผ“์˜ ์ƒํƒœ๊ฐ€ 3์ด์ƒ์ด๋ฉด์„œ ์ž์‹ ์ด ์•„๋‹ ๋•Œ
        {
            // write(i, buf, strlen(buf)); // ๋ฉ”์„ธ์ง€ ์ „์†ก
            // printf("serv => client at socket %d : %s\n", i, buf);
            write(i, msg, strlen(msg)); // ๋ฉ”์„ธ์ง€ ์ „์†ก
        }
    }

    state[x]++;
}

void handle_protocol(int x, fd_set *pset, int state[])
{
    // we have data packet in socket x. state[x] shows the state of socket x.
    // handle the protocol.
    int y;
    char buf[50];
    y = read(x, buf, 50); // read data
    buf[y] = 0;           // make it a string
    if (state[x] == 1)    // ex2 : ์ƒํƒœ๊ฐ€ 1์ธ ๊ฒฝ์šฐ
        handle_state_1(x, pset, buf, state);
    else if (state[x] == 2) // ex2 : ์ƒํƒœ๊ฐ€ 2์ธ ๊ฒฝ์šฐ
        handle_state_2(x, pset, buf, state);
    else if (state[x] >= 3 && state[x] <= 7) // ex4 : ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ (sequence ์™„๋ฃŒ)
        handle_state_3(x, pset, buf, state);
    else if (state[x] == 8) // ex4 : ํ•œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฑ„ํŒ…์„ 5๋ฒˆ ์ด์ƒ ์ž…๋ ฅํ–ˆ๋‹ค๋ฉด,
    {
        close(x);        // ์†Œ์ผ“์„ ๋‹ซ๊ณ 
        FD_CLR(x, pset); // pset์—์„œ ํ•ด๋‹น ์†Œ์ผ“ clear
    }
}

cliping.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define BUFFER_SIZE 50
#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

int main()
{
    int x, y, i;
    struct sockaddr_in serv_addr;
    char buf[BUFFER_SIZE];
    printf("Hi, I am the client\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((x = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", x);

    // connect to the server
    if (connect(x, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("can't connect to the server\n");
        exit(1);
    }
    // now start ping-pong-pang-pung protocol
    printf("client => serv : (Enter ping) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(x, buf, strlen(buf) - 1); // send "ping"

    y = read(x, buf, 50); // read "pong"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf);

    if (strcmp(buf, "pong") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }

    printf("client => serv : (Enter pang) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(x, buf, strlen(buf) - 1); // send "pang"

    y = read(x, buf, BUFFER_SIZE - 1); // read "pung"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf);

    if (strcmp(buf, "pung. Process completed. Start chatting.") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }

    int ch = fork();
    if (ch == 0)
    {
        printf("(Chat)\n");

        for (i = 0; i < 5; i++)
        {
            // printf("client => serv : (Chat) ");
            fgets(buf, BUFFER_SIZE - 1, stdin);
            write(x, buf, strlen(buf) - 1);
            // write(x, buf, BUFFER_SIZE - 1);
        }
        close(x);
        exit(0);
    }
    else
    {
        for (i = 0; i < 5; i++)
        {
            y = read(x, buf, BUFFER_SIZE - 1);
            if (y > 0)
            {
                buf[y] = '\0';
                printf("%s", buf);

                // printf("server => client: %s\n", buf);
            }
        }
    }

    close(x); // disconect the communication
}

5) Modify your code in Problem 4) such that the server attaches the clientโ€™s name and age in the message. For this purpose, the server should ask name and age for each client and store them in cli[] array which is an array of client{} structure to store name and age of each client. cli[x] will remember the client information whose socket number is x.

     struct client{
        char name[20];  // this client's name
        char age[5];      // this client's age as a string
     };
     ......
     struct client cli[50];  // max 50 clients
cli  aaa=> serv: ping
serv => cli aaa: pong
cli  aaa=> serv: pang
serv => cli aaa: pung. name?
cli  aaa=> serv: aaa
serv => cli aaa: age?
cli aaa => serv: 19

cli  bbb=> serv: ping
serv => cli bbb: pong
cli  bbb=> serv: pang
serv => cli bbb: pung. name?
cli  bbb=> serv: bbb
serv => cli aaa: age?
cli aaa => serv: 22

cli  ccc=> serv: ping
serv => cli ccc: pong
cli  ccc=> serv: pang
serv => cli ccc: pung. name?
cli  ccc=> serv: ccc
serv => cli aaa: age?
cli aaa => serv: 21

serv => cli aaa: start chatting
serv => cli bbb: start chatting
serv => cli bbb: start chatting
cli aaa => serv: "hello there"
serv=> cli bbb: "aaa 19 to bbb 22: hello there"
serv=> cli ccc: "aaa 19 to ccc 21: hello there"
cli bbb=> serv: "hi"
serv => cli aaa: "bbb 22 to aaa 19: hi"
serv => cli ccc: "bbb 22 to ccc 21: hi"

servping.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

struct client // ex 5 : ๊ตฌ์กฐ์ฒด ์„ ์–ธ
{
    char name[20]; // ex 5 : ํด๋ผ์ด์–ธํŠธ ์ด๋ฆ„
    char age[5];   // ex 5 : ํด๋ผ์ด์–ธํŠธ ๋‚˜์ด
};

void handle_protocol(int x, fd_set *pset, int state[], struct client cli[]);
void handle_state_1(int x, fd_set *pset, char *buf, int state[]);
void handle_state_2(int x, fd_set *pset, char *buf, int state[]);
void handle_state_3(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_4(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_5(int x, fd_set *pset, char *buf, int state[], struct client cli[]);

int main()
{

    int s1, s2, i, x, y;
    struct sockaddr_in serv_addr, cli_addr;
    char buf[50];
    socklen_t xx;

    printf("Hi, I am the server\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", s1);

    // bind ip
    x = bind(s1, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (x < 0)
    {
        printf("binding failed\n");
        exit(1);
    }
    printf("binding passed\n");
    listen(s1, 5);
    xx = sizeof(cli_addr);

    // now start ping-pong-pang-pung
    // pset remembers all sockets to monitor
    // rset is the copy of pset passed to select
    int maxfd = 50;        // just monitor max 50 sockets
    int state[maxfd];      // ex2 : ๊ฐ ์†Œ์ผ“๋ณ„ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด ์„ ์–ธ
    struct client cli[50]; // ex5 : ๊ตฌ์กฐ์ฒด ์ƒ์„ฑ

    fd_set rset, pset;
    FD_ZERO(&rset); // init rset
    FD_ZERO(&pset); // init pset

    // step 1. monitor conn req packet
    FD_SET(s1, &pset);
    // and loop on select
    for (i = 0; i < 20; i++)
    {                                           // should be infinite loop in real life
        rset = pset;                            // step 2
        select(maxfd, &rset, NULL, NULL, NULL); // step 3
        // now we have some packets
        for (x = 0; x < maxfd; x++)
        { // check which socket has a packet
            if (FD_ISSET(x, &rset))
            { // socket x has a packet
                // s1 is a special socket for which we have to do "accept"
                // otherwise do ping-pong-pang-pung
                if (x == s1)
                { // new client has arrived
                    // create a socket for this client
                    s2 = accept(s1, (struct sockaddr *)&cli_addr, &xx);
                    state[s2] = 1; // ex2 : ํ•ด๋‹น ์†Œ์ผ“์˜ ์ƒํƒœ๋ฅผ 1๋กœ ์ดˆ๊ธฐํ™”
                    printf("new cli at socket %d\n", s2);
                    FD_SET(s2, &pset); // and include this socket in pset
                }
                else
                { // data packet. do ping-pong-pang-pung protocol
                    handle_protocol(x, &pset, state, cli);
                }
            }
        }
    }
}

void handle_state_1(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 1. Expecting "ping" in buf. if we have ping, send "pong" and
    // just update state[x]=2; otherwise send error message and disconnect the connection

    if (strcmp(buf, "ping") == 0)
    { // if it is a ping
        printf("client at socket %d => serv: ping\n", x);
        write(x, "pong", 4); // send pong
        printf("serv => client at socket %d: pong\n", x);
    }
    else
    {
        printf("client at socket %d => serv: other message\n", x);
        write(x, "Error! You should enter \"ping\". Try again!", 44); // send pong
        printf("serv => client at socket %d: Error! You should enter \"ping\". Try again!\n", x);
        close(x);        // ex 2-1 : and stop the protocol
        FD_CLR(x, pset); // ex 2-1 : no more monitoring on this socket
    }
    state[x] = 2; // ex 2 : ์ƒํƒœ ๊ฐ’์„ 2๋กœ ์„ค์ •
}

void handle_state_2(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 2. we are expecting "pang" in buf. If we have "pang", send "pung"
    // and close the connection. If we didnโ€™t receive โ€œpangโ€, send โ€œprotocol errorโ€ to the
    // client and disconnect.
    if (strcmp(buf, "pang") == 0)
    { // it it is a pang
        printf("client at socket %d => serv: pang\n", x);
        write(x, "pung. name?", 11); // send pung
        printf("serv => client at socket %d: pung. name?\n", x);
    }

    else
    {
        printf("client at socket %d => serv: %s\n", x, buf);
        write(x, "Error! You should enter \"pang\". Try again!", 44); // send pong
        printf("serv => client at socket %d: Error! You should enter \"pang\". Try again!\n", x);
        close(x);        // and stop the protocol
        FD_CLR(x, pset); // no more monitoring on this socket
    }

    state[x] = 3; // ex 3 : ์ƒํƒœ ๊ฐ’์„ 3์œผ๋กœ ์„ค์ •
}

void handle_state_3(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    strcpy(cli[x].name, buf); // ex5 : buf(์ด๋ฆ„)๋ฅผ ํ•ด๋‹น ์†Œ์ผ“์˜ ์ด๋ฆ„ ํ•ญ๋ชฉ์œผ๋กœ ๋ณต์‚ฌ
    write(x, "age?", 4);      // ex5 : age ์š”์ฒญ ๋ฉ”์‹œ์ง€ ์ „์†ก
    state[x] = 4;             // ex5 : ์ƒํƒœ ๊ฐ’์„ 4์œผ๋กœ ์„ค์ •
}

void handle_state_4(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    strcpy(cli[x].age, buf);         // ex5 : buf(๋‚˜์ด)๋ฅผ ํ•ด๋‹น ์†Œ์ผ“์˜ ๋‚˜์ด ํ•ญ๋ชฉ์œผ๋กœ ๋ณต์‚ฌ
    write(x, "Start Chatting!", 15); // ex5 : chatting ์‹œ์ž‘ ๋ฉ”์‹œ์ง€ ์ „์†ก
    state[x] = 5;                    // ex5 : ์ƒํƒœ ๊ฐ’์„ 5์œผ๋กœ ์„ค์ •
}

void handle_state_5(int x, fd_set *pset, char *buf, int state[], struct client cli[]) // ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ
{
    int i;
    char msg[100];
    sprintf(msg, "msg from cli %d : %s\n", x, buf); // ๋ฉ”์„ธ์ง€์— ์†ก์‹ ์ž ๊ธฐ์žฌ (์†Œ์ผ“๋ฒˆํ˜ธ ์ด์šฉ)
    for (i = 0; i < 50; i++)                        // ์ตœ๋Œ€ 50๊ฐœ ์†Œ์ผ“๊นŒ์ง€ monitor
    {
        if (state[i] >= 3 && i != x) // ์†Œ์ผ“์˜ ์ƒํƒœ๊ฐ€ 3์ด์ƒ์ด๋ฉด์„œ ์ž์‹ ์ด ์•„๋‹ ๋•Œ
        {
            write(i, msg, strlen(msg)); // ๋ฉ”์„ธ์ง€ ์ „์†ก
        }
    }

    state[x]++; // ์ƒํƒœ + 1
}

void handle_protocol(int x, fd_set *pset, int state[], struct client cli[])
{
    // we have data packet in socket x. state[x] shows the state of socket x.
    // handle the protocol.
    int y;
    char buf[50];
    y = read(x, buf, 50);                         // read data
    buf[y] = 0;                                   // make it a string
    if (state[x] == 1)                            // ex2 : ์ƒํƒœ๊ฐ€ 1์ธ ๊ฒฝ์šฐ
        handle_state_1(x, pset, buf, state);      // ping ๋„์ฐฉ, pong ์ „์†ก ์ƒํƒœ
    else if (state[x] == 2)                       // ex2 : ์ƒํƒœ๊ฐ€ 2์ธ ๊ฒฝ์šฐ
        handle_state_2(x, pset, buf, state);      // pang ๋„์ฐฉ, pung ์ „์†ก ์ƒํƒœ
    else if (state[x] == 3)                       // ex5 : ์ƒํƒœ๊ฐ€ 3์ธ ๊ฒฝ์šฐ
        handle_state_3(x, pset, buf, state, cli); // ex5 : name ๋„์ฐฉ, age ์š”์ฒญ ์ƒํƒœ
    else if (state[x] == 4)                       // ex5 : ์ƒํƒœ๊ฐ€ 3์ธ ๊ฒฝ์šฐ
        handle_state_4(x, pset, buf, state, cli); // ex5 : name ๋„์ฐฉ, age ์š”์ฒญ ์ƒํƒœ
    else if (state[x] >= 5 && state[x] <= 9)      // ex4 : ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ (sequence ์™„๋ฃŒ)
        handle_state_5(x, pset, buf, state, cli);
    else if (state[x] == 10) // ex4 : ํ•œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฑ„ํŒ…์„ 5๋ฒˆ ์ด์ƒ ์ž…๋ ฅํ–ˆ๋‹ค๋ฉด,
    {
        close(x);        // ์†Œ์ผ“์„ ๋‹ซ๊ณ 
        FD_CLR(x, pset); // pset์—์„œ ํ•ด๋‹น ์†Œ์ผ“ clear
    }
}

cliping.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define BUFFER_SIZE 50
#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

int main()
{
    int x, y, i;
    struct sockaddr_in serv_addr;
    char buf[BUFFER_SIZE];
    printf("Hi, I am the client\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((x = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", x);

    // connect to the server
    if (connect(x, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("can't connect to the server\n");
        exit(1);
    }
    // now start ping-pong-pang-pung protocol
    printf("client => serv : (Enter ping) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(x, buf, strlen(buf) - 1); // send "ping"

    y = read(x, buf, 50); // read "pong"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf);

    if (strcmp(buf, "pong") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }

    printf("client => serv : (Enter pang) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(x, buf, strlen(buf) - 1); // send "pang"

    y = read(x, buf, BUFFER_SIZE - 1); // read "pung. name?"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf); // ex5 : print server message
    if (strcmp(buf, "pung. name?") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }
    printf("client => serv : ");
    fgets(buf, BUFFER_SIZE - 1, stdin); // ex5 : enter name
    write(x, buf, strlen(buf) - 1);     // ex5 : send name

    y = read(x, buf, BUFFER_SIZE - 1); // ex5 : read "age?"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf); // ex5 : print server message ("age?")
    printf("client => serv : ");
    fgets(buf, BUFFER_SIZE - 1, stdin); // ex5 : enter age
    write(x, buf, strlen(buf) - 1);     // ex5 : send age

    y = read(x, buf, BUFFER_SIZE - 1); // ex5 : read "Start chatting"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf); // ex5 : print server message ("Start chatting")

    int ch = fork(); // ex4 : ํ”„๋กœ์„ธ์Šค ๋ณต์ œ
    // ex4 : child๋Š” ์†ก์‹  ๋‹ด๋‹น, parents๋Š” ์ˆ˜์‹  ๋‹ด๋‹น
    if (ch == 0)
    {
        printf("(Chat)\n");

        for (i = 0; i < 5; i++)
        {
            fgets(buf, BUFFER_SIZE - 1, stdin);
            write(x, buf, strlen(buf) - 1);
        }
        close(x);
        exit(0);
    }
    else
    {
        for (i = 0; i < 5; i++)
        {
            y = read(x, buf, BUFFER_SIZE - 1);
            if (y > 0)
            {
                buf[y] = '\0';
                printf("%s", buf);
            }
        }
    }

    close(x); // disconect the communication
}

6) Modify your code in Problem 5) such that the client can now specify which client it wants to chat with. Add โ€œpartnerโ€ to client{} strucure to remember the socket number of the chatting partner. The server should ask which partner the clients wants to talk with and remember the partnerโ€™s socket number in the client{} structure. Assume if โ€˜cli Aโ€™ points to โ€˜cli Bโ€™ as a partner, โ€˜cli Bโ€™ also points to โ€˜cli Aโ€™ as a partner.

     struct client{
        char name[20];  // this client's name
        char age[5];      // this client's age as a string
        int partner;     // the socket number of the chatting partner of this client
     };
cli  aaa=> serv: ping
serv => cli aaa: pong
cli  aaa=> serv: pang
serv => cli aaa: name?
cli  aaa=> serv: aaa

cli  bbb=> serv: ping
serv => cli bbb: pong
cli  bbb=> serv: pang
serv => cli bbb: name?
cli  bbb=> serv: bbb

cli  ccc=> serv: ping
serv => cli ccc: pong
cli  ccc=> serv: pang
serv => cli ccc: name?
cli  ccc=> serv: ccc

cli  ddd=> serv: ping
serv => cli ddd: pong
cli  ddd=> serv: pang
serv => cli ddd: name?
cli  ddd=> serv: ddd

serv=>cli aaa: chat partner?
cli aaa=>serv: bbb
serv=>cli bbb: chat partner?
cli bbb=>serv: aaa

serv=>cli ccc: chat partner?
cli ccc=>serv: ddd
serv=>cli ddd: chat partner?
cli ddd=>serv: ccc

serv => cli aaa: start chatting
serv => cli bbb: start chatting
serv => cli ccc: start chatting
serv => cli ddd: start chatting

cli aaa => serv: hello there
serv=> cli bbb: aaa to bbb: hello there
cli bbb=> serv: hi
serv => cli aaa: bbb to aaa: hi

cli ccc => serv: hear me
serv=> cli ddd: ccc to ddd: hear me
cli ddd=> serv: hi there
serv => cli ccc: ddd to ccc: hi there

์œ„ ์‚ฌ์ง„์—์„œ ์™ผ์ชฝ ํ„ฐ๋ฏธ๋„์€ server์ด๊ณ , ์˜ค๋ฅธ์ชฝ์˜ 3๊ฐœ์˜ ํ„ฐ๋ฏธ๋„์€ client๋ฅผ ๋‹ด๋‹นํ•˜์˜€๋‹ค. ํŽธ์˜๋ฅผ ์œ„ํ•ด, ์˜ค๋ฅธ์ชฝ์˜ client๋“ค์„ ์œ„์—์„œ๋ถ€ํ„ฐ ๊ฐ๊ฐ first, second, third๋ผ๊ณ  ํ•˜์ž.

second์™€ third๋Š” ์„œ๋กœ๋ฅผ chatting partner๋กœ ์ง€์ •ํ•˜์˜€๋‹ค. chatting partner๋กœ ๋งค์นญ๋œ second์™€ third๋Š” ๋Œ€ํ™”๊ฐ€ ๊ฐ€๋Šฅํ•˜์˜€์ง€๋งŒ, chatting partner๋ฅผ ๋งค์นญํ•˜์ง€ ๋ชปํ•จ first์—๊ฒŒ๋Š” ๋ฉ”์„ธ์ง€๊ฐ€ ์˜ค์ง€ ์•Š์•˜๋‹ค.

servping.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

struct client // ex 5 : ๊ตฌ์กฐ์ฒด ์„ ์–ธ
{
    char name[20]; // ex 5 : ํด๋ผ์ด์–ธํŠธ ์ด๋ฆ„
    char age[5];   // ex 5 : ํด๋ผ์ด์–ธํŠธ ๋‚˜์ด
    int partner;   // ex 6 : the socket number of the chatting partner of this client
};

void handle_protocol(int x, fd_set *pset, int state[], struct client cli[]);
void handle_state_1(int x, fd_set *pset, char *buf, int state[]);
void handle_state_2(int x, fd_set *pset, char *buf, int state[]);
void handle_state_3(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_4(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_5(int x, fd_set *pset, char *buf, int state[], struct client cli[]);

int main()
{

    int s1, s2, i, x, y;
    struct sockaddr_in serv_addr, cli_addr;
    char buf[50];
    socklen_t xx;

    printf("Hi, I am the server\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", s1);

    // bind ip
    x = bind(s1, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (x < 0)
    {
        printf("binding failed\n");
        exit(1);
    }
    printf("binding passed\n");
    listen(s1, 5);
    xx = sizeof(cli_addr);

    // now start ping-pong-pang-pung
    // pset remembers all sockets to monitor
    // rset is the copy of pset passed to select
    int maxfd = 50;        // just monitor max 50 sockets
    int state[maxfd];      // ex2 : ๊ฐ ์†Œ์ผ“๋ณ„ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด ์„ ์–ธ
    struct client cli[50]; // ex5 : ๊ตฌ์กฐ์ฒด ์ƒ์„ฑ

    fd_set rset, pset;
    FD_ZERO(&rset); // init rset
    FD_ZERO(&pset); // init pset

    // step 1. monitor conn req packet
    FD_SET(s1, &pset);
    // and loop on select
    for (i = 0; i < 40; i++)                    // ex6 : select ๋ฃจํ”„ ํšŸ์ˆ˜๋ฅผ 20์—์„œ 40์œผ๋กœ ๋ณ€๊ฒฝ
    {                                           // should be infinite loop in real life
        rset = pset;                            // step 2
        select(maxfd, &rset, NULL, NULL, NULL); // step 3
        // now we have some packets
        for (x = 0; x < maxfd; x++)
        { // check which socket has a packet
            if (FD_ISSET(x, &rset))
            { // socket x has a packet
                // s1 is a special socket for which we have to do "accept"
                // otherwise do ping-pong-pang-pung
                if (x == s1)
                { // new client has arrived
                    // create a socket for this client
                    s2 = accept(s1, (struct sockaddr *)&cli_addr, &xx);
                    state[s2] = 1; // ex2 : ํ•ด๋‹น ์†Œ์ผ“์˜ ์ƒํƒœ๋ฅผ 1๋กœ ์ดˆ๊ธฐํ™”
                    printf("new cli at socket %d\n", s2);
                    FD_SET(s2, &pset); // and include this socket in pset
                }
                else
                { // data packet. do ping-pong-pang-pung protocol
                    handle_protocol(x, &pset, state, cli);
                }
            }
        }
    }
}

void handle_state_1(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 1. Expecting "ping" in buf. if we have ping, send "pong" and
    // just update state[x]=2; otherwise send error message and disconnect the connection

    if (strcmp(buf, "ping") == 0)
    { // if it is a ping
        printf("client at socket %d => serv: ping\n", x);
        write(x, "pong", 4); // send pong
        printf("serv => client at socket %d: pong\n", x);
    }
    else
    {
        printf("client at socket %d => serv: other message\n", x);
        write(x, "Error! You should enter \"ping\". Try again!", 44); // send pong
        printf("serv => client at socket %d: Error! You should enter \"ping\". Try again!\n", x);
        close(x);        // ex 2-1 : and stop the protocol
        FD_CLR(x, pset); // ex 2-1 : no more monitoring on this socket
    }
    state[x] = 2; // ex 2 : ์ƒํƒœ ๊ฐ’์„ 2๋กœ ์„ค์ •
}

void handle_state_2(int x, fd_set *pset, char *buf, int state[])
{
    // socket x is in state 2. we are expecting "pang" in buf. If we have "pang", send "pung"
    // and close the connection. If we didnโ€™t receive โ€œpangโ€, send โ€œprotocol errorโ€ to the
    // client and disconnect.
    if (strcmp(buf, "pang") == 0)
    { // it it is a pang
        printf("client at socket %d => serv: pang\n", x);
        write(x, "pung. name?", 11); // send pung
        printf("serv => client at socket %d: pung. name?\n", x);
    }

    else
    {
        printf("client at socket %d => serv: %s\n", x, buf);
        write(x, "Error! You should enter \"pang\". Try again!", 44); // send pong
        printf("serv => client at socket %d: Error! You should enter \"pang\". Try again!\n", x);
        close(x);        // and stop the protocol
        FD_CLR(x, pset); // no more monitoring on this socket
    }

    state[x] = 3; // ex 3 : ์ƒํƒœ ๊ฐ’์„ 3์œผ๋กœ ์„ค์ •
}

void handle_state_3(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    strcpy(cli[x].name, buf);      // ex5 : buf(์ด๋ฆ„)๋ฅผ ํ•ด๋‹น ์†Œ์ผ“์˜ ์ด๋ฆ„ ํ•ญ๋ชฉ์œผ๋กœ ๋ณต์‚ฌ
    write(x, "Chat partner?", 13); // ex6 : chat partner ์š”์ฒญ ๋ฉ”์‹œ์ง€ ์ „์†ก
    state[x] = 4;                  // ex5 : ์ƒํƒœ ๊ฐ’์„ 4์œผ๋กœ ์„ค์ •
}

void handle_state_4(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    int i;
    for (i = 0; i < 50; i++)
    {
        if (strcmp(cli[i].name, buf) == 0) // ex6 : ์ž…๋ ฅ๋ฐ›์€ chat partner(buf)์™€ ๋™์ผํ•œ client name์ด ์žˆ์œผ๋ฉด,
            cli[x].partner = i;            // ex6 : ํ•ด๋‹น ์†Œ์ผ“ ๋ฒˆํ˜ธ๋ฅผ partner ๋ณ€์ˆ˜์— ์ €์žฅ
    }
    write(x, "Start Chatting!", 15); // ex5 : chatting ์‹œ์ž‘ ๋ฉ”์‹œ์ง€ ์ „์†ก
    state[x] = 5;                    // ex5 : ์ƒํƒœ ๊ฐ’์„ 5์œผ๋กœ ์„ค์ •
}

void handle_state_5(int x, fd_set *pset, char *buf, int state[], struct client cli[]) // ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ
{
    int i;
    char msg[100];
    sprintf(msg, "msg from cli %d : %s\n", x, buf); // ex6 : ๋ฉ”์„ธ์ง€์— ์†ก์‹ ์ž(ํ˜„์žฌ cli)์™€ ์ˆ˜์‹ ์ž(partner) ๊ธฐ์žฌ (์†Œ์ผ“๋ฒˆํ˜ธ ์ด์šฉ)
    write(cli[x].partner, msg, strlen(msg));        // ex6 : partner์—๊ฒŒ ๋ฉ”์„ธ์ง€ ์ „์†ก

    state[x]++; // ์ƒํƒœ + 1
}

void handle_protocol(int x, fd_set *pset, int state[], struct client cli[])
{
    // we have data packet in socket x. state[x] shows the state of socket x.
    // handle the protocol.
    int y;
    char buf[50];
    y = read(x, buf, 50);                         // read data
    buf[y] = 0;                                   // make it a string
    if (state[x] == 1)                            // ex2 : ์ƒํƒœ๊ฐ€ 1์ธ ๊ฒฝ์šฐ
        handle_state_1(x, pset, buf, state);      // ping ๋„์ฐฉ, pong ์ „์†ก ์ƒํƒœ
    else if (state[x] == 2)                       // ex2 : ์ƒํƒœ๊ฐ€ 2์ธ ๊ฒฝ์šฐ
        handle_state_2(x, pset, buf, state);      // pang ๋„์ฐฉ, pung ์ „์†ก ์ƒํƒœ
    else if (state[x] == 3)                       // ex5 : ์ƒํƒœ๊ฐ€ 3์ธ ๊ฒฝ์šฐ
        handle_state_3(x, pset, buf, state, cli); // ex5 : name ๋„์ฐฉ, age ์š”์ฒญ ์ƒํƒœ
    else if (state[x] == 4)                       // ex5 : ์ƒํƒœ๊ฐ€ 3์ธ ๊ฒฝ์šฐ
        handle_state_4(x, pset, buf, state, cli); // ex5 : name ๋„์ฐฉ, age ์š”์ฒญ ์ƒํƒœ
    else if (state[x] >= 5 && state[x] <= 9)      // ex4 : ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ (sequence ์™„๋ฃŒ)
        handle_state_5(x, pset, buf, state, cli);
    else if (state[x] == 10) // ex4 : ํ•œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฑ„ํŒ…์„ 5๋ฒˆ ์ด์ƒ ์ž…๋ ฅํ–ˆ๋‹ค๋ฉด,
    {
        close(x);        // ์†Œ์ผ“์„ ๋‹ซ๊ณ 
        FD_CLR(x, pset); // pset์—์„œ ํ•ด๋‹น ์†Œ์ผ“ clear
    }
}

cliping.c

cliping.c๋Š” 6๋ฒˆ์˜ ์ฝ”๋“œ๋ฅผ ๊ทธ๋Œ€๋กœ ์ด์šฉํ•˜์˜€๋‹ค.

7) Implement a chatting server. The state of the client during the protocol is as follows. At any moment multiple pair of clients should be able to talk at the same time.

All clientโ€™s initial state is 1.

cli eee => serv: hello
serv => cli eee: name?
cli eee=> serv: eee
serv => cli eee: ready?
cli eee => serv : yes
serv => cli eee: client list (aaa bbb ccc โ€ฆ..)
cli eee=> serv : bbb
serv => cli eee: go
................
cli bbb => serv : yes
serv => cli bbb : client list (aaa bbb ccc eee ...)
cli bbb => serv : eee
serv => cli bbb : go

cli eee => serv : hi how are you
serv => cli bbb : hi how are you
cli bbb => serv : hi there
serv => cli eee : hi there
..............

myserver.c


#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

struct client // ex 5 : ๊ตฌ์กฐ์ฒด ์„ ์–ธ
{
    char name[20]; // ex 5 : ํด๋ผ์ด์–ธํŠธ ์ด๋ฆ„
    char age[5];   // ex 5 : ํด๋ผ์ด์–ธํŠธ ๋‚˜์ด
    int partner;   // ex 6 : the socket number of the chatting partner of this client
    int state;
};

void handle_protocol(int x, fd_set *pset, int state[], struct client cli[]);
void handle_state_1(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_2(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_3(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_4(int x, fd_set *pset, char *buf, int state[], struct client cli[]);
void handle_state_5(int x, fd_set *pset, char *buf, int state[], struct client cli[]);

int main()
{

    int s1, s2, i, x, y;
    struct sockaddr_in serv_addr, cli_addr;
    char buf[50];
    socklen_t xx;

    printf("Hi, I am the server\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((s1 = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", s1);

    // bind ip
    x = bind(s1, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if (x < 0)
    {
        printf("binding failed\n");
        exit(1);
    }
    printf("binding passed\n");
    listen(s1, 5);
    xx = sizeof(cli_addr);

    // pset remembers all sockets to monitor
    // rset is the copy of pset passed to select
    int maxfd = 50;   // just monitor max 50 sockets
    int state[maxfd]; // ex2 : ๊ฐ ์†Œ์ผ“๋ณ„ ์ƒํƒœ๋ฅผ ์ €์žฅํ•˜๋Š” ๋ฐฐ์—ด ์„ ์–ธ

    struct client cli[50]; // ex5 : ๊ตฌ์กฐ์ฒด ์ƒ์„ฑ
    for (i = 0; i < 50; i++)
        cli[i].state = 0; // ex7 : ๋ชจ๋“  ํ›„๋ณด ์†Œ์ผ“๋“ค์˜ ์ดˆ๊ธฐ state๋Š” 0์œผ๋กœ ์„ค์ •

    fd_set rset, pset;
    FD_ZERO(&rset); // init rset
    FD_ZERO(&pset); // init pset

    // step 1. monitor conn req packet
    FD_SET(s1, &pset);
    // and loop on select
    for (i = 0; i < 40; i++)                    // ex6 : select ๋ฃจํ”„ ํšŸ์ˆ˜๋ฅผ 20์—์„œ 40์œผ๋กœ ๋ณ€๊ฒฝ
    {                                           // should be infinite loop in real life
        rset = pset;                            // step 2
        select(maxfd, &rset, NULL, NULL, NULL); // step 3
        // now we have some packets
        for (x = 0; x < maxfd; x++)
        { // check which socket has a packet
            if (FD_ISSET(x, &rset))
            { // socket x has a packet
                // s1 is a special socket for which we have to do "accept"
                if (x == s1)
                { // new client has arrived
                    // create a socket for this client
                    s2 = accept(s1, (struct sockaddr *)&cli_addr, &xx);
                    cli[s2].state = 1; // ex7 : ์ƒˆ๋กœ ์ƒ์„ฑ๋œ ํด๋ผ์ด์–ธํŠธ ์†Œ์ผ“์˜ ์ƒํƒœ๋ฅผ 1๋กœ ์„ค์ •
                    printf("new cli at socket %d\n", s2);
                    FD_SET(s2, &pset); // and include this socket in pset
                }
                else
                {
                    handle_protocol(x, &pset, state, cli);
                }
            }
        }
    }
}

void handle_state_1(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    if (strcmp(buf, "hello") == 0) // ex7 : "hello"๋ฅผ ๋ฐ›์œผ๋ฉด
    {
        printf("client at socket %d => serv: hello\n", x);
        write(x, "name?", 5); // ex7 : send "name?"
        printf("serv => client at socket %d: name?\n", x);
    }
    else
    {
        printf("client at socket %d => serv: other message\n", x);
        write(x, "Error! You should enter \"hello\". Try again!", 44); // send Error message
        printf("serv => client at socket %d: Error! You should enter \"hello\". Try again!\n", x);
        close(x);        // ex 2-1 : and stop the protocol
        FD_CLR(x, pset); // ex 2-1 : no more monitoring on this socket
    }
    cli[x].state = 2; // ex 2 : ์ƒํƒœ ๊ฐ’์„ 2๋กœ ์„ค์ •
}

void handle_state_2(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    strcpy(cli[x].name, buf);                                    // ex7 : buf(์ด๋ฆ„)๋ฅผ ํ•ด๋‹น ์†Œ์ผ“์˜ ๊ตฌ์กฐ์ฒด์˜ ์ด๋ฆ„ ํ•ญ๋ชฉ์œผ๋กœ ๋ณต์‚ฌ
    printf("client at socket %d => serv: %s\n", x, cli[x].name); // ๋Œ€๋‹ต ์ถœ๋ ฅ
    write(x, "Ready?", 6);                                       // ex7 : "Ready?" ๋ฉ”์‹œ์ง€ ์ „์†ก
    printf("serv => client at socket %d: Ready?\n", x);
    cli[x].state += 1; // ex3 : ์ƒํƒœ ๊ฐ’์„ 3์œผ๋กœ ์„ค์ •
}

void handle_state_3(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    int i;
    printf("client at socket %d => serv: %s\n", x, buf); // ๋Œ€๋‹ต ์ถœ๋ ฅ
    if (strcmp(buf, "yes") == 0)                         // ex7 : "Ready?"์— ๋Œ€ํ•œ ๋Œ€๋‹ต์œผ๋กœ "yes"๋ฅผ ๋ฐ›์œผ๋ฉด
    {
        // ex7 : client list๋ฅผ ๋ณด๋ƒ„
        strcpy(buf, "client list (");
        for (i = 0; i < 50; i++)
        {
            if (x != i && cli[i].state > 2)
            {
                strcat(buf, cli[i].name);
                strcat(buf, ", ");
            }
        }
        if (buf[strlen(buf) - 2] == ',')
        {
            buf[strlen(buf) - 2] = '\0';
        }
        strcat(buf, ")");
        write(x, buf, strlen(buf));
        printf("serv => client at socket %d: %s\n", x, buf);
        cli[x].state += 1;
    }
    else
    {
        printf("client at socket %d => serv: other message\n", x);
        write(x, "You are not ready yet!", 22); // ex7 : send Error message
        printf("serv => client at socket %d: You are not ready yet!\n", x);
        close(x);        // ex 2-1 : and stop the protocol
        FD_CLR(x, pset); // ex 2-1 : no more monitoring on this socket
    }
}

void handle_state_4(int x, fd_set *pset, char *buf, int state[], struct client cli[])
{
    int i;
    printf("client at socket %d => serv: %s\n", x, buf);
    for (i = 0; i < 50; i++)
    {
        if (x != i && cli[i].state > 2 && strcmp(cli[i].name, buf) == 0) // ex6 : ์ž…๋ ฅ๋ฐ›์€ chat partner(buf)์™€ ๋™์ผํ•œ client name์ด ์žˆ์œผ๋ฉด,
        {
            cli[x].partner = i; // ex6 : ํ•ด๋‹น ์†Œ์ผ“ ๋ฒˆํ˜ธ๋ฅผ partner ๋ณ€์ˆ˜์— ์ €์žฅ
            write(x, "Go!", 3); // ex7 : chatting ์‹œ์ž‘ ๋ฉ”์‹œ์ง€ ์ „์†ก
            cli[x].state += 1;  // ex5 : ์ƒํƒœ ๊ฐ’์„ 5์œผ๋กœ ์„ค์ •
            break;
        }
    }
}

void handle_state_5(int x, fd_set *pset, char *buf, int state[], struct client cli[]) // ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ
{
    char msg[50];
    int len = sprintf(msg, "%s => %s : %s", cli[x].name, cli[cli[x].partner].name, buf);
    write(cli[x].partner, msg, len); // ex6 : partner์—๊ฒŒ ๋ฉ”์„ธ์ง€ ์ „์†ก
    printf("%s\n", msg);
    cli[x].state++; // ์ƒํƒœ + 1
}

void handle_protocol(int x, fd_set *pset, int state[], struct client cli[])
{
    // we have data packet in socket x.
    // cli[x].state shows the state of socket x.
    // handle the protocol.
    int y;
    char buf[50];
    y = read(x, buf, 50);                            // read data
    buf[y] = 0;                                      // make it a string
    if (cli[x].state == 1)                           // ex2 : ์ƒํƒœ๊ฐ€ 1์ธ ๊ฒฝ์šฐ
        handle_state_1(x, pset, buf, state, cli);    // hello ๋„์ฐฉ, pong ์ „์†ก ์ƒํƒœ
    else if (cli[x].state == 2)                      // ex2 : ์ƒํƒœ๊ฐ€ 2์ธ ๊ฒฝ์šฐ
        handle_state_2(x, pset, buf, state, cli);    // pang ๋„์ฐฉ, pung ์ „์†ก ์ƒํƒœ
    else if (cli[x].state == 3)                      // ex5 : ์ƒํƒœ๊ฐ€ 3์ธ ๊ฒฝ์šฐ
        handle_state_3(x, pset, buf, state, cli);    // ex5 : name ๋„์ฐฉ, age ์š”์ฒญ ์ƒํƒœ
    else if (cli[x].state == 4)                      // ex5 : ์ƒํƒœ๊ฐ€ 3์ธ ๊ฒฝ์šฐ
        handle_state_4(x, pset, buf, state, cli);    // ex5 : name ๋„์ฐฉ, age ์š”์ฒญ ์ƒํƒœ
    else if (cli[x].state >= 5 && cli[x].state <= 9) // ex4 : ์ฑ„ํŒ… ์†ก์ˆ˜์‹ ์ด ๊ฐ€๋Šฅํ•œ ์ƒํƒœ (sequence ์™„๋ฃŒ)
        handle_state_5(x, pset, buf, state, cli);
    else if (cli[x].state == 10) // ex4 : ํ•œ ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์ฑ„ํŒ…์„ 5๋ฒˆ ์ด์ƒ ์ž…๋ ฅํ–ˆ๋‹ค๋ฉด,
    {
        close(x);        // ์†Œ์ผ“์„ ๋‹ซ๊ณ 
        FD_CLR(x, pset); // pset์—์„œ ํ•ด๋‹น ์†Œ์ผ“ clear
    }
}

myclient.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/select.h>

#define BUFFER_SIZE 50
#define SERV_TCP_PORT 8739
#define SERV_ADDR "165.246.38.151" //"192.168.50.122"

int main()
{
    int x, y, i;
    struct sockaddr_in serv_addr;
    char buf[BUFFER_SIZE];
    printf("Hi, I am the client\n");

    bzero((char *)&serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = PF_INET;
    serv_addr.sin_addr.s_addr = inet_addr(SERV_ADDR);
    serv_addr.sin_port = htons(SERV_TCP_PORT);

    // open a tcp socket
    if ((x = socket(PF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("socket creation error\n");
        exit(1);
    }
    printf("socket opened successfully. socket num is %d\n", x);

    // connect to the server
    if (connect(x, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
    {
        printf("can't connect to the server\n");
        exit(1);
    }

    printf("client => serv : (Enter hello) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(x, buf, strlen(buf) - 1); // send "hello"

    y = read(x, buf, 50); // read "name?"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf);

    if (strcmp(buf, "name?") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }

    printf("client => serv : (Enter name) ");
    fgets(buf, BUFFER_SIZE - 1, stdin);
    write(x, buf, strlen(buf) - 1); // send name

    y = read(x, buf, BUFFER_SIZE - 1); // read "Ready?"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf); // ex5 : print server message
    if (strcmp(buf, "Ready?") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }
    printf("client => serv : (Enter yes/no) ");
    fgets(buf, BUFFER_SIZE - 1, stdin); // ex5 : enter "yes"
    write(x, buf, strlen(buf) - 1);     // ex5 : send "yes"

    y = read(x, buf, BUFFER_SIZE - 1); // ex5 : read "client list"s
    buf[y] = '\0';
    printf("serv => client : %s\n", buf); // ex5 : print server message ("client list"s)
    printf("client => serv : (Enter partner) ");
    fgets(buf, BUFFER_SIZE - 1, stdin); // ex5 : enter client lists
    write(x, buf, strlen(buf) - 1);     // ex5 : send client

    y = read(x, buf, BUFFER_SIZE - 1); // ex5 : read "Go!"
    buf[y] = '\0';
    printf("serv => client : %s\n", buf); // ex5 : print server message ("Go!")

    if (strcmp(buf, "Go!") != 0)
    {
        fprintf(stderr, "Error occurred by the protocol.\n");
        close(x);
        exit(EXIT_FAILURE);
    }

    int ch = fork(); // ex4 : ํ”„๋กœ์„ธ์Šค ๋ณต์ œ
    // ex4 : child๋Š” ์†ก์‹  ๋‹ด๋‹น, parents๋Š” ์ˆ˜์‹  ๋‹ด๋‹น
    if (ch == 0)
    {
        printf("(Chat)\n");

        while (1)
        {
            fgets(buf, BUFFER_SIZE - 1, stdin);
            write(x, buf, strlen(buf) - 1);
        }
    }
    else
    {
        while (1)
        {
            y = read(x, buf, BUFFER_SIZE - 1);
            if (y > 0)
            {
                buf[y] = '\0';
                printf("%s\n", buf);
            }
        }
    }

    close(x); // disconect the communication

    return 0;
}