//-----------------------------------------------------------------
//  tcp_lib.c: ソケット通信によるデータ転送用ライブラリ
//-----------------------------------------------------------------
#include "tcp_lib.h"
//-----------------------------------------------------------------
// IPアドレスの取得
//-----------------------------------------------------------------
// ホスト名からIPアドレスの取得
char* GetIP(char* hostname)
{
    struct in_addr ipv4;
    unsigned long haddr;
    struct hostent *phost = gethostbyname(hostname);
    haddr = *((unsigned long *)((phost->h_addr_list)[0]));
    ipv4.s_addr = haddr;
    return inet_ntoa(ipv4);
}
// ホスト名の表示
void Disp_ipinfo(char* msg,int portno)
{
    char hostname[100];
    gethostname(hostname,sizeof(hostname));
    printf("** %s\n",msg);
    printf("  %s[%s]: %d\n",hostname,GetIP(hostname),portno);
}
//-----------------------------------------------------------------
//
// サーバ用
//
//-----------------------------------------------------------------
int TCP_accept(int acc,char* clhost)
{
    struct in_addr ipv4;
    struct sockaddr_in from;
    int sockadrlen = sizeof(from);
    int ack = accept(acc,(struct sockaddr*)&from,&sockadrlen);
    ipv4 = from.sin_addr;
    sprintf(clhost,"%s",inet_ntoa(ipv4));
    return ack;
}
int TCP_acc_port(int portno)
{
    int s;
    struct sockaddr_in addr;
    if((s=socket(AF_INET,SOCK_STREAM,0)) < 0){
        perror("socket"); return -1;
    }
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port        = htons(portno);
    if(bind(s,(struct sockaddr *)&addr,sizeof(addr)) < 0){
        perror("bind");
        fprintf(stderr,"port number %d is already used."
                "kill another program.",portno);
        return -1;
    }
    listen(s,5);
    return s;
}
//-----------------------------------------------------------------
//
// クライアント用
//
//-----------------------------------------------------------------
// ホスト-ポート先指定のソケット通信開始
int TCP_open(char* hostname,int portno)
{
    int sock = TCP_connect(hostname,portno);
    if(sock<0) return -1;
    return sock;
}
int TCP_connect(char *hostname,int portno)
{
    int s;
    struct hostent *hostent;
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    if((hostent = gethostbyname(hostname)) == NULL) {
        fprintf(stderr,"unknown host %s\n",hostname); return -1;
    }
    bcopy(hostent->h_addr,(char*)&addr.sin_addr,hostent->h_length);
    addr.sin_port = htons(portno);
    if((s=socket(AF_INET,SOCK_STREAM,0)) < 0){
        perror("socket"); return -1;
    }
    if(connect(s,(struct sockaddr *)&addr,sizeof(addr)) < 0){
        perror(hostname); close(s); return -1;
    }
    return s;
}
// ソケット通信終了
void TCP_close(int sock)
{
    close(sock);
}
//-----------------------------------------------------------------
// ソケットをファイル記述子に割り当て
//-----------------------------------------------------------------
int fdopen_sock(int sock,FILE **inp,FILE **outp)
{
    int sock2;
    if((sock2=dup(sock)) < 0) {
        perror("dup");
        return -1;
    }
    if((*inp = fdopen(sock2,"r")) == NULL) {
        perror("fdopen:r");
        close(sock2); return -1;
    }
    if((*outp = fdopen(sock,"w")) == NULL) {
        perror("fdopen:w");
        fclose(*inp); *inp = 0; return -1;
    }
    // setvbuf() によりバッファリングなしの設定.
    // 出力内容を即座にネットワークに送信
    setvbuf(*outp,(char *)NULL,_IOLBF,0);
    return 0;
}
//-----------------------------------------------------------------
//
// ソケットによる送受信
//
//-----------------------------------------------------------------
// 送信(See: UNIX Network Programming, SS.6.6, Utility routines)
int TCP_send(int sock,BYTE* buff,int nbytes)
{
    register int nleft, nwritten;
    nleft = nbytes;
    while(nleft > 0) {
        nwritten = write(sock,buff,nleft);
        if(nwritten <= 0) return nwritten;
        nleft -= nwritten;
        buff  += nwritten;
    }
    return (nbytes - nleft);
}
//-----------------------------------------------------------------
// 受信
//-----------------------------------------------------------------
int TCP_recv(int sock,BYTE* recbuf,int timeout)
{
    int rbyte;
    struct timeval tout; // タイムアウト時間
    fd_set fds;
    memset(recbuf,0,sizeof(BYTE)*BUFSIZ);
    if(timeout){
        tout.tv_sec  = timeout;//TIMEOUT_SEC;
        tout.tv_usec = 0;//TIMEOUT_USEC;
        FD_ZERO(&fds);                       //  fd_set 初期化
        FD_SET(sock,&fds);                   //  sock 設定
        select(sock+1,&fds,NULL,NULL,&tout); //  データ受信待ち
        if(FD_ISSET(sock,&fds)){             //  該当sock 読み込みデータあり
            rbyte = read(sock,recbuf,BUFSIZ);
        }
        if(rbyte < 0) rbyte = 0;
    } else {
        rbyte = read(sock,recbuf,BUFSIZ);
        if(rbyte < 0) rbyte = 0;
    }
    return rbyte;
}