AIによるコード生成の話。

今日は、真面目です。

さて、サンプルコードならば会話系AIで生成ができることが確認できましたが、「このコードで本当にOKか」ということで今日は記載していきたいと思います。

題材は、SCTPサーバプログラムです。

まず質問にて、サンプルコードを作成してもらいました。以下が生成さたサンプルコードです。

#include <iostream>
#include <sctp.h>
#include <pthread.h>

using namespace std;

void *thread_func(void *arg) {
  int sd = *((int *)arg);
  char buffer[1024];
  int n;

  while ((n = recv(sd, buffer, 1024, 0)) > 0) {
    cout << buffer << endl;
  }

  close(sd);

  return NULL;
}

int main() {
  int sd, rc;
  struct sctp_initmsg initmsg;
  struct sctp_sndrcvinfo sinfo;
  socklen_t len;
  pthread_t tid;

  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    return 1;
  }

  memset(&initmsg, 0, sizeof(initmsg));
  initmsg.sinit_maxstreams = 10;
  initmsg.sinit_max_init_streams = 10;
  initmsg.sinit_vtag_verification = 0;
  initmsg.sinit_cookie_life = 0;
  initmsg.sinit_max_sdu = 65535;
  initmsg.sinit_pathmax = 1;

  rc = setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
  if (rc < 0) {
    perror("setsockopt");
    return 1;
  }

  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    return 1;
  }

  rc = listen(sd, 5);
  if (rc < 0) {
    perror("listen");
    return 1;
  }

  while (1) {
    int new_sd;
    struct sockaddr_in client_addr;
    len = sizeof(client_addr);

    new_sd = accept(sd, (struct sockaddr *)&client_addr, &len);
    if (new_sd < 0) {
      perror("accept");
      return 1;
    }

    cout << "New connection from " << inet_ntoa(client_addr.sin_addr) << endl;

    pthread_create(&tid, NULL, thread_func, &new_sd);
  }

  return 0;
}

accept(クライアント接続)後、スレッド化され、受信処理ができるサンプルコードが生成されました。

これは、これでコンパイルも通り、実行できます。

ただ、これだと「SCTP」の持ち味がでていないので、会話を続けていきます。

まず、recv関数ではなく、sctp_recvmsg関数ではないかと指摘。

で、実際に変更したサンプルコードを生成してもらいました。

#include <iostream>
#include <sctp.h>
#include <pthread.h>

using namespace std;

void *thread_func(void *arg) {
  int sd = *((int *)arg);
  char buffer[1024];
  struct sctp_sndrcvinfo sinfo;
  socklen_t slen = sizeof(sinfo);
  int n;

  while ((n = sctp_recvmsg(sd, buffer, 1024, &sinfo, &slen, 0)) > 0) {
    cout << "Received " << n << " bytes from " << sinfo.sconn_addr.sin_addr.s_addr << ":" << ntohs(sinfo.sconn_addr.sin_port) << " on stream " << sinfo.ssn << endl;
    cout << buffer << endl;
  }

  close(sd);

  return NULL;
}

int main() {
  int sd, rc;
  struct sctp_initmsg initmsg;
  socklen_t len;
  pthread_t tid;

  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    return 1;
  }

  memset(&initmsg, 0, sizeof(initmsg));
  initmsg.sinit_maxstreams = 10;
  initmsg.sinit_max_init_streams = 10;
  initmsg.sinit_vtag_verification = 0;
  initmsg.sinit_cookie_life = 0;
  initmsg.sinit_max_sdu = 65535;
  initmsg.sinit_pathmax = 1;

  rc = setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
  if (rc < 0) {
    perror("setsockopt");
    return 1;
  }

  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    return 1;
  }

  rc = listen(sd, 5);
  if (rc < 0) {
    perror("listen");
    return 1;
  }

  while (1) {
    int new_sd;
    struct sockaddr_in client_addr;
    len = sizeof(client_addr);

    new_sd = accept(sd, (struct sockaddr *)&client_addr, &len);
    if (new_sd < 0) {
      perror("accept");
      return 1;
    }

    cout << "New connection from " << inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << endl;

    pthread_create(&tid, NULL, thread_func, &new_sd);
  }

  return 0;
}

recvがsctp_recvmsgに変更されました。

