Window下CLion实现本机通过socket通信-C++

1.引言-什么是socket

socket即套接字,用于描述地址和端口,是一个通信链的句柄。应用程序通过socket向网络发出请求或者回应。

sockets(套接字)编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW);前两种较常用。基于TCP的socket编程是采用的流式套接字

有可能多种协议使用同一种数据传输方式,所以在socket编程中,需要同时指明数据传输方式和协议。

2. socket常用函数

2.1 sockaddr_in

struct sockaddr_in这个结构体用来处理网络通信的地址

struct sockaddr_in {
    short            sin_family;       // 2 bytes e.g. AF_INET, AF_INET6
    unsigned short   sin_port;    //16位 2 bytes e.g. htons(3490)
    struct in_addr   sin_addr;     //32位 4 bytes see struct in_addr, below
    char             sin_zero[8];     // 8 bytes zero this if you want to
};
//另一个结构体 in_addr存放32位ip地址
struct in_addr {
    unsigned long s_addr;          // 4 bytes load with inet_pton()
};

端口号需要用 htons() 函数转换

2.2 htons()、 inet_addr()和inet_ntoa()

htons()作用是将端口号由主机字节序转换为网络字节序的整数值。(host to net)

inet_addr()作用是将一个IP字符串转化为一个网络字节序的整数值,用于sockaddr_in.sin_addr.s_addr。

inet_ntoa()作用是将一个sin_addr结构体输出成IP字符串(network to ascii)。比如:

printf("%s",inet_ntoa(mysock.sin_addr));

htonl()作用和htons()一样,不过它针对的是32位的(long),而htons()针对的是两个字节,16位的(short)。

与htonl()和htons()作用相反的两个函数是:ntohl()和ntohs()。

2.3 socket()

创建套接字

int socket(int af, int type, int protocol);

afIP地址的类型

  • AF_INET : IPv4

  • AF_INET6: IPV6

type:数据传输方式

  • SOCK_STREAM:面向连接的数据传输方式

  • SOCK_DGRAM:无连接的数据传输方式

protocol:传输协议

  • IPPROTO_TCP:TCP传输协议

  • IPPTOTO_UDP:UDP传输协议

返回值

  • 成功:0

  • 失败:-1

2.4 bind()

地址绑定,将套接字与地址关联

int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);

sockfd:socket文件描述符

addr:sockaddr 结构体变量的指针

addrlen:addr 变量的大小

返回值

  • 成功:0

  • 失败:-1

2.5 connect()

建立连接,创建与指定外部端口的连接

int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);

sockfd:socket文件描述符

addr:sockaddr 结构体变量的指针

addrlen:addr 变量的大小

返回值

  • 成功:0

  • 失败:-1

2.6 listen()

让套接字进入被动监听状态(指当没有客户端请求时,套接字处于”睡眠“状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求),使得一个进程可以接收其他进程的请求,从而成为一个服务器进程

int listen(int sockfd, int backlog);

sockfd:被监听的套接字的标识符

backlog:请求队列的最大长度(能存放多少个客户端请求)

  • 请求队列:当套接字正在处理客户端请求时,如果有新的请求进来,套接字将把新的请求放入缓冲区,再从缓冲区取出请求 ,此缓冲区称为请求队列

返回值

  • 成功:0

  • 失败:-1

2.7 accept()

在一个套接口接收一个连接,当套接字处于监听状态时,可以通过accept()函数来接受客户端请求,accept()会阻塞程序进行,直到有新的请求到来

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:服务器端套接字的标识符

addr:sockaddr 结构体变量的指针

addrlen:addr 变量的大小

返回值

  • 成功:返回接收到的套接字的描述符

  • 失败:-1

2.8 send()

发送数据,将数据由指定的socket传给对方主机

int send(int s, const void * msg, int len, unsigned int falgs);

s:以建立好连接的socket标识符

msg:发送的消息内容

len:发送内容的长度

falgs:一般设为0

返回值

  • 成功:返回实际传送出去的字符数

  • 失败:-1

