windows c++ socket网络编程

本文详细介绍了在Windows平台上使用Winsock进行网络编程,包括WSAStartup的初始化、服务器的创建过程(包括创建、绑定、监听和接受连接)、客户端的连接与数据交换,以及代码示例和优化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

五、socket网络编程

5.1 整体需要注意的点

需要注意的点:

  • windows的头文件是<WinSock2.h>,并且需要加入

    #pragma comment(lib,"ws2_32.lib")
    
  • 必须先执行WSAStartup,如果直接执行socket,那么会创建失败。

    WSADATA wsaData;//接受//
    WORD wVerSion = MAKEWORD(2, 2);//版本号//
    if (WSAStartup(wVerSion, &wsaData))    //wsastartup()函数向操作系统说明,我们要用哪个库文件// wsastartup()主要就是进行相应的socket库绑定。 //初始化成功,返回0//
    {
        printf("WSAStartup\n");
        return 0;
    }
    
  • 另外需要加入下面命令,防止一些warning阻止程序运行。

    #define _WINSOCK_DEPRECATED_NO_WARNINGS 
    

5.2 创建服务器顺序:

  1. WSA的启动
  2. socket创建
  3. 绑定socket和ip
    1. 创建sockaddr_in结构体,把协议族(AF_INET),IP和port复制给该结构体。
    2. bind绑定,其中入参要将sockaddr_in强转为sockaddr。
  4. 监听socket,listen(),其作用是把socket从主动变为被动,从而成为一个服务器,可以使用accpet来接收连接。
  5. 1 以下可循环
    1. 接收连接,会产生新的fd。
    2. 接收数据
    3. 发送数据
  6. 关闭socket
//Serve端//
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include<stdio.h>
#include <iostream>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;
class TCP {
public:
    int Server() {
        WSADATA wsaData;//接受//
        WORD wVerSion = MAKEWORD(2, 2);//版本号//
        if (WSAStartup(wVerSion, &wsaData))    //wsastartup()函数向操作系统说明,我们要用哪个库文件// wsastartup()主要就是进行相应的socket库绑定。 //初始化成功,返回0//
        {
            printf("WSAStartup\n");
            return 0;
        }

        //PF_INET与AF_INET一样
        //1、用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族
        //2、套接字类型
        //3、套接字使用的协议
        //这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符
        SOCKET ServerFD;
        ServerFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        if (ServerFD == -1)    //INVALID_SOCKET = -1, 表示不是有效的套接字//
        {
            printf("socket 失败\n");
        }
        else {
            printf("Server sock fd = %d\n", ServerFD);
        }

        //2 绑定socket
        //2.1 先把协议族、ip和port放入sockaddr_in结构体
        string ip_addr = "127.0.0.1";
        int ip_port = 8090;

        sockaddr_in sockaddr;
        memset(&sockaddr, 0, sizeof(sockaddr));
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_addr.s_addr = inet_addr(ip_addr.c_str());
        sockaddr.sin_port = htons(ip_port);

        //2.2 把初始化好的sockaddr_in(sockaddr) 绑定到 socket(ServerFD)
        // 需要将sockaddr_in强转为sockaddr
        if (::bind(ServerFD, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
            printf("绑定失败!\n");
        }
        else {
            printf("绑定成功!\n");
        }

        //3 让该socket监听,使其真正成为服务器。才能使用accept接收连接。
        if (::listen(ServerFD, 1024) < 0) {
            printf("监听失败!\n");
        }
        else {
            printf("正在监听中...\n");
        }
        while (true) {
            //接收客户端连接
            SOCKET acceptFD;
            acceptFD = ::accept(ServerFD, nullptr, nullptr);
            if (acceptFD < 0) {
                printf("接收失败!\n");
                return 0;
            }
            //从连接的套接字中接收数据。
            char buf[1024] = { 0 };
            size_t len = ::recv(acceptFD, buf, sizeof(buf), 0);
            if (len < 0) {
                printf("接收数据失败!\n");
            }
            else {
                printf("len:%d\nmessage:%s\n", len, buf);
            }

            string response("I got the message!");
            size_t send_len = ::send(acceptFD, response.c_str(), sizeof(response), 0);
            if (send_len < 0) {
                printf("发送失败!");
            }
            else {
                printf("发送成功!");
            }


        }
        ::close(ServerFD);
        return 0;
    }
};

5.3 创建客户端顺序

