c/c++一小時入門tcp/udp通訊

1、實現只能接受一個連接的tcp服務器

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define MAXLINE 4096

int main(int argc, char** argv) {
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    char buff[4096];
    int n;
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    if (listen(listenfd, 10) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    printf("=============waiting for client's request=============\n");
    while (1)
    {
        if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
            printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
            continue;
        }
        n = recv(connfd, buff, MAXLINE, 0);
        buff[n] = '\0';
        printf("recv msg from client: %s\n", buff);
        close(connfd);
    }
    close(listenfd);
    return 0;
}

在這裏插入圖片描述
用telnet連接你寫的服務端,顯示,it’s work。

2、通過fork實現能接受多個tcp連接的服務端

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#define MAXLINE 4096
#define MAX_CLIENT 1000

int main(int argc, char** argv) {
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    char buff[4096];
    int n;
    pid_t pid;
    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("create socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(6666);

    if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1) {
        printf("bind socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    if (listen(listenfd, MAX_CLIENT) == -1) {
        printf("listen socket error: %s(errno: %d)\n", strerror(errno), errno);
        return 0;
    }
    printf("=============waiting for client's request=============\n");
    while (1)
    {
        if ((connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1) {
            printf("accept socket error: %s(errno: %d)\n", strerror(errno), errno);
            exit(-1);
        }
        pid = fork();
        printf("listenfd: %d", listenfd);
        if (pid == 0) {
            close(listenfd);
            n = recv(connfd, buff, MAXLINE, 0);
            buff[n] = '\0';
            printf("recv msg from client: %s\n", buff);
            close(connfd);
            exit(0);
        } else if (pid > 0) {
            close(connfd);
        }
    }
    close(listenfd);
    return 0;
}

在這裏插入圖片描述

3、使用select實現tcp服務端

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

#define BUF_SIZE    1024

void print_sockaddr(struct sockaddr_in addr)
{
    // 保存點分十進制的地址
    char ip_address[INET_ADDRSTRLEN];
    int port;

    inet_ntop(AF_INET, &addr.sin_addr, ip_address, sizeof(ip_address));
    port = ntohs(addr.sin_port);
    printf("(%s:%d)\n", ip_address, port);
}

int main(int argc, char *argv[])
{
    int sfd = -1;
    struct addrinfo hints;
    struct addrinfo *result;
    struct addrinfo *rp;

    struct sockaddr_in client_addr;
    struct sockaddr_in peer_addr;
    fd_set read_fds;
    fd_set work_fds;

    struct timeval tout;
    int peer_addrlen;
    int client_addrlen;
    int optval;
    int max_sockfd;

    char recv_buf[BUF_SIZE];
    int port = 0;

    if (argc != 2) {
        fprintf(stderr, "usage: %s port\n", argv[0]);
        exit(1);
    }

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_family = AF_UNSPEC;        /* 允許IPv4 或者 IPv6 */
    hints.ai_socktype = SOCK_STREAM;    /* TCP */
    hints.ai_flags = AI_PASSIVE;
    hints.ai_protocol = 0;
    hints.ai_canonname = NULL;
    hints.ai_addr = NULL;
    hints.ai_next = NULL;

    int s = getaddrinfo(NULL, argv[1], &hints, &result);
    if (s != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(s));
        exit(EXIT_FAILURE);
    }
    for (rp = result; rp != NULL; rp = rp->ai_next) {
        sfd = socket(rp->ai_family, rp->ai_socktype,
                     rp->ai_protocol);
        if (sfd == -1)
            continue;

        if ((setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR,
                        &optval, sizeof (optval))) != 0)
            continue;

        if (bind(sfd, rp->ai_addr, rp->ai_addrlen) != 0)
            continue;

        if (listen (sfd, 5) != 0)
            continue;

        /* 成功 */
        break;
    }
    if (rp == NULL) {
        fprintf(stderr, "Could not bind\n");
        exit(EXIT_FAILURE);
    }
    freeaddrinfo(result);

    FD_ZERO(&read_fds);
    FD_SET(sfd, &read_fds);
    FD_SET(STDIN_FILENO, &read_fds);
    max_sockfd = sfd;

    while (1) {
        tout.tv_sec = 2;
        tout.tv_usec = 0;

        work_fds = read_fds;
        int ret = select (max_sockfd + 1, &work_fds, NULL, NULL, &tout);
        if (ret == 0) {
            continue;
        }
        if (ret == -1) {
            continue;
        }
        for (int i = 0; i < max_sockfd + 1; i++) {
            if (!FD_ISSET(i, &work_fds)) {
                continue;
            }

            int fd = i;
            if (fd == sfd) {
                client_addrlen = sizeof(client_addr);
                int cfd = accept(sfd, (struct sockaddr *)&client_addr,
                                 (socklen_t *)&client_addrlen);
                printf("accept fd:%d ", cfd);
                print_sockaddr(client_addr);

                if (cfd < 0) {
                    printf("accept cfd < 0!");
                    continue;
                }
                FD_SET(cfd, &read_fds);
                if (cfd > max_sockfd) {
                    max_sockfd = cfd;
                }
            } else {
                ssize_t num_read = recv(fd, recv_buf, sizeof(recv_buf), 0);
                if (num_read <= 0) {
                    printf ("client has left fd:%d\n", fd);
                    close(fd);
                    FD_CLR(fd, &read_fds);
                    continue;
                }

                recv_buf[num_read] = '\0';
                printf("receive %zd bytes: \"%s\" from fd:%d", num_read, recv_buf, fd);
                peer_addrlen = sizeof(peer_addr);
                getpeername(fd, (struct sockaddr *)&peer_addr, (socklen_t *)&peer_addrlen);
                print_sockaddr(peer_addr);

                send(fd, recv_buf, (size_t)num_read, 0);
            }
        }
    }

    return EXIT_SUCCESS;
}

