text: /******************************************************************************
   * Copyright (C) 2014-2020 Zhifeng Gong <gozfree@163.com>
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   ******************************************************************************/
  #include <stdio.h>
  #include <stdlib.h>
  #include <stdint.h>
  #include <errno.h>
  #ifndef __ANDROID__
  #include <ifaddrs.h>
  #endif
  #include <stdarg.h>
  #include <string.h>
  #include <unistd.h>
  #include <signal.h>
  #include <netdb.h>
  #include <fcntl.h>
  #include <net/if.h>
  #include <sys/ioctl.h>
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <assert.h>
  #include "libptcp.h"
  
  static long filesize(FILE *fd)
  {
      long curpos, length;
      curpos = ftell(fd);
      fseek(fd, 0L, SEEK_END);
      length = ftell(fd);
      fseek(fd, curpos, SEEK_SET);
      return length;
  }
  
  struct xfer_callback {
      void *(*xfer_server_init)(const char *host, uint16_t port);
      void *(*xfer_client_init)(const char *host, uint16_t port);
      int (*xfer_send)(void *arg, void *buf, size_t len);
      int (*xfer_recv)(void *arg, void *buf, size_t len);
      int (*xfer_close)(void *arg);
      int (*xfer_errno)(void *arg);
  };
  
  //=====================tcp=======================
  static void *tcp_server_init(const char *host, uint16_t port)
  {
      int fd;
      struct sockaddr_in si;
      socklen_t len = sizeof(si);
  
      si.sin_family = AF_INET;
      si.sin_addr.s_addr = host ? inet_addr(host) : INADDR_ANY;
      si.sin_port = htons(port);
      fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (-1 == fd) {
          printf("socket: %s\n", strerror(errno));
          return NULL;
      }
      if (-1 == bind(fd, (struct sockaddr*)&si, sizeof(si))) {
          printf("bind: %s\n", strerror(errno));
          close(fd);
          return NULL;
      }
      if (-1 == listen(fd, SOMAXCONN)) {
          printf("listen: %s\n", strerror(errno));
          close(fd);
          return NULL;
      }
  
      int *afd = (int *)calloc(1, sizeof(int));
      *afd = accept(fd, (struct sockaddr *)&si, &len);
      if (*afd == -1) {
          printf("accept: %s\n", strerror(errno));
          return NULL;
      }
      return (void *)afd;
  }
  
  static void *tcp_client_init(const char *host, uint16_t port)
  {
      int *fd = (int *)calloc(1, sizeof(int));
      struct sockaddr_in si;
      *fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (-1 == *fd) {
          printf("socket: %s\n", strerror(errno));
          return NULL;
      }
      si.sin_family = AF_INET;
      si.sin_addr.s_addr = inet_addr(host);
      si.sin_port = htons(port);
  
      if (-1 == connect(*fd, (struct sockaddr*)&si, sizeof(si))) {
          printf("connect: %s\n", strerror(errno));
          close(*fd);
          return NULL;
      }
      return (void *)fd;
  }
  
  static int tcp_send(void *arg, void *buf, size_t len)
  {
      int *fd = (int *)arg;
      return send(*fd, buf, len, 0);
  }
  
  static int tcp_recv(void *arg, void *buf, size_t len)
  {
      int *fd = (int *)arg;
      return recv(*fd, buf, len, 0);
  }
  
  static int tcp_close(void *arg)
  {
      int *fd = (int *)arg;
      return close(*fd);
  }
  
  static int tcp_errno(void *arg)
  {
      return errno;
  }
  
  //=================udp=======================
  static void *udp_server_init(const char *host, uint16_t port)
  {
      int *fd = (int *)calloc(1, sizeof(int));
      struct sockaddr_in si;
      *fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (-1 == *fd) {
          printf("socket: %s\n", strerror(errno));
          return NULL;
      }
      si.sin_family = AF_INET;
      si.sin_addr.s_addr = inet_addr(host);
      si.sin_port = htons(port);
  
      if (-1 == bind(*fd, (struct sockaddr*)&si, sizeof(si))) {
          printf("connect: %s\n", strerror(errno));
          close(*fd);
          return NULL;
      }
      return (void *)fd;
  }
  
  static void *udp_client_init(const char *host, uint16_t port)
  {
      int *fd = (int *)calloc(1, sizeof(int));
      struct sockaddr_in si;
      *fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
      if (-1 == *fd) {
          printf("socket: %s\n", strerror(errno));
          return NULL;
      }
      si.sin_family = AF_INET;
      si.sin_addr.s_addr = inet_addr(host);
      si.sin_port = htons(port);
  
      if (-1 == connect(*fd, (struct sockaddr*)&si, sizeof(si))) {
          printf("connect: %s\n", strerror(errno));
          close(*fd);
          return NULL;
      }
      return (void *)fd;
  }
  
  static int udp_send(void *arg, void *buf, size_t len)
  {
      int *fd = (int *)arg;
      return send(*fd, buf, len, 0);
  }
  
  static int udp_recv(void *arg, void *buf, size_t len)
  {
      int *fd = (int *)arg;
      return recv(*fd, buf, len, 0);
  }
  
  static int udp_close(void *arg)
  {
      int *fd = (int *)arg;
      return close(*fd);
  }
  
  static int udp_errno(void *arg)
  {
      return errno;
  }
  
  //=================ptcp=======================
  static void *ptcp_server_init(const char *host, uint16_t port)
  {
      ptcp_socket_t *ps = ptcp_socket();
      if (ps == NULL) {
          printf("error!\n");
          return NULL;
      }
  
      struct sockaddr_in si;
      si.sin_family = AF_INET;
      si.sin_addr.s_addr = host ? inet_addr(host) : INADDR_ANY;
      si.sin_port = htons(port);
  
      ptcp_bind(ps, (struct sockaddr*)&si, sizeof(si));
      ptcp_listen(ps, 0);
      return ps;
  }
  
  static int _ptcp_recv(void *arg, void *buf, size_t len)
  {
      ptcp_socket_t *ps = (ptcp_socket_t *)arg;
      int ret = ptcp_recv(ps, buf, len);
      return ret;
  }
  
  static void *ptcp_client_init(const char *host, uint16_t port)
  {
      ptcp_socket_t *ps = ptcp_socket();
      if (ps == NULL) {
          printf("error!\n");
          return NULL;
      }
  
      struct sockaddr_in si;
      si.sin_family = AF_INET;
      si.sin_addr.s_addr = inet_addr(host);
      si.sin_port = htons(port);
      if (0 != ptcp_connect(ps, (struct sockaddr*)&si, sizeof(si))) {
          printf("ptcp_connect failed!\n");
      } else {
          printf("ptcp_connect success\n");
      }
      return ps;
  }
  
  static int _ptcp_send(void *arg, void *buf, size_t len)
  {
      ptcp_socket_t *ps = (ptcp_socket_t *)arg;
      return ptcp_send(ps, buf, len);
  }
  
  static int _ptcp_close(void *arg)
  {
      ptcp_socket_t *ps = (ptcp_socket_t *)arg;
      ptcp_close(ps);
      return 0;
  }
  
  #define ECLOSED 123
  static int ptcp_errno(void *arg)
  {
      ptcp_socket_t *ps = (ptcp_socket_t *)arg;
  
      if (ptcp_is_closed(ps) || ptcp_is_closed_remotely(ps)) {
          return ECLOSED;
      }
      return ptcp_get_error(ps);
  }
  
  static int file_send(char *name, struct xfer_callback *cbs)
  {
      int len, flen, slen, total;
      char buf[1024] = {0};
      FILE *fp = fopen(name, "r");
      assert(fp);
      total = flen = filesize(fp);
      void *arg = cbs->xfer_client_init("127.0.0.1", 5555);
  
      sleep(1);
      while (flen > 0) {
          usleep(20 * 1000);
          len = fread(buf, 1, sizeof(buf), fp);
          if (len == -1) {
              printf("%s:%d xxxx\n", __func__, __LINE__);
              break;
          }
          slen = cbs->xfer_send(arg, buf, len);
          if (slen <= 0) {
              printf("xfer_send error: %d\n", cbs->xfer_errno(arg));
              break;
          }
          if (slen != len) {
              printf("%s:%d slen=%d, len=%d\n", __func__, __LINE__, slen, len);
          }
          flen -= len;
          printf("already sent %s file %dKB/%dKB = %02f%%\n", name, (total-flen), total, (total-flen)/(total*1.0)*100);
      }
      sleep(1);
      cbs->xfer_close(arg);
      fclose(fp);
      printf("file %s length is %u\n", name, flen);
      return total;
  }
  static int file_recv(char *name, struct xfer_callback *cbs)
  {
      int len, flen, rlen;
      char buf[1024] = {0};
      FILE *fp = fopen(name, "w");
      assert(fp);
      void *arg = cbs->xfer_server_init("127.0.0.1", 5555);
      assert(arg);
  
      flen = 0;
      while (1) {
          usleep(10 * 1000);
          memset(buf, 0, sizeof(buf));
          rlen = cbs->xfer_recv(arg, buf, sizeof(buf));
          if (rlen <= 0) {
              if (ECLOSED == cbs->xfer_errno(arg)){
                  printf("xfer closed\n");
                  break;
              } else {
                  printf("%s:%d errno:%d\n", __func__, __LINE__, cbs->xfer_errno(arg));
                  usleep(100 * 1000);
                  continue;
              }
          }
          len = fwrite(buf, 1, rlen, fp);
          assert(len==rlen);
          flen += len;
          printf("already recv %s file %dKB\n", name, flen);
      }
      cbs->xfer_close(arg);
      fclose(fp);
      return flen;
  }
  
  static void sigterm_handler(int sig)
  {
      exit(0);
  }
  
  static struct xfer_callback tcp_cbs = {
      tcp_server_init,
      tcp_client_init,
      tcp_send,
      tcp_recv,
      tcp_close,
      tcp_errno
  };
  static struct xfer_callback udp_cbs = {
      udp_server_init,
      udp_client_init,
      udp_send,
      udp_recv,
      udp_close,
      udp_errno
  };
  static struct xfer_callback ptcp_cbs = {
      ptcp_server_init,
      ptcp_client_init,
      _ptcp_send,
      _ptcp_recv,
      _ptcp_close,
      ptcp_errno
  };
  int main(int argc, char **argv)
  {
      if (argc != 3) {
          printf("./test -s / -r filename\n");
          return 0;
      }
      signal(SIGPIPE,SIG_IGN);
      signal(SIGINT, sigterm_handler);
      struct xfer_callback *cbs;
      cbs = &tcp_cbs;
      cbs = &udp_cbs;
      cbs = &ptcp_cbs;
      if (!strcmp(argv[1], "-s")) {
          file_send(argv[2], cbs);
      }
      if (!strcmp(argv[1], "-r")) {
          file_recv(argv[2], cbs);
      }
      return 0;
  }
  