MENU

服务器端

May 3, 2020 • 笔记

// server.cpp : 定义控制台应用程序的入口点。
//

#include <stdio.h>
#include <winsock2.h>
#include <tchar.h>

#pragma comment(lib,"ws2_32.lib")

#define MAX_NUM 10

typedef struct _client
{
    int port;
    SOCKET socketid;
    HANDLE rev_handle;

}CLIENT_S;


//全局变量区
HANDLE acpt_handle;
CLIENT_S g_client[MAX_NUM];
int g_num = 0;


void init_client()
{
    int i;
    for (i = 0; i < MAX_NUM; i++)
    {
        memset(&g_client[i], 0, sizeof(CLIENT_S));
    }
    g_num = 0;
}


void log_print(char* str)
{
    printf("[系统日志]:%s\n", str);
}

void message_print(char* str, int port)
{
    printf("[用户%d]:%s\n", port, str);
}

void del(int index)
{
    int i;
    for (i = index; i < g_num; i++)
    {
        memcpy(&g_client[i], &g_client[i + 1], sizeof(CLIENT_S));
    }
    memset(&g_client[g_num - 1], 0, sizeof(CLIENT_S));
    g_num--;
}


int getindex_bysocket(SOCKET s)
{
    int i;
    for (i = 0; i < g_num; i++)
    {
        if (g_client[i].socketid == s)
        {
            return i;
        }
    }
    return -1;
}

void send_all(char* sendData)
{
    int i;
    for (i = 0; i < g_num; i++)
    {
        send(g_client[i].socketid, sendData, (int)strlen(sendData), 0);
    }
}



int MyRevThread(LPVOID lpParam)
{
    SOCKET sClient = SOCKET(lpParam);


    char revData[255];
    char sendData[255];
    int keeplive = true;
    while (keeplive)
    {
        //接收数据
        int ret = recv(sClient, revData, 255, 0);
        //printf("Myret: %d\n",ret);
        if (ret > 0)
        {
            revData[ret] = 0x00;
            int index;
            index = getindex_bysocket(sClient);
            sprintf(sendData, "[用户%d]: %s", g_client[index].port, revData);
            send_all(sendData);
            message_print(revData, g_client[index].port);
        }
        else if (ret == -1)
        {
            int index;
            index = getindex_bysocket(sClient);
            char log[50];
            sprintf(log, "[用户%d] 断开链接!", g_client[index].port);
            log_print(log);

            closesocket(g_client[index].socketid);
            CloseHandle(g_client[index].rev_handle);
            keeplive = false;
            del(index);
            break;
        }

    }
    return 0;
}


int MyAcpThread(LPVOID lpParam)
{
    SOCKET slisten = SOCKET(lpParam);

    while (true)
    {
        SOCKET sClient;
        sockaddr_in remoteAddr;
        int nAddrlen = sizeof(remoteAddr);

        sClient = accept(slisten, (SOCKADDR*)&remoteAddr, &nAddrlen);
        if (sClient == INVALID_SOCKET)
        {
            log_print("接受到一个连接失败...!");
            return -1;
        }

        DWORD thID = 0;
        HANDLE handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyRevThread, (LPVOID)sClient, 0, (LPDWORD)&thID);
        if (handle == NULL)
        {
            log_print("消息接收线程创建失败!");
            return -1;
        }

        char log[50];
        sprintf(log, "[用户%d] 加入成功! 线程ID %d", remoteAddr.sin_port, thID);
        log_print(log);

        g_client[g_num].port = remoteAddr.sin_port;
        g_client[g_num].rev_handle = handle;
        g_client[g_num].socketid = sClient;
        g_num++;
    }

    return 0;
}



void start()
{
    //初始化WSA
    WORD sockVersion = MAKEWORD(2, 2);
    WSADATA wsaData;
    if (WSAStartup(sockVersion, &wsaData) != 0)
    {
        log_print("初始化WSA失败!");
        return;
    }

    log_print("初始化WSA完成!");

    //创建套接字
    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (slisten == INVALID_SOCKET)
    {
        log_print("创建套接字失败!");
        return;
    }

    log_print("创建套接字完成!");

    //绑定IP和端口
    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(8888);;
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        log_print("创建套接字失败!");
        return;
    }

    log_print("绑定IP和端口完成!");

    //开始监听
    if (listen(slisten, 5) == SOCKET_ERROR)
    {
        log_print("开始监听失败!");
        return;
    }


    HANDLE handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyAcpThread, (LPVOID)slisten, 0, NULL);
    if (handle == NULL)
    {
        log_print("客户端监听线程创建失败!");
        return;
    }
    acpt_handle = handle;
    log_print("开始监听...等待连接!");

    //closesocket(slisten);
    //WSACleanup();


}

void print_menu()
{
    printf("************************************\n");
    printf("*****************欢迎***************\n");
    printf("************************************\n\n");
    printf("************ 1、查看所有客户端********\n");
    printf("************ 0、关闭服务器并退出*****\n");
    printf("************************************\n");

}

void print_client(int index)
{
    printf("************************************\n");
    printf("索引:%d\t", index);
    printf("端口号:%d\t\n", g_client[index].port);
    printf("************************************\n");
}

void print_all()
{
    int optVal;
    int optLen = sizeof(int);


    int i;
    for (i = 0; i < g_num; i++)
    {
        print_client(i);
    }
}


int _tmain(int argc, _TCHAR* argv[])
{
    unsigned int ch;
    argc = 8080;
    init_client();
    start();
    while (true)
    {
        print_menu();
        scanf("%u", &ch);
        getchar();
        switch (ch)
        {
        case 0:
            send_all("goodbye");
            printf("退出!\n");
            WSACleanup();
            exit(0);
            break;
        case 1:
            print_all();
            break;

        default:
            printf("输入错误,重新选择!\n");
            break;
        }

    }
    return 0;
}
作者:NorthCity1984
出处:https://grimoire.cn/note/ih.html
版权:本文《服务器端》版权归作者所有
转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任

Last Modified: May 10, 2020