  1. WSA初始化
  2. 创建客户端socket,socket
  3. 连接服务器,connect
  4. 以下可循环
    1. 发送数据
    2. 接收数据
  5. 关闭连接。
class TCPClient {

public:
    int Client() {
        WSADATA wsaData;//接受//
        WORD wVerSion = MAKEWORD(2, 2);//版本号//
        if (WSAStartup(wVerSion, &wsaData))    //wsastartup()函数向操作系统说明,我们要用哪个库文件// wsastartup()主要就是进行相应的socket库绑定。 //初始化成功,返回0//
        {
            printf("WSAStartup\n");
            return 0;
        }

        //1. 创建客户端fd
        SOCKET clientFD;
        clientFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        //2. 连接服务器
        // 2.1 初始化sockaddr_in
        string Server_IP = "127.0.0.1";
        int Server_Port = 8090;

        struct sockaddr_in sockaddr;
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_addr.s_addr = inet_addr(Server_IP.c_str());
        sockaddr.sin_port = htons(Server_Port);

        // 2.2 连接服务器
        if (::connect(clientFD, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
            printf("客户端连接失败!\n");
            return 0;
        }

        //3. 发送数据
        string request("hello Server");
        size_t len;
        len = ::send(clientFD, request.c_str(), sizeof(request), 0);
        if (len < 0) {
            printf("客户端发送数据失败!\n");
        }

        //4. 接收数据
        char buf[1024] = { 0 };
        len = ::recv(clientFD, buf, sizeof(buf), 0);
        if (len < 0) {
            printf("客户端接收数据失败!\n");
        }
        printf("客户端接收到数据\nlen:%d\nmessage:%s\n", len, buf);

        //关闭客户端
        ::closesocket(clientFD);
        return 0;
    }
};

5.4 测试程序

#include "TCPServer.h"//我把TCPClient也放在了这个文件下
#include <thread>
using namespace std;

int main() {
    thread t([]() {
        TCPServer ts;
        ts.Server();
        });
    TCPClient tc;
    tc.Client();
}

5.5 测试结果

在这里插入图片描述

5.6 第一次改进优化

把socket操作的部分封装到一起

//Serve端//
#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include<stdio.h>
#include <iostream>
#include <string>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

class Socket {
public:
    void initWSA() {
        WSADATA wsaData;//接受//
        WORD wVerSion = MAKEWORD(2, 2);//版本号//
        if (WSAStartup(wVerSion, &wsaData))    //wsastartup()函数向操作系统说明,我们要用哪个库文件// wsastartup()主要就是进行相应的socket库绑定。 //初始化成功,返回0//
        {
            printf("WSAStartup\n");
        }
    }
    SOCKET createSocket() {
        SOCKET SocketFD;
        SocketFD = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        if (SocketFD == -1)    //INVALID_SOCKET = -1, 表示不是有效的套接字//
        {
            printf("socket 失败\n");
        }
        else {
            printf("Server sock fd = %d\n", SocketFD);
        }
        return SocketFD;
    }