在這裏插入圖片描述

4、實現tcp連接的客戶端

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#define PORT 6666
#define MAXDATASIZE 4096

int main(int argc, char *argv[])
{
    int sockfd, num;
    char buf[MAXDATASIZE];
    struct hostent *he;
    struct sockaddr_in server;
    if (argc != 2) {
        printf("Usage: %s <IP Address>\n", argv[0]);
        exit(1);
    }
    if ((he = gethostbyname(argv[1])) == NULL) {
        printf("gethostbyname() error\n");
        exit(1);
    }
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        printf("socket() error\n");
        exit(1);
    }
    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(PORT);
    server.sin_addr = *((struct in_addr *)he -> h_addr);
    if (connect(sockfd, (struct sockaddr *)&server, sizeof(server)) == -1) {
        printf("connect() error\n");
        exit(1);
    }
    char str[] = "horst\n";
    if ((num = send(sockfd, str, sizeof(str), 0)) == -1) {
        printf("send() error\n");
        exit(1);
    }
    if ((num = recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
        printf("recv() error\n");
        exit(1);
    }
    buf[num - 1] = '\0';
    printf("server message: %s\n", buf);
    close(sockfd);
    return 0;
}

不再使用telnet測試第三步的代碼,使用上方代碼作爲tcp client測試tcp連接,測試成功
在這裏插入圖片描述

5、實現udp服務端

udp相比tcp就更簡單了

#include <stdio.h>   
#include <sys/types.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <unistd.h>   
#include <errno.h>   
#include <string.h>   
#include <stdlib.h>   
  
#define SERV_PORT   8000   
  
