Windows下Socket服务器同时连接多客户端

https://blog.csdn.net/yao_hou/article/details/102728970

下面的代码是一个server对应多个client, 仅做测试demo, 如果是实际项目,需实际处理。

编译脚本CMakeList.txt

如果使用cmake编译,需要添加ws2_32库

link_libraries(ws2_32)

add_executable(select_client main.cpp)

服务端代码

服务器单线程启动,监听8000端口

#include <winsock2.h>
#include <iostream>

using namespace std;

int main() {
    // 初始化winsock的环境
    WSADATA wd;
    if (WSAStartup(MAKEWORD(2, 2), &wd) == SOCKET_ERROR) {
        cout << "WSAStartup  error:" << GetLastError() << endl;
        return 0;
    }

    // 1.创建监听套接字
    SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (sListen == INVALID_SOCKET) {
        cout << "socket  error:" << GetLastError() << endl;
        return 0;
    }

    // 2.绑定到ip与端口
    sockaddr_in serverSockAddr;
    serverSockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    serverSockAddr.sin_port = htons(8000);
    serverSockAddr.sin_family = AF_INET;
    int len = sizeof(sockaddr_in);
    if (bind(sListen, (SOCKADDR *) &serverSockAddr, len) == SOCKET_ERROR) {
        cout << "bind  error:" << GetLastError() << endl;
        return 0;
    }

    // 3.监听套接字
    if (listen(sListen, 5) == SOCKET_ERROR) {
        cout << "listen  error:" << GetLastError() << endl;
        return 0;
    }

    // 4. select开始了
    fd_set readSet;//定义一个读(接受消息)的集合
    FD_ZERO(&readSet);//初始化集合
    FD_SET(sListen, &readSet);

    // 不停的select才可以读取套接字的状态改变
    while (true) {
        fd_set tmpSet; // 定义一个临时的集合
        FD_ZERO(&tmpSet); // 初始化集合
        tmpSet = readSet; // 每次循环都是所有的套接字

        // 利用select选择出集合中可以读写的多个套接字,有点像筛选
        int ret = select(0, &tmpSet, NULL, NULL, NULL);//最后一个参数为NULL,一直等待,直到有数据过来
        if (ret == SOCKET_ERROR) {
            continue;
        }

        // 成功筛选出来的tmpSet可以发送或者接收的socket
        for (int i = 0; i < tmpSet.fd_count; ++i) {
            //获取到套接字
            SOCKET selectedSocket = tmpSet.fd_array[i];

            // 接收到客户端的链接
            if (selectedSocket == sListen) {
                SOCKET c = accept(selectedSocket, NULL, NULL);
                // fd_set集合最大值为64
                if (readSet.fd_count < FD_SETSIZE) {
                    //往集合中添加客户端套接字
                    FD_SET(c, &readSet);
                    cout << c << " logged in." << endl;

                    // 给客户端发送欢迎
                    char buf[100] = {0};
                    sprintf(buf, "hello from server", c);
                    send(c, buf, 100, 0);
                } else {
                    cout << "max 64 clients for now." << endl;
                }

            } else {
                // 接收客户端的数据
                char buf[100] = {0};
                ret = recv(selectedSocket, buf, 100, 0);
                if (ret == SOCKET_ERROR || ret == 0) {
                    closesocket(selectedSocket);
                    FD_CLR(selectedSocket, &readSet);
                    cout << selectedSocket << "logged off." << endl;
                } else {
                    cout << selectedSocket << " sent " << buf << endl;
                }
            }
        }
    }

    // 关闭监听套接字
    closesocket(sListen);

    // 清理winsock环境
    WSACleanup();

    return 0;
}

客户端启动,连接127.0.0.1:8000

#include<winsock2.h>//winsock2的头文件
#include<iostream>

using namespace std;

//勿忘,链接dll的lib
#pragma comment(lib, "ws2_32.lib")

int main() {
    //加载winsock2的环境
    WSADATA wd;
    if (WSAStartup(MAKEWORD(2, 2), &wd) != 0) {
        cout << "WSAStartup  error:" << GetLastError() << endl;
        return 0;
    }

    //1.创建流式套接字
    SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (s == INVALID_SOCKET) {
        cout << "socket  error:" << GetLastError() << endl;
        return 0;
    }

    //2.连接服务器
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8000);
    addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

    int len = sizeof(sockaddr_in);
    if (connect(s, (SOCKADDR *) &addr, len) == SOCKET_ERROR) {
        cout << "connect  error:" << GetLastError() << endl;
        return 0;
    }

    //3接收服务端的消息
    char buf[100] = {0};
    recv(s, buf, 100, 0);
    cout << buf << endl;

    //3随时给服务端发消息
    int ret = 0;
    do {
        char buf[100] = {0};
        cout << "Please input something:";
        cin >> buf;
        ret = send(s, buf, 100, 0);
    } while (ret != SOCKET_ERROR && ret != 0);

    //4.关闭监听套接字
    closesocket(s);

    //清理winsock2的环境
    WSACleanup();

    return 0;
}
posted @ 2021-03-15 16:30  codeRhythm  阅读(1044)  评论(0编辑  收藏  举报