能夠完成開啓兩個線程分別來啓動兩個服務器,收到數據後發給特定的客戶端

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<unistd.h>
#include<ctype.h>
#include<arpa/inet.h>
#include <pthread.h>
#include <stdlib.h>
#include<pthread.h>


// =================================================
// =================================================
//                  宏定義
// =================================================
// =================================================
#define RECE_BUF_LEN 128 // receive buffer length
#define SEND_BUF_LEN 128 // send buffer length
#define PORT_1      0xa5a6 // port
#define PORT_2      0xa5a7 // port
#define PORT_3      0xa5a6 // port
#define SERVER_INIT_IP_1      "192.168.50.130"
#define SERVER_INIT_IP_2      "192.168.50.130"

#define CLIENT_INIT_IP      "192.168.50.129"

typedef struct {
    int* server_fd;
    int domain;
    int type;
    int protocol;
    struct sockaddr_in* server_addr;
    const char *strptr;
    uint16_t hostshort;
    int serverIndex;
}SERVER_INFO;

// =================================================
// =================================================
//                  全局變量
// =================================================
// =================================================
int g_uDebugFlag = 0;

// 服務器端文件描述符
int server_fd_1;
int server_fd_2;

// 發送數據的客戶端地址信息的結構體
struct sockaddr_in client_addr_from;
socklen_t socklen = sizeof(client_addr_from);

// 用於存儲接收到的數據
char receBuf[RECE_BUF_LEN];

// 用於存儲發送的數據
char sendBuf[SEND_BUF_LEN];


struct sockaddr_in client_addr;
// sockaddr數據結構struct sockaddr_in中包含了IP地址協議類型、IP地址和端口號
struct sockaddr_in server_addr_1;
struct sockaddr_in server_addr_2;

// =================================================
// =================================================
//                  函數
// =================================================
// =================================================
// 對struct sockaddr_in進行設置
int setSockaddr_in(struct sockaddr_in *pAddr, sa_family_t sin_family, const char *strptr, uint16_t hostshort){
    // 對socket數據結構內存初始化
    memset(pAddr, 0, sizeof(struct sockaddr_in));

    // printf("sizeof(struct sockaddr_in) = %d\n", sizeof(struct sockaddr_in));
    // printf("sizeof(pAddr) = %d\n", sizeof(pAddr));
    // printf("sizeof(*pAddr) = %d\n", sizeof(*pAddr));

    if(g_uDebugFlag){
        printf("%s at %d : hostlong = %s\n", __FUNCTION__, __LINE__, strptr);
    }

    (*pAddr).sin_family = sin_family;
    // 兩種配置綁定IP的方式
    // 方式一
    inet_pton(sin_family, strptr, &(*pAddr).sin_addr.s_addr);
    // 方式二
    
    (*pAddr).sin_port = htons(hostshort);
    // (*pAddr).sin_port = htons(PORT); // 這裏寫死,統一使用同一個端口號

    // =============================
    // 打印查看一下
    // =============================
    // inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
    char str[INET_ADDRSTRLEN] = {0};
    printf("set struct sockaddr_in IP: %s; port %d.\n", inet_ntop(AF_INET, &(*pAddr).sin_addr, str, sizeof(str)), ntohs((*pAddr).sin_port));
    // printf("str = %s\n", str);

    return 0;
}