int main()  
{  
  /* sock_fd --- socket文件描述符 創建udp套接字*/  
  int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if(sock_fd < 0)  
  {  
    perror("socket");  
    exit(1);  
  }  
  
  /* 將套接字和IP、端口綁定 */  
  struct sockaddr_in addr_serv;  
  int len;  
  memset(&addr_serv, 0, sizeof(struct sockaddr_in));  //每個字節都用0填充
  addr_serv.sin_family = AF_INET;//使用IPV4地址
  addr_serv.sin_port = htons(SERV_PORT);//端口
  /* INADDR_ANY表示不管是哪個網卡接收到數據,只要目的端口是SERV_PORT,就會被該應用程序接收到 */  
  addr_serv.sin_addr.s_addr = htonl(INADDR_ANY);  //自動獲取IP地址
  len = sizeof(addr_serv);  
    
  /* 綁定socket */  
  if(bind(sock_fd, (struct sockaddr *)&addr_serv, sizeof(addr_serv)) < 0)  
  {  
    perror("bind error:");  
    exit(1);  
  }  
  
    
  int recv_num;  
  int send_num;  
  char send_buf[20] = "i am server!";  
  char recv_buf[20];  
  struct sockaddr_in addr_client;  
  
  while(1)  
  {  
    printf("server wait:\n");  
      
    recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_client, (socklen_t *)&len);  
      
    if(recv_num < 0)  
    {  
      perror("recvfrom error:");  
      exit(1);  
    }  
  
    recv_buf[recv_num] = '\0';  
    printf("server receive %d bytes: %s\n", recv_num, recv_buf);  
  
    send_num = sendto(sock_fd, send_buf, recv_num, 0, (struct sockaddr *)&addr_client, len);  
      
    if(send_num < 0)  
    {  
      perror("sendto error:");  
      exit(1);  
    }  
  }  
    
  close(sock_fd);  
    
  return 0;  
}

到這步仍無法測試代碼是否運行成功,telnet默認是tcp協議,不支持udp,需要實現udp client才能測試

6、實現udp client

#include <stdio.h>   
#include <string.h>   
#include <errno.h>   
#include <stdlib.h>   
#include <unistd.h>   
#include <sys/types.h>   
#include <sys/socket.h>   
#include <netinet/in.h>   
#include <arpa/inet.h>   
   
  
#define DEST_PORT 8000   
#define DSET_IP_ADDRESS  "127.0.0.1"   
   
  
int main()  
{  
  /* socket文件描述符 */  
  int sock_fd;  
  
  /* 建立udp socket */  
  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);  
  if(sock_fd < 0)  
  {  
    perror("socket");  
    exit(1);  
  }  
    
  /* 設置address */  
  struct sockaddr_in addr_serv;  
  int len;  
  memset(&addr_serv, 0, sizeof(addr_serv));  
  addr_serv.sin_family = AF_INET;  
  addr_serv.sin_addr.s_addr = inet_addr(DSET_IP_ADDRESS);  
  addr_serv.sin_port = htons(DEST_PORT);  
  len = sizeof(addr_serv);  
  
    
  int send_num;  
  int recv_num;  
  char send_buf[20] = "hey, who are you?";  
  char recv_buf[20];  
      
  printf("client send: %s\n", send_buf);  
    
  send_num = sendto(sock_fd, send_buf, strlen(send_buf), 0, (struct sockaddr *)&addr_serv, len);  
    
  if(send_num < 0)  
  {  
    perror("sendto error:");  
    exit(1);  
  }  
    
  recv_num = recvfrom(sock_fd, recv_buf, sizeof(recv_buf), 0, (struct sockaddr *)&addr_serv, (socklen_t *)&len);  
    
  if(recv_num < 0)  
  {  
    perror("recvfrom error:");  
    exit(1);  
  }  
    
  recv_buf[recv_num] = '\0';  
  printf("client receive %d bytes: %s\n", recv_num, recv_buf);  
    
  close(sock_fd);  
    
  return 0;  
}


測試成功,udp server和client都正確實現了。上述代碼都沒有使用框架,框架底層基本都是這樣實現的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章