2.9 recv()

接受数据,接收远端主机经过指定的socket传来的数据,并把数据存到buf指定的内存空间

int recv(int sock, void *buf, int len, unsigned int flags);

sock:接收端套接字描述符

buf:指定缓冲区,存放接收到的数据

len:缓冲区的长度

flags:一般设为0

返回值

  • 成功:返回接收到的字符数

  • 失败:-1

3.客户端/服务端模式

在TCP/IP(Transmission Control Protocol Internet Protocol / 传输控制协议和网际协议,TCP/IP是一种网络协议,由TCP和IP两个协议组成。它负责在计算机网络中传输数据,并确保数据传输的可靠性,同时确定数据在网络中的路径。TCP/IP是网络通信的基础,也是网络上最常用的协议之一)网络应用中,通信的两个进程相互作用的主要模式是客户/服务器模式,即客户端向服务器发出请求,服务器接收请求后,提供相应的服务。因此需要一种机制为希望通信的进程间建立联系,为二者的数据交换提供同步,这就是基于客户/服务端模式的TCP/IP。

3.1 什么是TCP

TCP有三个关键步骤:三次握手,传输确认和四次挥手

构建思路:

服务端:建立socket,声明自身的端口号和地址并绑定到socket,使用listen打开监听,然后不断用accept去查看是否有连接,如果有,捕获socket,并通过recv获取消息的内容,通信完成后调用closeSocket关闭这个对应accept到的socket,如果不再需要等待任何客户端连接,那么用closeSocket关闭掉自身的socket。

客户端:建立socket,通过端口号和地址确定目标服务器,使用Connect连接到服务器,send发送消息,等待处理,通信完成后调用closeSocket关闭socket。

3.2 编程步骤

(1)服务端

1、加载套接字库,创建套接字(WSAStartup()/socket());

2、绑定套接字到一个IP地址和一个端口上(bind());

3、将套接字设置为监听模式等待连接请求(listen());

4、请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());

5、用返回的套接字和客户端进行通信(send()/recv());

6、返回,等待另一个连接请求;

7、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());

(2)客户端

1、加载套接字库,创建套接字(WSAStartup()/socket());

2、向服务器发出连接请求(connect());

3、和服务器进行通信(send()/recv());

4、关闭套接字,关闭加载的套接字库(closesocket()/WSACleanup());

3.3 学习代码

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

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

int main(int argc, char* argv[]) 
{
        WORD sockVersion = MAKEWORD(2, 2);
        WSADATA wsaData;
        if (WSAStartup(sockVersion, &wsaData) != 0)
        {
                return 0;
        }
        
        SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (slisten == INVALID_SOCKET)
        {
                printf("socket error !");
                return 0;
        }
        
        sockaddr_in sin;
        sin.sin_port = htons(8888);
        sin.sin_family = AF_INET;
        sin.sin_addr.S_un.S_addr = INADDR_ANY;
        if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
        {
                printf("bind error !");
        }
        
        if (listen(slisten, 5) == SOCKET_ERROR)
        {
                printf("listen error !");
                return 0;
        }
        
        SOCKET sClient;
        sockaddr_in remoteAddr;
        int nAddrlen = sizeof(remoteAddr);
        char revData[255];
        while (true)
        {
                printf("Wating for connecting... \r\n");
                sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
                if (sClient == INVALID_SOCKET)
                {
                        printf("accept error !");
                        continue;
                }
                printf("accept a connection: %s \r\n", inet_ntoa(remoteAddr.sin_addr));
                
                int ret = recv(sClient, revData, 255, 0);
                if (ret > 0)
                {
                        revData[ret] = 0x00;
                        printf(revData);
                }
                
                const char * sendData = "hello, TCP Client";
                send(sClient, sendData, strlen(sendData), 0);
                closesocket(sClient);
                 
        }
        
        closesocket(slisten);
        WSACleanup();
        return 0;
}

client.cpp