int getSockaddr_in(struct sockaddr_in* pAddr){
    // inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
    char str[INET_ADDRSTRLEN] = {0};
    printf("get struct sockaddr_in IP: %s; port %d.\n", inet_ntop(AF_INET, &(*pAddr).sin_addr, str, sizeof(str)), ntohs((*pAddr).sin_port));
    // printf("str = %s\n", str);

    return 0;
}
// 默認初始化服務端
int serverInit(int* server_fd, int domain, int type, int protocol, struct sockaddr_in* server_addr, const char *strptr, uint16_t hostshort){
    // ==========================================================
    // 1. 創建socket
    // ==========================================================
    if((*server_fd = socket(domain, type, protocol)) == -1){
        printf("%s at %d : server socket created fail !!!\n", __FUNCTION__,__LINE__);
        return -1;
    }
    // ==========================================================
    // 2. 通過struct sockaddr_in 結構體設置服務器地址和監聽端口
    // ==========================================================
    if(setSockaddr_in(server_addr, domain, strptr, hostshort) != 0){
        printf("%s at %d : setSockaddr_in set fail !!!\n", __FUNCTION__,__LINE__);
        return -1;
    }

    // ==========================================================
    // 3. 使用bind()函數綁定監聽端口,將套接字文件描述符和地址類型
    //    變量(struct sockaddr_in)進行綁定
    // ==========================================================
    if(bind(*server_fd, (struct sockaddr*)server_addr, sizeof(struct sockaddr_in)) == -1){
        printf("%s at %d : bind fail !!!\n", __FUNCTION__,__LINE__);
        return -1;
    }
    return 0;
}

