windows网络编程

一、简单的网络编程

这里展示一下如何使用TCP连接。

服务器的代码:

#include<winsock2.h>  //记住,这个头文件一定要在最上面定义,否则报错
#include<Windows.h>
#include<iostream>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")  

int main() {

    WSADATA data{ 0 };

    WSAStartup(MAKEWORD(2, 2), &data); // 初始化Socket环境  

    SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建Socket对象  

    sockaddr_in serverAddr{ 0 };

    serverAddr.sin_family = AF_INET;

    serverAddr.sin_port = htons(8888); // 绑定到端口8888上  

    inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);    
    // 如果我们的sin_addr指示了某个固定地址,然后还绑定了,那么服务器就只会接受这个地址的信息。
    // 如果要接收所有地址的信息,要使用serverAddr.sin_addr.s_addr = INADDR_ANY;

    bind(server, (sockaddr*)&serverAddr, sizeof(serverAddr)); // 绑定Socket到指定地址和端口号上  

    listen(server, SOMAXCONN); // 进入监听状态,最多等待5个连接请求  

    // 循环接受客户端连接请求并处理通信过程...  

    sockaddr_in clientAddr{ 0 };

    int size = sizeof(clientAddr);

    while (1)
    {
        printf("等待客户端链接中……\n");

        SOCKET client = accept(server, (SOCKADDR*)&clientAddr, &size);

        char buff[0x100]{ 0 };

        recv(client, buff, 0x100, 0);

        printf("客户端说:%s\n", buff);

        send(client, "收到!\n", sizeof("收到!\n"), 0);
    }

    closesocket(server); // 关闭Socket对象,释放资源  

    WSACleanup(); // 清理Socket环境资源  

    return 0;

}

客户端代码:

#include<winsock2.h>  //记住,这个头文件一定要在最上面定义,否则报错
#include<Windows.h>
#include<iostream>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")  

int main() {

    WSADATA data{ 0 };

    WSAStartup(MAKEWORD(2, 2), &data); // 初始化Socket环境  

    SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建Socket对象  

    sockaddr_in serverAddr{ 0 };

    serverAddr.sin_family = AF_INET;

    serverAddr.sin_port = htons(8888); // 绑定到端口8888上  

    inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
    
    int result = connect(client, (sockaddr*)&serverAddr, sizeof(serverAddr));
    
    if (result != 0)
    {
        printf("连接服务器失败!\n");
    }

    send(client, "发消息了!\n", sizeof("发消息了!\n"), 0);

    char buff[0x100]{ 0 };

    recv(client, buff, 0x100, 0);

    printf("服务器说:%s\n", buff);

    system("pause");

    return 0;

}

二、多人聊天室

while (1)
    {
        printf("等待客户端链接中……\n");

        SOCKET client = accept(server, (SOCKADDR*)&clientAddr, &size);

        char buff[0x100]{ 0 };

        recv(client, buff, 0x100, 0);

        printf("客户端说:%s\n", buff);

        send(client, "收到!\n", sizeof("收到!\n"), 0);
    }

如果想打造一个多人聊天室,我们还得修改一下服务器的代码。多人聊天,A连接到服务器,B连接到服务器,他们两个得一直和服务器保持连接,那才能不断通信,对吧。我们来看上面这个代码,每一次客户端和服务器交流完,又会执行新的一个

SOCKET client = accept(server, (SOCKADDR*)&clientAddr, &size);

这会导致什么问题呢?就是我们之前通信的客户端,被覆盖掉了,永远找不回来了,相当于连接断掉了(虽然实际上没有,但都被覆盖了找不回来了和断开了有啥区别)。因此,我们可以使用线程,每连接一个客户端进来,都给他分配一个线程,这样就可以了。

服务器代码:

#include<winsock2.h>  //记住,这个头文件一定要在最上面定义,否则报错
#include<Windows.h>
#include<iostream>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#include<vector>
using namespace std;
vector<SOCKET>clientlist;

DWORD WINAPI recvMessage(LPVOID param)
{
    SOCKET client = (SOCKET)param;
    char buff[0x100]{ 0 };
    int recvsize = 0;
    while ((recvsize=recv(client,buff,0x100,0))>0)
    {
        for (int i = 0; i < clientlist.size(); i++)
        {
            if (clientlist[i] != client)
            {
                send(clientlist[i], buff, recvsize, 0);
            }
        }
    }

    for (int i = 0; i < clientlist.size(); i++)
    {
        if (clientlist[i] == client)
        {
            cout << "客户端" << client << "断开了连接" << endl;
            closesocket(clientlist[i]);
            clientlist.erase(clientlist.begin() + i);
        }
    }
    return 0;
}