では、SCPTを利用するためのイベントオプションを指摘します。

サンプルコードは、以下の通り。

#include <iostream>
#include <sctp.h>
#include <pthread.h>

using namespace std;

void *thread_func(void *arg) {
  int sd = *((int *)arg);
  char buffer[1024];
  struct sctp_sndrcvinfo sinfo;
  socklen_t slen = sizeof(sinfo);
  int n;

  struct sctp_event_subscribe events;
  events.sctp_data_io_event = 1;
  events.sctp_association_event = 1;

  setsockopt(sd, IPPROTO_SCTP, SCTP_EVENTS, &events, sizeof(events));

  while ((n = sctp_recvmsg(sd, buffer, 1024, &sinfo, &slen, 0)) > 0) {
    cout << "Received " << n << " bytes from " << sinfo.sconn_addr.sin_addr.s_addr << ":" << ntohs(sinfo.sconn_addr.sin_port) << " on stream " << sinfo.ssn << endl;
    cout << buffer << endl;
  }

  close(sd);

  return NULL;
}

int main() {
  int sd, rc;
  struct sctp_initmsg initmsg;
  socklen_t len;
  pthread_t tid;

  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    return 1;
  }

  memset(&initmsg, 0, sizeof(initmsg));
  initmsg.sinit_maxstreams = 10;
  initmsg.sinit_max_init_streams = 10;
  initmsg.sinit_vtag_verification = 0;
  initmsg.sinit_cookie_life = 0;
  initmsg.sinit_max_sdu = 65535;
  initmsg.sinit_pathmax = 1;

  rc = setsockopt(sd, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, sizeof(initmsg));
  if (rc < 0) {
    perror("setsockopt");
    return 1;
  }

  struct sockaddr_in addr;
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    return 1;
  }

  rc = listen(sd, 5);
  if (rc < 0) {
    perror("listen");
    return 1;
  }

  while (1) {
    int new_sd;
    struct sockaddr_in client_addr;
    len = sizeof(client_addr);

    new_sd = accept(sd, (struct sockaddr *)&client_addr, &len);
    if (new_sd < 0) {
      perror("accept");
      return 1;
    }

    cout << "New connection from " << inet_ntoa(client_addr.sin_addr) << ":" << ntohs(client_addr.sin_port) << endl;

    pthread_create(&tid, NULL, thread_func, &new_sd);
  }

  return 0;
}

受信処理前にイベント取得用のオプションが追加されました。

さて、ここでコードではなくSCTPの特徴の1つを聞いてみました。

うむ。回答が返ってきました。「複数のIPを使用しての通信が可能であること」が回答されてきました。

今までのサンプルコードでその機能が有効になっているかを確認してみると、ネガティブである回答をされ、sctp_bindを利用するように回答されていますが、説明で出てきているIPアドレスが今までどこにもでていないので、確認。

ですよね。

struct sockaddr_in 内で設定できるIPアドレスは1つなのでまず、INADDR_ANY(0.0.0.0)を指定すれば、OSで割り振られIPを使用して通信が利用できることが回答されました。

受信時に接続先とは別のIPアドレスで返ってきて確認することができることを回答してもらいました。

今までの会話を踏まえ、サンプルコード生成してもらいました。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sctp/sctp.h>

int main(int argc, char *argv[]) {
  int sd;
  struct sockaddr_in addr;
  int rc;

  /* ソケットを作成 */
  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    exit(1);
  }

  /* アドレスを初期化 */
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  /* 接続をバインド */
  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    exit(1);
  }

  /* 接続をリッスン */
  listen(sd, 5);

  /* 接続を待機 */
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    int client_sd;

    /* 接続を受け入れ */
    client_sd = accept(sd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_sd < 0) {
      perror("accept");
      exit(1);
    }

    /* 接続からデータを受信 */
    char buffer[1024];
    ssize_t bytes_recv;

    bytes_recv = recv(client_sd, buffer, sizeof(buffer), 0);
    if (bytes_recv < 0) {
      perror("recv");
      exit(1);
    }

    /* データを表示 */
    printf("Received: %s\n", buffer);

    /* 接続を閉じる */
    close(client_sd);
  }

  return 0;
}

スレッドやsctp_recvmsgが消えました。これでは、今までの会話内容が反映されていないことがわかります。