int serverSend(char* sendBuf, int sendBufLen, int serverIndex, int* server_fd){
    printf("=================== server_%d begin to send message =====================\n", serverIndex);
    // ==========================================================
    // 5. 向客戶端發送數據,是用sendto()函數向服務器主機發送數據
    // ==========================================================

    // ==========================================================
    // 這裏需要添加一個判斷,用於選擇發給誰
    // ==========================================================

    int sendLen = 0;
    sendLen = sendto(*server_fd, sendBuf, sendBufLen, 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
    
    // inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
    char str[INET_ADDRSTRLEN] = {0};
    printf("send to %s at port %d.\n", inet_ntop(AF_INET, &client_addr.sin_addr, str, sizeof(str)), ntohs(client_addr.sin_port));
    // printf("str = %s\n", str);

    printf("sendLen = %d\n", sendLen);
    if(sendLen <= 0){
        printf("%s at %d : sendLen  0 fail !!!\n", __FUNCTION__, __LINE__);
        return -1;
    }

    // =========================================================
    // =========================================================
    // 打印發送內容
    // =========================================================
    // =========================================================
    printf("sendBuf: %s\n", sendBuf);
    printf("=================== server_%d now send message over =====================\n", serverIndex);
    printf("\n");

    // =========================================================
    // =========================================================
    // 發送結束之後,需要把發送緩存清空
    // 如果不清空,如果再下次接收的個數少於之前的內容,
    // 那麼再次發送的時候,會把上次保留的也發出來
    // =========================================================
    // =========================================================
    memset(sendBuf, 0, sendBufLen);
    // ==========================================================
    // 6. 關閉套接字,是用close()函數釋放資源
    // 如果在這裏關閉套接字,那麼,server_fd就被釋放了,又因爲server_fd
    // 是全局變量,server_fd釋放後,就不再是socket了,recfrom就不會再
    // 阻塞了,就導致了無限循環
    // ==========================================================
    // close(server_fd);
    return 0;
}

int serverReceive(int* sockfd, int domain, int serverIndex){
    // ==========================================================
    // 4. 接收客戶端的數據,使用recvfrom()函數接收客戶端的網絡數據
    // ==========================================================
    while(1){
        printf("===================serverReceive_%d begin to receive message =====================\n", serverIndex);
        int receLen;
        receLen = recvfrom(*sockfd, receBuf, sizeof(receBuf), 0, (struct sockaddr*)&client_addr_from, &socklen);
        printf("receLen = %d\n", receLen);

        // inet_ntop調用成功時,這個指針就是inet_ntop函數的返回值
        char str[INET_ADDRSTRLEN] = {0};
        printf("receive from %s at port %d.\n", inet_ntop(domain, &client_addr_from.sin_addr, str, sizeof(str)), ntohs(client_addr_from.sin_port));
        // printf("str = %s\n", str);

        if(receLen <= 0){
            printf("%s at %d : receLen  0 fail !!!\n", __FUNCTION__,__LINE__);
            continue;
        }

        // =========================================================
        // =========================================================
        // 打印接收內容
        // =========================================================
        // =========================================================
        printf("receBuf = %s\n", receBuf);
        printf("===================serverReceive_%d now receive message over =====================\n", serverIndex);
        printf("\n");

        // if(setSockaddr_in(&client_addr, AF_INET, inet_ntop(AF_INET, &client_addr_from.sin_addr, str, sizeof(str)), ntohs(client_addr_from.sin_port)) != 0){
        //     printf("%s at %d : setSockaddr_in set fail !!!\n", __FUNCTION__,__LINE__);
        //     continue;
        // }

        // 指定發給特定客戶端
        if(setSockaddr_in(&client_addr, domain, CLIENT_INIT_IP, PORT_3) != 0){
            printf("%s at %d : setSockaddr_in set fail !!!\n", __FUNCTION__,__LINE__);
            continue;
        }

        // ===================================================
        // ===================================================
        // 注意:如果不serverSend(receBuf,需要在這裏
        //       把接收buf清空,不然輸出信息有誤。
        // memset(receBuf, 0, receLen);
        // ===================================================
        // ===================================================
        if(serverSend(receBuf, receLen, serverIndex, sockfd) != 0){
            printf("%s at %d : server_%d serverSend fail !!!\n", __FUNCTION__, __LINE__, serverIndex);
            continue;
        }
    }
    // ==========================================================
    // 5. 關閉套接字,是用close()函數釋放資源
    // ==========================================================
    close(*sockfd);
    return 0;
}

void* server(void* args){
    SERVER_INFO* info = (SERVER_INFO*) args;
    // 主要完成創建socket、初始化設置服務器地址和監聽端口、將套接字
    // socket id, 協議類,協議名,協議,struct sockaddr_in*,IP,端口號綁定監聽端口
    if(serverInit(info->server_fd, info->domain, info->type, info->protocol, info->server_addr, info->strptr, info->hostshort) != 0){
        printf("%s at %d : server_%d serverInit fail !!!\n", __FUNCTION__, __LINE__, info->serverIndex);
        return (void*)-1;
    }

    if(getSockaddr_in(info->server_addr) != 0){
        printf("%s at %d : server_%d getSockaddr_in server_addr fail !!!\n", __FUNCTION__, __LINE__, info->serverIndex);
        return (void*)-1;
    }

    if(serverReceive(info->server_fd, info->domain, info->serverIndex) != 0){
        printf("%s at %d : server_%d serverReceive fail !!!\n", __FUNCTION__, __LINE__, info->serverIndex);
        return (void*)-1;
    }
    return (void*)-1;
}

void main(){
        SERVER_INFO server_1 = {
        &server_fd_1,
        AF_INET,
        SOCK_DGRAM,
        0,
        &server_addr_1,
        CLIENT_INIT_IP,
        PORT_3,
        0,
    };

    server(&server_1);
}

// void main(){
//     SERVER_INFO server_1 = {
//         &server_fd_1,
//         AF_INET,
//         SOCK_DGRAM,
//         0,
//         &server_addr_1,
//         SERVER_INIT_IP_1,
//         PORT_1,
//         1,
//     };

//     SERVER_INFO server_2 = {
//         &server_fd_2,
//         AF_INET,
//         SOCK_DGRAM,
//         0,
//         &server_addr_2,
//         SERVER_INIT_IP_2,
//         PORT_2,
//         2,
//     };

//     // 表示th是一條新的線程,此時還沒有創建線程,
//     // 只是定義了一個變量,如同int a,還有沒給a複製。
//     pthread_t th1;
//     pthread_create(&th1, NULL, server, &server_1);
//     pthread_t th2;
//     pthread_create(&th2, NULL, server, &server_2);

//     // 注意,這裏th不需要帶&
//     pthread_join(th1, NULL);
//     pthread_join(th2, NULL);
// }

 

發佈了178 篇原創文章 · 獲贊 126 · 訪問量 50萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章