C++之socket编程

环境搭建

  1. 开发环境- Clion
    本地安装了vs2017,可以选择使用vs自带的C++编译器,如下图:

  对于vc++

  1. 引入库文件
    socket依赖winsocket.h、winsocket.lib和winsocket.dll
    在CMakeLists中:
#添加头文件搜索路径
link_libraries(ws2_32 wsock32)

全部配置:

cmake_minimum_required(VERSION 3.16)
project(demo2)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")

#add_executable( )
#添加头文件搜索路径
link_libraries(ws2_32 wsock32)


# 遍历项目根目录下所有的 .cpp 文件
#file (GLOB_RECURSE files *.cpp)
#foreach (file ${files})
#    string(REGEX REPLACE ".+/(.+)\\..*" "\\1" exe ${file})
#    add_executable (${exe} ${file} socketDemo/msg_client.cpp overr/overmain.cpp)
#    message (\ \ \ \ --\ src/${exe}.cpp\ will\ be\ compiled\ to\ bin/${exe})
#endforeach ()

add_executable(main main.cpp)
add_executable(client socketDemo/msg_client.cpp)
add_executable(server socketDemo/msg_server.cpp)

#链接gcc-g++静态库
#target_link_libraries(demo2 libgcc.a)
#target_link_libraries(demo2 libstdc++.a)
#链接网络库(如需要)
#target_link_libraries(demo2 ws2_32)
#链接线程库(必须放到最后)
#target_link_libraries(demo2 libpthread.a)

获取网络中计算机的IP地址和计算机名

流式套接字编程

  网络数据的传输是靠套接字实现的。套接字有三种类型:流式套接字(socket_stream)、数据包套接字(socket_dgram)和原始套接字(RAW)。

  流式套接字是面向连接的,提供双向、有序、无重复且记录无边界的数据流服务。适用于处理大数据量,开销大

服务器端编程步骤

  1. 初始化阶段调用函数WSAStartup
      初始化Windows Sockets DLL,只有初始化成功后,才能调用其他Windows API函数。
void WSAStartup(
    _In_ WORD wVersionRequested,  // 所使用的win socket版本
    _Out_ LPWSADATA lpWSAData //储存系统返回的winSocket信息
    );
  1. 建立socket
      初始化动态链接后,在服务端建立一个监听socket
socket(
    _In_ int af,  // 目前提供PF_INET(AF_INET)
    _In_ int type,  // socket type, e.g. socket_stream(TCP),socket_dgram(UDP)
    _In_ int protocol  // 通讯协议,默认值0
    );

所有的WSASocket函数都可以通过调用 WSAGetLastError()获取上次错误

  1. 绑定端口
bind(
    _In_ SOCKET s,
    _In_reads_bytes_(namelen) const struct sockaddr FAR * name,  // socket地址,IP
    _In_ int namelen  // name的长度
    );

服务器端程序:

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ws2tcpip.h>

using namespace std;

#define BUF_SIZE 1024

void initialization();

