Linux socket 程序示例

主线程读socket,子线程写socket
 #include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstdio>
#include <cstring>

void* chat_send(void* param) {
  // 以下操作没有改变默认选项
  pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
  pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, nullptr);

  int client_socket = *(int*)param;
  while (true) {
    char buf[1024];
    std::memset(buf, 0, sizeof(buf));
    std::scanf("%s", buf);
    send(client_socket, buf, strlen(buf), 0); // 此处是一个 cancellation point 可免去 pthread_testcancel();
  }
}

int main(int argc, char const* argv[]) {
  int server_socket = socket(AF_INET, SOCK_STREAM, 0);

  sockaddr_in server_address;
  server_address.sin_family = AF_INET;
  server_address.sin_port = htons(8081);
  server_address.sin_addr.s_addr = inet_addr("0.0.0.0");

  bind(server_socket, (sockaddr*)&server_address, sizeof(server_address));
  listen(server_socket, 1);

  int client_socket = accept(server_socket, nullptr, 0);
  if (client_socket < 0) {
    std::perror("accept failed");
  }

  pthread_t send_thread_id;
  pthread_create(&send_thread_id, nullptr, chat_send, &client_socket);

  while (true) {
    char buf[1024];
    int n = recv(client_socket, &buf, 1024, 0);
    if (n <= 0) {
      std::printf("recv %d, disconnect\n", n);
      break;
    }
    buf[n] = '\0';
    std::printf("\r[out:] >> %s\n", buf);
  }

  pthread_cancel(send_thread_id);
  close(client_socket);
  return 0;
}
多进程服务端
#include <errno.h>
#include <netinet/in.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>

#include <cstdio>
#include <cstring>

int main(int argc, char const *argv[]) {
  struct sockaddr_in server_address, client_address;
  socklen_t client_address_len;
  char buf[1024];

  auto s = socket(AF_INET, SOCK_STREAM, 0);
  
  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_address.sin_port = htons(8081);
  bind(s, (struct sockaddr *)&server_address, sizeof(server_address));
  listen(s, 1);

  while (true) {
    client_address_len = sizeof(client_address);
    auto client =
        accept(s, (struct sockaddr *)&client_address, &client_address_len);
    if (client == -1) {
      std::perror("servcer accept failed!");
      _exit(1);
    }

    std::printf("accept %d, client: %d\n",
                ntohl(client_address.sin_addr.s_addr), client);

    // 屏蔽 SIGCLD 信号,让内核回收子进程资源
    signal(SIGCLD, SIG_IGN);
    pid_t pid = fork();
    if (pid != 0) {
      std::printf("fork sub process %d\n", pid);
    } else {
      while (true) {
        // 使用 select 实现超时检测和检查是否断开
        fd_set read_fds;
        FD_ZERO(&read_fds);
        FD_SET(client, &read_fds);
        // struct timeval timeout;
        // timeout.tv_sec = 3;
        auto nselect = select(FD_SETSIZE, &read_fds, nullptr, nullptr, nullptr);
        if (nselect == -1) {
          perror(strcat("select failed", std::strerror(errno)));
        }
        if (nselect == 0) {
          printf("没有可读文件描述符\n");
          break;
        }
        auto nrecv = read(client, buf, sizeof(buf));
        if (nrecv == -1) {
          std::printf("nrecv: %ld\n", nrecv);
          perror(std::strerror(errno));
          goto NEXTACCEPT;
        } else if (nrecv == 0) {
          std::printf("nrecv: 0, 连接 %d 已断开\n", getpid());
          goto NEXTACCEPT;
        }

        buf[nrecv] = '\0';
        std::printf("recv %ld from %d : %s\n", nrecv, getpid(), buf);
      }
    NEXTACCEPT:
      close(client);
      _exit(0);
    }
  }

  return 0;
}
多线程服务端
#include <errno.h>
#include <netinet/in.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstdio>
#include <cstring>

void *socket_deal(void *);

int main(int argc, char const *argv[]) {
  struct sockaddr_in server_address, client_address;
  socklen_t client_address_len;

  auto s = socket(AF_INET, SOCK_STREAM, 0);
  server_address.sin_family = AF_INET;
  server_address.sin_addr.s_addr = htonl(INADDR_ANY);
  server_address.sin_port = htons(8081);
  bind(s, (struct sockaddr *)&server_address, sizeof(server_address));
  listen(s, 1);

  while (true) {
    client_address_len = sizeof(client_address);
    auto client =
        accept(s, (struct sockaddr *)&client_address, &client_address_len);
    if (client == -1) {
      std::perror("servcer accept failed!");
      _exit(1);
    }

    std::printf("accept %d, client: %d\n",
                ntohl(client_address.sin_addr.s_addr), client);

    int *param = new int;
    *param = client;
    pthread_t thread_id;
    pthread_create(&thread_id, nullptr, socket_deal, param);
    pthread_detach(thread_id);
    std::printf("create thread %ld to deal socket\n", pthread_self());
  }

  return 0;
}

void *socket_deal(void *client_point) {
  int client = *(int *)client_point;
  delete (int *)client_point;
  char buf[1024];

  while (true) {
    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(client, &read_fds);
    // struct timeval timeout;
    // timeout.tv_sec = 3;
    auto nselect = select(FD_SETSIZE, &read_fds, nullptr, nullptr, nullptr);
    if (nselect == -1) {
      perror(strcat("select failed", std::strerror(errno)));
    }
    if (nselect == 0) {
      printf("没有可读文件描述符\n");
      break;
    }
    auto nrecv = read(client, buf, sizeof(buf));
    if (nrecv == -1) {
      std::printf("nrecv: %ld\n", nrecv);
      perror(std::strerror(errno));
      goto NEXTACCEPT;
    } else if (nrecv == 0) {
      std::printf("nrecv: 0, 连接 %ld 已断开\n", pthread_self());
      goto NEXTACCEPT;
    }

    buf[nrecv] = '\0';
    std::printf("recv %ld from %ld : %s\n", nrecv, pthread_self(), buf);
  }
NEXTACCEPT:
  close(client);
  return nullptr;
}
客户端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>

#include <cstdio>
#include <cstring>

int main(int argc, char const *argv[]) {
  sockaddr_in server_address;
  char buf[1024];
  std::memset(buf, 0, 1024);

  int server = socket(AF_INET, SOCK_STREAM, 0);
  server_address.sin_family = AF_INET;
  server_address.sin_port = htons(8081);
  server_address.sin_addr.s_addr = inet_addr("127.0.0.1");

  if (connect(server, (sockaddr *)&server_address, sizeof(server_address)) <
      0) {
    std::perror("connect failed!");
    _exit(1);
  }

  while (true) {
    std::memset(buf, 0, 1024);
    printf("[input] >> ");
    std::scanf("%s", buf);

    int n = send(server, buf, strlen(buf), 0);
    printf("send's length: %d\n", n);
    if (n < 0) {
      std::perror("send failed!");
      close(server);
      _exit(1);
    }
  }

  close(server);
  return 0;
}
posted @ 2022-09-06 23:26  某某人8265  阅读(122)  评论(0编辑  收藏  举报