そこで、この内容を指摘。

文章があれですが、サンプルコードを生成してくれました。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sctp/sctp.h>

#define BUFFER_SIZE 1024

void *thread_func(void *arg) {
  int sd = *((int *)arg);
  char buffer[BUFFER_SIZE];
  ssize_t bytes_recv;

  while (1) {
    /* 接続からデータを受信 */
    bytes_recv = sctp_recvmsg(sd, buffer, BUFFER_SIZE, 0, NULL, NULL, NULL);
    if (bytes_recv < 0) {
      perror("sctp_recvmsg");
      exit(1);
    }

    /* データを表示 */
    printf("Received: %s\n", buffer);

    /* データを送信 */
    sctp_sendmsg(sd, buffer, bytes_recv, 0, NULL, NULL, 0);
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  int sd;
  struct sockaddr_in addr;
  int rc;

  /* ソケットを作成 */
  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    exit(1);
  }

  /* アドレスを初期化 */
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  /* 接続をバインド */
  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    exit(1);
  }

  /* 接続をリッスン */
  listen(sd, 5);

  /* 接続を待機 */
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    int client_sd;

    /* 接続を受け入れ */
    client_sd = accept(sd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_sd < 0) {
      perror("accept");
      exit(1);
    }

    /* スレッドを作成 */
    pthread_t thread;
    rc = pthread_create(&thread, NULL, thread_func, &client_sd);
    if (rc < 0) {
      perror("pthread_create");
      exit(1);
    }

    /* スレッドをjoin */
    rc = pthread_join(thread, NULL);
    if (rc < 0) {
      perror("pthread_join");
      exit(1);
    }
  }

  return 0;
}

たりない。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sctp/sctp.h>

#define BUFFER_SIZE 1024

int main(int argc, char *argv[]) {
  int sd;
  struct sockaddr_in addr;
  int rc;

  /* ソケットを作成 */
  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    exit(1);
  }

  /* アドレスを初期化 */
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  /* 接続をバインド */
  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    exit(1);
  }

  /* イベントをサブスクライブ */
  struct sctp_event_subscribe events;
  memset(&events, 0, sizeof(events));
  events.sctp_assoc_id = 0;
  events.sctp_event = SCTP_EVENT_DATA_RECEIVED;
  rc = sctp_setsockopt(sd, SCTP_EVENTS, &events, sizeof(events));
  if (rc < 0) {
    perror("sctp_setsockopt");
    exit(1);
  }

  /* 接続をリッスン */
  listen(sd, 5);

  /* 接続を待機 */
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    int client_sd;

    /* 接続を受け入れ */
    client_sd = accept(sd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_sd < 0) {
      perror("accept");
      exit(1);
    }

    /* 接続からデータを受信 */
    char buffer[BUFFER_SIZE];
    ssize_t bytes_recv;

    bytes_recv = sctp_recvmsg(sd, buffer, BUFFER_SIZE, 0, NULL, NULL, NULL);
    if (bytes_recv < 0) {
      perror("sctp_recvmsg");
      exit(1);
    }

    /* データを表示 */
    printf("Received: %s\n", buffer);

    /* データを送信 */
    sctp_sendmsg(sd, buffer, bytes_recv, 0, NULL, NULL, 0);
  }

  return 0;
}

やっぱりたりない

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sctp/sctp.h>

#define BUFFER_SIZE 1024