int main() {

    WSADATA data{ 0 };

    WSAStartup(MAKEWORD(2, 2), &data); // 初始化Socket环境  

    SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建Socket对象  

    sockaddr_in serverAddr{ 0 };

    serverAddr.sin_family = AF_INET;

    serverAddr.sin_port = htons(8888); // 绑定到端口8888上  

    inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);    
    // 如果我们的sin_addr指示了某个固定地址,然后还绑定了,那么服务器就只会接受这个地址的信息。
    // 如果要接收所有地址的信息,要使用serverAddr.sin_addr.s_addr = INADDR_ANY;

    bind(server, (sockaddr*)&serverAddr, sizeof(serverAddr)); // 绑定Socket到指定地址和端口号上  

    listen(server, SOMAXCONN); // 进入监听状态,最多等待5个连接请求  

    // 循环接受客户端连接请求并处理通信过程...  

    sockaddr_in clientAddr{ 0 };

    int size = sizeof(clientAddr);

    while (1)
    {
        //套接字关闭:虽然代码中没有显式调用closesocket(client);来关闭套接字,但当accept函数再次被调用时,之前与客户端通信的套接字(client)由于作用域的限制而被丢弃(即它超出了其定义的范围),并且其资源最终会被操作系统回收。
        //这意味着之前的连接在逻辑上已经“关闭”,因为服务器没有保留与该客户端通信的套接字。
        SOCKET client = accept(server, (SOCKADDR*)&clientAddr, &size);

        clientlist.push_back(client);

        //因此创建了一个线程,专门去处理这个client,当recvMessage函数结束的时候,线程的资源也会被系统回收
        CreateThread(NULL, NULL, recvMessage, (LPVOID)client, NULL, NULL);

        printf("客户端%u连接到了服务器\n", client);
    }

    closesocket(server); // 关闭Socket对象,释放资源  

    WSACleanup(); // 清理Socket环境资源  

    return 0;

}

客户端代码:

#include<winsock2.h>  //记住,这个头文件一定要在最上面定义,否则报错
#include<Windows.h>
#include<iostream>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")  
using namespace std;

DWORD WINAPI recvMessage(LPVOID param)
{
    SOCKET client = (SOCKET)param;
    char buff[0x100]{ 0 };
    int recvsize = 0;
    while (recvsize = recv(client, buff, 0x100, 0) > 0)
    {
        printf("%s\n", buff);
    }
    return 0;
}

int main() {

    WSADATA data{ 0 };

    WSAStartup(MAKEWORD(2, 2), &data); // 初始化Socket环境  

    SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 创建Socket对象  

    sockaddr_in serverAddr{ 0 };

    serverAddr.sin_family = AF_INET;

    serverAddr.sin_port = htons(8888); // 绑定到端口8888上  

    inet_pton(AF_INET, "127.0.0.1", &serverAddr.sin_addr);
    
    int result = connect(client, (sockaddr*)&serverAddr, sizeof(serverAddr));
    
    if (result != 0)
    {
        printf("链接服务器失败!\n");
    }

    HANDLE hthread = CreateThread(NULL, NULL, recvMessage, (LPVOID)client, NULL, NULL);

    char buff[0x100]{ 0 };

    while (cin>>buff && strcmp(buff,"exit"))
    {
        printf("the len of buff is %d\n", strlen(buff));
        send(client, buff, strlen(buff) + 1, 0);
    }

    closesocket(client);
    CloseHandle(hthread);

    system("pause");

    return 0;

}

三、IOCP

现在还有一个问题,我们每有一个客户端连接进来,都会给他分配一个线程。但是,线程不是越多越好的,会有线程创建和销毁的开销、上下文切换的开销以及系统资源的限制(系统会限制你线程数量)等问题。那该怎么办呢?
我们就可以使用IOCP,这玩意相当于领导,我们的线程相当于员工。我们开公司,员工肯定不是越多越好,那员工的数量该怎么确定呢?我们根据经验得知,线程数量=处理器数量*2是最好的。那每一个线程员工该分配什么工作呢?因此,就要IOCP这个领导,来确定我们线程员工的工作了。
代码看这个吧:
https://www.cnblogs.com/tobetterlife/p/12171016.html

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值