#include <WINSOCK2.H>
#include <STDIO.H>
#include <iostream>
#include <cstring>
using namespace std;
#pragma comment(lib, "ws2_32.lib")

int main()
{
    WORD sockVersion = MAKEWORD(2,2);
    WSADATA data;
    if (WSAStartup(sockVersion, &data) != 0)
    {
        return 0;
    }

    while (true)
    {
        SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (sclient == INVALID_SOCKET)
        {
            printf("Invalid socket!");
            return 0;
        }

        sockaddr_in serAddr;
        serAddr.sin_family = AF_INET;
        serAddr.sin_port = htons(8888);
        serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
        if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
        {
            printf("connect error!");
            closesocket(sclient);
            return 0;
        }

        string data;
        cin>>data;
        const char * sendData;
        sendData = data.c_str();
        send(sclient, sendData, strlen(sendData), 0);

        char recData[255];
        int ret = recv(sclient, recData, 255, 0);
        if (ret > 0)
        {
            recData[ret] = 0x00;
            printf(recData);
        }
        closesocket(sclient);
    }

    WSACleanup();
    return 0;
}

3.4 客户端一直发送数据代码

server.cpp

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

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

int main(int argc, char* argv[])
{
    WORD sockVersion = MAKEWORD(2,2);
    WSADATA wsadata;
    if (WSAStartup(sockVersion, &wsadata) != 0)
    {
        return 0;
    }

    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (slisten == INVALID_SOCKET)
    {
        printf("socket error!");
        return 0;
    }

    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(6000);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf("bind error!");
        return 0;
    }

    if (listen(slisten, 5) == SOCKET_ERROR)
    {
        printf("listen error!");
        return 0;
    }

    SOCKET sClient;
    sockaddr_in remoteAddr;
    int nAddrlen = sizeof(remoteAddr);
    char revData[255];
    while (true)
    {
        printf("Waiting for connecting... \n");
        sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
        if (sClient == SOCKET_ERROR)
        {
            printf("accept error !");
            continue;
        }
        int ret = recv(sClient, revData, 255, 0);
        if (ret > 0 )
        {
            revData[ret] = 0x00;
            printf("Received: %s\n", revData);
        }

        const char * sendData = "Hello World from Server";
        send(sClient, sendData, strlen(sendData), 0);
        printf("Sent: %s\n", sendData);

        // Keep receiving messages
        while (true)
        {
            ret = recv(sClient, revData, 255, 0);
            if (ret > 0)
            {
                revData[ret] = 0x00;
                printf("Received: %s\n", revData);
            }
        }

        closesocket(sClient);
    }

    closesocket(slisten);
    WSACleanup();
    return 0;
}

client.cpp

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

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

int main()
{
    WORD sockVersion = MAKEWORD(2,2);
    WSADATA data;
    if(WSAStartup(sockVersion, &data) != 0)
    {
        return 0;
    }

    SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sclient == INVALID_SOCKET)
    {
        printf("Invalid socket!");
        return 0;
    }

    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(6000);
    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
    {
        printf("connect error!");
        closesocket(sclient);
        return 0;
    }

    const char * sendData = "Hello World from Client";
    send(sclient, sendData, strlen(sendData), 0);
    printf("Sent: %s\n", sendData);

    char recData[255];
    int ret = recv(sclient, recData, 255, 0);
    if (ret > 0)
    {
        recData[ret] = 0x00;
        printf("Received: %s\n", recData);
    }

    // Keep sending messages
    while (true)
    {
        const char * helloData = "Hello";
        send(sclient, helloData, strlen(helloData), 0);
        printf("Sent: %s\n", helloData);

        // Sleep for 1 second
        Sleep(1000);
    }

    closesocket(sclient);
    WSACleanup();
    return 0;
}

3.5 用户在客户端输入数据代码

server.cpp

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

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