void *thread_func(void *arg) {
  int sd = *((int *)arg);
  char buffer[BUFFER_SIZE];
  ssize_t bytes_recv;

  while (1) {
    /* 接続からデータを受信 */
    bytes_recv = sctp_recvmsg(sd, buffer, BUFFER_SIZE, 0, NULL, NULL, NULL);
    if (bytes_recv < 0) {
      perror("sctp_recvmsg");
      exit(1);
    }

    /* データを表示 */
    printf("Received: %s\n", buffer);

    /* データを送信 */
    sctp_sendmsg(sd, buffer, bytes_recv, 0, NULL, NULL, 0);
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  int sd;
  struct sockaddr_in addr;
  int rc;

  /* ソケットを作成 */
  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    exit(1);
  }

  /* アドレスを初期化 */
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  /* 接続をバインド */
  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    exit(1);
  }

  /* イベントをサブスクライブ */
  struct sctp_event_subscribe events;
  memset(&events, 0, sizeof(events));
  events.sctp_assoc_id = 0;
  events.sctp_event = SCTP_EVENT_DATA_RECEIVED;
  rc = sctp_setsockopt(sd, SCTP_EVENTS, &events, sizeof(events));
  if (rc < 0) {
    perror("sctp_setsockopt");
    exit(1);
  }

  /* 接続をリッスン */
  listen(sd, 5);

  /* 接続を待機 */
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    int client_sd;

    /* 接続を受け入れ */
    client_sd = accept(sd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_sd < 0) {
      perror("accept");
      exit(1);
    }

    /* スレッドを作成 */
    pthread_t thread;
    rc = pthread_create(&thread, NULL, thread_func, &client_sd);
    if (rc < 0) {
      perror("pthread_create");
      exit(1);
    }

    /* スレッドをjoin */
    rc = pthread_join(thread, NULL);
    if (rc < 0) {
      perror("pthread_join");
      exit(1);
    }
  }

  return 0;
}

う~ん、足りない。

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sctp/sctp.h>

#define BUFFER_SIZE 1024

void *thread_func(void *arg) {
  int sd = *((int *)arg);
  char buffer[BUFFER_SIZE];
  ssize_t bytes_recv;

  while (1) {
    /* 接続からデータを受信 */
    bytes_recv = sctp_recvmsg(sd, buffer, BUFFER_SIZE, 0, NULL, NULL, NULL);
    if (bytes_recv < 0) {
      perror("sctp_recvmsg");
      exit(1);
    }

    /* データを表示 */
    printf("Received: %s\n", buffer);

    /* データを送信 */
    sctp_sendmsg(sd, buffer, bytes_recv, 0, NULL, NULL, 0);
  }

  return NULL;
}

int main(int argc, char *argv[]) {
  int sd;
  struct sockaddr_in addr;
  int rc;

  /* ソケットを作成 */
  sd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
  if (sd < 0) {
    perror("socket");
    exit(1);
  }

  /* アドレスを初期化 */
  memset(&addr, 0, sizeof(addr));
  addr.sin_family = AF_INET;
  addr.sin_port = htons(5000);
  addr.sin_addr.s_addr = INADDR_ANY;

  /* 接続をバインド */
  rc = bind(sd, (struct sockaddr *)&addr, sizeof(addr));
  if (rc < 0) {
    perror("bind");
    exit(1);
  }

  /* イベントをサブスクライブ */
  struct sctp_event_subscribe events;
  memset(&events, 0, sizeof(events));
  events.sctp_assoc_id = 0;
  events.sctp_event = SCTP_EVENT_DATA_RECEIVED;
  rc = sctp_setsockopt(sd, SCTP_EVENTS, &events, sizeof(events));
  if (rc < 0) {
    perror("sctp_setsockopt");
    exit(1);
  }

  /* 接続をリッスン */
  listen(sd, 5);

  /* 接続を待機 */
  while (1) {
    struct sockaddr_in client_addr;
    socklen_t client_addr_len;
    int client_sd;

    /* 接続を受け入れ */
    client_sd = accept(sd, (struct sockaddr *)&client_addr, &client_addr_len);
    if (client_sd < 0) {
      perror("accept");
      exit(1);
    }

    /* スレッドを作成 */
    pthread_t thread;
    rc = pthread_create(&thread, NULL, thread_func, &client_sd);
    if (rc < 0) {
      perror("pthread_create");
      exit(1);
    }

    /* スレッドをjoin */
    rc = pthread_join(thread, NULL);
    if (rc < 0) {
      perror("pthread_join");
      exit(1);
    }
  }

  return 0;
}

足りないどころか反映されていない。

っと7回ほど、レビュー指摘してきましたが、サンプルコードを生成するだけでもユーザがに「必要な知識」を持っていることが必須となります。

今回のことからもわかります通り、コードをAIが作成するには、まだ時期尚早であることがわかり、なんでもかんでも「今の状態で」依存することは危険であることが分かったかと思います。

今は「黎明期」だと思いますので、今後の発展に期待をしつつ、自分も置いてきぼりにならないよう努力をしていきたいと思います。

ちなみに、BSDでは、sctp_bindではなく、sctp_bindxとなります。