int main()
{
    //定义长度变量
    int send_len = 0;
    int recv_len = 0;
    int len = 0;
    //定义发送缓冲区和接受缓冲区
    char send_buf[100];
    char recv_buf[100];
    //定义服务端套接字,接受请求套接字
    SOCKET s_server;
    SOCKET s_accept;
    //服务端地址客户端地址
    SOCKADDR_IN server_addr;
    SOCKADDR_IN accept_addr;
    initialization();
    //填充服务端信息
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(5010);
    //创建套接字
    s_server = socket(AF_INET, SOCK_STREAM, 0);
    if (bind(s_server, (SOCKADDR *) &server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
    {
        cout << "套接字绑定失败!" << endl;
        WSACleanup();
    } else
    {
        cout << "套接字绑定成功!" << endl;
    }
    //设置套接字为监听状态
    if (listen(s_server, SOMAXCONN) < 0)
    {
        cout << "设置监听状态失败!" << endl;
        WSACleanup();
    } else
    {
        cout << "设置监听状态成功!" << endl;
    }
    cout << "服务端正在监听连接,请稍候...." << endl;
    //接受连接请求
    len = sizeof(SOCKADDR);
    s_accept = accept(s_server, (SOCKADDR *) &accept_addr, &len);
    if (s_accept == SOCKET_ERROR)
    {
        cout << "连接失败!" << endl;
        WSACleanup();
        return 0;
    }
    cout << "连接建立,准备接受数据" << endl;
    //接收数据
    while (1)
    {
        recv_len = recv(s_accept, recv_buf, 100, 0);
        if (recv_len < 0)
        {
            cout << "接受失败!" << endl;
            break;
        } else
        {
            cout << "客户端信息:" << recv_buf << endl;
        }
//        cout << "请输入回复信息:";
//        cin >> send_buf;
        send_len = send(s_accept, "ok..", 100, 0);
        if (send_len < 0)
        {
            cout << "发送失败!" << endl;
            break;
        }
    }
    //关闭套接字
    closesocket(s_server);
    closesocket(s_accept);
    //释放DLL资源
    WSACleanup();
    return 0;
}

void initialization()
{
    //初始化套接字库
    WORD w_req = MAKEWORD(2, 2);//版本号
    WSADATA wsadata;
    int err;
    err = WSAStartup(w_req, &wsadata);
    if (err != 0)
    {
        cout << "初始化套接字库失败!" << endl;
    } else
    {
        cout << "初始化套接字库成功!" << endl;
    }
    //检测版本号
    if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2)
    {
        cout << "套接字库版本号不符!" << endl;
        WSACleanup();
    } else
    {
        cout << "套接字库版本正确!" << endl;
    }
    //填充服务端地址信息

}

客户端程序:

#include <iostream>
#include <string>
#include <WinSock2.h>

using namespace std;

void initialization2();
void client();
int main()
{
    client();
    system("pause");
    return 0;
}

void client()
{
    //定义长度变量
    int send_len = 0;
    int recv_len = 0;
    //定义发送缓冲区和接受缓冲区
    char send_buf[100];
    char recv_buf[100];
    //定义服务端套接字,接受请求套接字
    SOCKET s_server;
    //服务端地址客户端地址
    SOCKADDR_IN server_addr;
    initialization2();
    //填充服务端信息
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    server_addr.sin_port = htons(5010);
    //创建套接字
    s_server = socket(AF_INET, SOCK_STREAM, 0);
    if (connect(s_server, (SOCKADDR *) &server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
    {
        cout << "服务器连接失败!" << endl;
        WSACleanup();
    } else
    {
        cout << "服务器连接成功!" << endl;
    }

    //发送,接收数据
    while (1)
    {
        cout << "请输入发送信息:";
        cin >> send_buf;
        send_len = send(s_server, "give me", 100, 0);
        if (send_len < 0)
        {
            cout << "发送失败!" << endl;
            break;
        }
        recv_len = recv(s_server, recv_buf, 100, 0);
        if (recv_len < 0)
        {
            cout << "receive err" << endl;
            break;
        }
        else
        {
            cout << "服务端信息:" << recv_buf << endl;
        }

    }
    //关闭套接字
    closesocket(s_server);
    //释放DLL资源
    WSACleanup();
}

void initialization2()
{
    //初始化套接字库
    WORD w_req = MAKEWORD(2, 2);//版本号
    WSADATA wsadata;
    int err;
    err = WSAStartup(w_req, &wsadata);
    if (err != 0)
    {
        cout << "初始化套接字库失败!" << endl;
    } else
    {
        cout << "初始化套接字库成功!" << endl;
    }
    //检测版本号
    if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2)
    {
        cout << "套接字库版本号不符!" << endl;
        WSACleanup();
    } else
    {
        cout << "套接字库版本正确" << endl;
    }
    //填充服务端地址信息

}

静态编译

为了让程序能够顺利移植到其他平台,需要静态链接
参考
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static") :必须

cmake_minimum_required(VERSION 3.16)
project(demo3)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static")


add_executable(demo3 main.cpp)

#链接静态库
#链接boost静态库(project_Name为你的项目名,*分别代表库名、mingw版本号、boost库版本号,如:libboost_system-mgw72-mt-s-1_65_1.a)
#target_link_libraries(demo3 libboost_*-mgw*-mt-s-*.a)
#链接gcc-g++静态库
target_link_libraries(demo3 libgcc.a)
target_link_libraries(demo3 libstdc++.a)
#链接网络库(如需要)
target_link_libraries(demo3 ws2_32)
#链接线程库(必须放到最后)
target_link_libraries(demo3 libpthread.a)
posted @ 2020-07-19 22:23  fight139  阅读(1356)  评论(0编辑  收藏  举报