    //TCPServer使用
    void bindSocket(const SOCKET& SocketFD, string ip_addr = "127.0.0.1", int ip_port = 8090) {
        //2 绑定socket
        //2.1 先把协议族、ip和port放入sockaddr_in结构体

        sockaddr_in sockaddr;
        memset(&sockaddr, 0, sizeof(sockaddr));
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_addr.s_addr = inet_addr(ip_addr.c_str());
        sockaddr.sin_port = htons(ip_port);

        //2.2 把初始化好的sockaddr_in(sockaddr) 绑定到 socket(ServerFD)
        // 需要将sockaddr_in强转为sockaddr
        if (::bind(SocketFD, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
            printf("绑定失败!\n");
        }
        else {
            printf("绑定成功!\n");
        }
    }
    //TCPClient使用
    void connectSocket(const SOCKET& SocketFD, string ip_addr = "127.0.0.1", int ip_port = 8090) {
        struct sockaddr_in sockaddr;
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_addr.s_addr = inet_addr(ip_addr.c_str());
        sockaddr.sin_port = htons(ip_port);

        // 2.2 连接服务器
        if (::connect(SocketFD, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
            printf("客户端连接失败!\n");
        }
    }

    void listenSocket(const SOCKET& SocketFD) {
        if (::listen(SocketFD, 1024) < 0) {
            printf("监听失败!\n");
        }
        else {
            printf("正在监听中...\n");
        }
    }

};

class TCPServer :public Socket{
public:
    
    int Server() {
        //1. 初始化WSA
        initWSA();
        //2. 创建服务器socket,获取其fd
        SOCKET ServerFD = createSocket();
        //3. 绑定服务器socket和IP
        bindSocket(ServerFD);

        //3 让该socket监听,使其真正成为服务器。才能使用accept接收连接。
        listenSocket(ServerFD);
        SOCKET acceptFD;
        acceptFD = ::accept(ServerFD, nullptr, nullptr);
        while (true) {
            //接收客户端连接
            if (acceptFD < 0) {
                printf("接收失败!\n");
                return 0;
            }
            //从连接的套接字中接收数据。
            char buf[1024] = { 0 };
            size_t len = ::recv(acceptFD, buf, sizeof(buf), 0);
            if (len < 0) {
                printf("接收数据失败!\n");
            }
            else {
                printf("服务器端接收数据\nlen:%d\nmessage:%s\n", len, buf);
            }

            /*string response("hello client!");
            size_t send_len = ::send(acceptFD, response.c_str(), sizeof(response), 0);
            if (send_len < 0) {
                printf("发送失败!");
            }
            else {
                printf("服务器发送成功!");
            }*/
            //::closesocket(acceptFD);


        }
        ::closesocket(ServerFD);
        return 0;
    }
};

class TCPClient :public Socket{

public:
    int Client() {
        //1. 初始化WSA
        initWSA();

        //2. 创建客户端fd
        SOCKET clientFD = createSocket();

        //3. 连接服务器
        connectSocket(clientFD);

        int i = 0;

        //4. 发送数据
        while (true) {
            string request("hello Server");
            request = request + to_string(i++);
            size_t len;
            len = ::send(clientFD, request.c_str(), sizeof(request), 0);
            if (len < 0) {
                printf("客户端发送数据失败!\n");
            }

            ////5. 接收数据
            //char buf[1024] = { 0 };
            //len = ::recv(clientFD, buf, sizeof(buf), 0);
            //if (len < 0) {
            //    printf("客户端接收数据失败!\n");
            //}
            //printf("客户端接收到数据\nlen:%d\nmessage:%s\n", len, buf);
        }
        

        //关闭客户端
        ::closesocket(clientFD);
        return 0;
    }
};

5.7第二次改进

  • socket公共部分提出来
  • TCPServer只实现listenSocket、bindSocket和acceptSocket
  • TCPClient只实现connectSocket。

5.7.1 socket公共部分

#define _WINSOCK_DEPRECATED_NO_WARNINGS 
#include<stdio.h>
#include <iostream>
#include <string>
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")

using namespace std;

class Socket {
public:
    Socket():_ip(""),_port(0),_sockfd(0) {
        WSADATA wsaData;//接受//
        WORD wVerSion = MAKEWORD(2, 2);//版本号//
        if (WSAStartup(wVerSion, &wsaData))    //wsastartup()函数向操作系统说明,我们要用哪个库文件// wsastartup()主要就是进行相应的socket库绑定。 //初始化成功,返回0//
        {
            printf("WSAStartup\n");
        }
        _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

        if (_sockfd == -1)    //INVALID_SOCKET = -1, 表示不是有效的套接字//
        {
            printf("socket 失败\n");
        }
        else {
            printf("Server sock fd = %d\n", _sockfd);
        }
    }


    SOCKET getSockFD() {
        return _sockfd;
    }

    bool setIP(const string& ip) {
        _ip = ip;
        return true;
    }

    bool setPort(int port) {
        _port = port;
        return true;
    }

private:
    string _ip;
    int _port;
    SOCKET _sockfd;

};

5.7.2 TCPServer部分

class TCPServer :public Socket{
public:

    TCPServer():_socket(new Socket){
    }
    
    //TCPServer使用
    void bindSocket(string ip_addr = "127.0.0.1", int ip_port = 8090) {
        //2 绑定socket
        //2.1 先把协议族、ip和port放入sockaddr_in结构体

        sockaddr_in sockaddr;
        memset(&sockaddr, 0, sizeof(sockaddr));
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_addr.s_addr = inet_addr(ip_addr.c_str());
        sockaddr.sin_port = htons(ip_port);

        //2.2 把初始化好的sockaddr_in(sockaddr) 绑定到 socket(ServerFD)
        // 需要将sockaddr_in强转为sockaddr
        if (::bind(_socket->getSockFD(), (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
            printf("绑定失败!\n");
        }
        else {
            printf("绑定成功!\n");
        }
        _socket->setIP(ip_addr);
        _socket->setPort(ip_port);
    }




    void listenSocket(int backlog) {
        if (::listen(_socket->getSockFD(), backlog) < 0) {
            printf("监听失败!\n");
        }
        else {
            printf("正在监听中...\n");
        }
    }

    int Server() {
        ////1. 初始化WSA
        //initWSA();
        ////2. 创建服务器socket,获取其fd
        //SOCKET ServerFD = createSocket();
        //3. 绑定服务器socket和IP
        bindSocket();
        //3 让该socket监听,使其真正成为服务器。才能使用accept接收连接。
        auto serverfd = _socket->getSockFD();
        listenSocket(serverfd);
        SOCKET acceptFD;
        acceptFD = ::accept(serverfd, nullptr, nullptr);
        while (true) {
            //接收客户端连接
            if (acceptFD < 0) {
                printf("接收失败!\n");
                return 0;
            }
            //从连接的套接字中接收数据。
            char buf[1024] = { 0 };
            size_t len = ::recv(acceptFD, buf, sizeof(buf), 0);
            if (len < 0) {
                printf("接收数据失败!\n");
            }
            else {
                printf("服务器端接收数据\nlen:%d\nmessage:%s\n", len, buf);
            }

            /*string response("hello client!");
            size_t send_len = ::send(acceptFD, response.c_str(), sizeof(response), 0);
            if (send_len < 0) {
                printf("发送失败!");
            }
            else {
                printf("服务器发送成功!");
            }*/
            //::closesocket(acceptFD);


        }
        ::closesocket(serverfd);
        return 0;
    }
private:
    Socket *_socket;
};

5.7.3 TCPClient部分

class TCPClient :public Socket{

public:
    TCPClient() :_socket(new Socket){
    }


    //TCPClient使用
    void connectSocket(string ip_addr = "127.0.0.1", int ip_port = 8090) {
        struct sockaddr_in sockaddr;
        sockaddr.sin_family = AF_INET;
        sockaddr.sin_addr.s_addr = inet_addr(ip_addr.c_str());
        sockaddr.sin_port = htons(ip_port);

        // 2.2 连接服务器
        if (::connect(_socket->getSockFD(), (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0) {
            printf("客户端连接失败!\n");
        }
    }

    int Client() {
        SOCKET clientFD = _socket->getSockFD();
        //3. 连接服务器
        connectSocket();

        int i = 0;

        //4. 发送数据
        while (true) {
            string request("hello Server");
            request = request + to_string(i++);
            size_t len;
            len = ::send(clientFD, request.c_str(), sizeof(request), 0);
            if (len < 0) {
                printf("客户端发送数据失败!\n");
            }

            ////5. 接收数据
            //char buf[1024] = { 0 };
            //len = ::recv(clientFD, buf, sizeof(buf), 0);
            //if (len < 0) {
            //    printf("客户端接收数据失败!\n");
            //}
            //printf("客户端接收到数据\nlen:%d\nmessage:%s\n", len, buf);
        }
        

        //关闭客户端
        ::closesocket(clientFD);
        return 0;
    }

private:
    Socket *_socket;
};

5.7.4 测试代码

#include "TCP.h"
#include <thread>
#include <Windows.h>
using namespace std;

int main() {
    thread t([]() {
        TCPServer ts;
        ts.Server();
        });
    TCPClient tc;
    tc.Client();
    Sleep(2000);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆得儿不是猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值