int main(int argc, char* argv[])
{
    WORD sockVersion = MAKEWORD(2,2);
    WSADATA wsadata;
    if (WSAStartup(sockVersion, &wsadata) != 0)
    {
        return 0;
    }

    SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (slisten == INVALID_SOCKET)
    {
        printf("socket error!");
        return 0;
    }

    sockaddr_in sin;
    sin.sin_family = AF_INET;
    sin.sin_port = htons(6000);
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    if(bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf("bind error!");
        return 0;
    }

    if (listen(slisten, 5) == SOCKET_ERROR)
    {
        printf("listen error!");
        return 0;
    }

    SOCKET sClient;
    sockaddr_in remoteAddr;
    int nAddrlen = sizeof(remoteAddr);
    char revData[255];

    while (true)
    {
        printf("Waiting for connecting... \n");
        sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
        if (sClient == SOCKET_ERROR)
        {
            printf("accept error !");
            continue;
        }

        // Keep receiving messages
        while (true)
        {
            int ret = recv(sClient, revData, 255, 0);
            if (ret > 0)
            {
                revData[ret] = 0x00;
                printf("Received: %s\n", revData);
            }
            else if (ret == 0) // Connection closed
            {
                printf("Client disconnected.\n");
                closesocket(sClient);
                break;
            }
            else if (ret == SOCKET_ERROR) // Error receiving
            {
                printf("recv error!\n");
                closesocket(sClient);
                break;
            }
        }
    }

    closesocket(slisten);
    WSACleanup();
    return 0;
}

client.cpp

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

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

int main()
{
    WORD sockVersion = MAKEWORD(2,2);
    WSADATA data;
    if(WSAStartup(sockVersion, &data) != 0)
    {
        return 0;
    }

    SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sclient == INVALID_SOCKET)
    {
        printf("Invalid socket!");
        return 0;
    }

    sockaddr_in serAddr;
    serAddr.sin_family = AF_INET;
    serAddr.sin_port = htons(6000);
    serAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
    {
        printf("connect error!");
        closesocket(sclient);
        return 0;
    }

    char sendData[255];

    while (true)
    {
        std::cout << "Enter message: ";
        std::cin.getline(sendData, 255);
        if (strlen(sendData) == 0) {
            continue;
        }
        send(sclient, sendData, strlen(sendData), 0);
        printf("Sent: %s\n", sendData);
    }

    closesocket(sclient);
    WSACleanup();
    return 0;
}

4.在CLion建立客户端和服务端

修改CLion配置如下图

切换到此,点击运行即可进行通信

5. 运行效果

  • 25
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Clion是一款常用的C/C++开发环境,它可以在Windows、Linux、Mac OS等操作系统上运行。要在Clion实现窗口,需要使用外部GUI库。常见的GUI库有Qt、wxWidgets、FLTK等。这里以Qt为例,介绍如何在Clion实现窗口。 首先,需要在Clion中安装Qt插件。打开Clion,点击File->Settings->Plugins,在搜索框中输入Qt,找到Qt Support插件并安装。 安装完插件后,在Clion中新建一个Qt项目。选择File->New Project->Qt,在弹出窗口中选择Qt Gui Application。然后按照向导一步步设置项目的名称、路径、Qt版本等。 创建好项目后,在项目的根目录下会出现一个CMakeLists.txt文件。编辑这个文件,添加如下代码: ``` find_package(Qt5 COMPONENTS Widgets REQUIRED) add_executable(MyApp main.cpp) target_link_libraries(MyApp Qt5::Widgets) ``` 这段代码的作用是告诉CMake找到Qt5的Widgets组件,并将其链接到MyApp可执行文件中。 接下来,打开main.cpp文件,在其中添加如下代码: ``` #include <QtWidgets/QApplication> #include <QtWidgets/QMainWindow> int main(int argc, char **argv) { QApplication app(argc, argv); QMainWindow mainWindow; mainWindow.show(); return app.exec(); } ``` 这段代码的作用是创建一个QApplication对象和一个QMainWindow对象,并显示MainWindow窗口。 编写完代码后,点击Build->Build Project编译项目。编译完成后,点击Run->Run 'MyApp'启动程序,就可以看到一个空白的MainWindow窗口了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值