Linux TCP UDP 混合

基本的思路是这样的:在服务器端,有两个机器,一个对外开启 TCP 监听,然后把监听到的请求内容送到后面的另外一台,或者多台轮询机器上,内网之间使用 UDP,然后等待业务逻辑机器处理完成,这个地方可以做成异步的,然后再返回到用户。

即是这样: client <-TCP-> server_front <-UDP-> server_back

下面来看代码,首先是 server_front 的 demo

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <arpa/inet.h>

#include "struct.h"

int main() {
    int tcpfd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcpfd == -1) {
        perror("tcpfd socket");
        return -1;
    }
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(38080);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(tcpfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("tcpfd bind");
        return -1;
    }
    if (listen(tcpfd, 10) == -1) {
        perror("tcpfd listen");
        return -1;
    }
    
    // udp
    int udpfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (udpfd == -1) {
        perror("udpfd socket");
        return -1;
    }
    struct sockaddr_in udp_serv_addr;
    memset(&udp_serv_addr, 0, sizeof(udp_serv_addr));
    udp_serv_addr.sin_family = AF_INET;
    udp_serv_addr.sin_port = htons(8080);
    if (inet_pton(AF_INET, "10.1.164.18", &udp_serv_addr.sin_addr) <= 0) {
        perror("udpfd inet pton");
        return -1;
    }
    
    while (1) {
        int tcp_conn_fd = accept(tcpfd, NULL, NULL);
        if (tcp_conn_fd == -1) {
            perror("tcpfd accept");
            return -1;
        }
        char buff[2048];
        memset(buff, 0, sizeof(buff));
        int recvn = recv(tcp_conn_fd, buff, sizeof(buff), 0);
        if (recvn == -1) {
            perror("tcpfd recv");
            return -1;
        }
        printf("recv: [%s]\n", buff);
        //char html_resp[] = "HTTP/1.1 200 OK\r\n\r\ntcp udp mix";
        UDP_MSG udp_msg;
        memset(&udp_msg, 0, sizeof(udp_msg));
        strcpy(udp_msg.str1, "HTTP/1.1 200 OK\r\n\r\n");
        strcpy(udp_msg.str2, "send to udp for cat");
        if (sendto(udpfd, &udp_msg, sizeof(udp_msg), 0, (struct sockaddr*)&udp_serv_addr, sizeof(udp_serv_addr)) == -1) {
            perror("udp send to");
            return -1;
        }
        if (recvfrom(udpfd, &udp_msg, sizeof(udp_msg), 0, NULL, NULL) == -1) {
            perror("udp recv from");
            return -1;
        }
        if (send(tcp_conn_fd, udp_msg.cat, strlen(udp_msg.cat), 0) == -1) {
            perror("tcp send");
            return -1;
        }
        close(tcp_conn_fd);
    }
    close(tcpfd);
    return 0;
}

然后是内部的报文格式 struct.h

#ifndef STRUCT_H
#define STRUCT_H

typedef struct _UDP_MSG {
    int add1;
    int add2;
    int sum;
    char str1[128];
    char str2[128];
    char cat[256];
} UDP_MSG;

#endif /* STRUCT_H */

然后是后端的业务逻辑服务器

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

#include "struct.h"

#define MAX_LINE 1024
#define SERV_PORT 8080

int udp_serv();

int main() {
    return udp_serv();
}

int udp_serv() {
    int sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (sockfd == -1) {
        perror("socket");
        return -1;
    }
    
    struct sockaddr_in serv_addr;
    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = htons(SERV_PORT);

    if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
        perror("bind");
        return -1;
    }

    while (1) {
        struct sockaddr_in cli_addr;
        memset(&cli_addr, 0, sizeof(cli_addr));
        UDP_MSG msg;
        memset(&msg, 0, sizeof(msg));
        socklen_t cli_addr_len = sizeof(cli_addr);
        recvfrom(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cli_addr, &cli_addr_len);
        msg.sum = msg.add1 + msg.add2;
        strcpy(msg.cat, msg.str1);
        strcat(msg.cat, msg.str2);
        printf("msg.add1 is: %d\n", msg.add1);
        printf("msg.add2 is: %d\n", msg.add2);
        printf("msg.sum is: %d\n", msg.sum);
        printf("msg.str1 is: %s\n", msg.str1);
        printf("msg.str2 is: %s\n", msg.str2);
        printf("msg.cat is: %s\n", msg.cat);
        sendto(sockfd, &msg, sizeof(msg), 0, (struct sockaddr*)&cli_addr, cli_addr_len);
    }

    return 0;
}

编译一下,就可以测试了

$ wget http://localhost:38080/123 && nl 123
--16:05:03--  http://localhost:38080/123
           => `123.2'
Resolving localhost... 127.0.0.1
Connecting to localhost|127.0.0.1|:38080... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified

    [<=> ] 19            --.--K/s             

16:05:03 (3.62 MB/s) - `123.2' saved [19]

     1	send to udp for cat

期间,server_front 的输出应该是这样的

recv: [GET /123 HTTP/1.0
User-Agent: Wget/1.10.2
Accept: */*
Host: localhost:38080
Connection: Keep-Alive

]

后台业务逻辑服务器的输出应该是这样的

msg.add1 is: 0
msg.add2 is: 0
msg.sum is: 0
msg.str1 is: HTTP/1.1 200 OK


msg.str2 is: send to udp for cat
msg.cat is: HTTP/1.1 200 OK

send to udp for cat

Leave a Reply

Your email address will not be published. Required fields are marked *