C++:C/S架构,通过TCP实现双向自由通信

首先写一个TCP.hpp的准备文件

#ifndef TCP_HPP
#define TCP_HPP

#include <iostream>
#include <cstring>
extern "C"
{
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <fcntl.h>
}

//设置套接字为非阻塞模式
int setNonBlocking(int &sockfd)
{
    //获取套接字当前状态      获取状态
    int flags = fcntl(sockfd, F_GETFL, 0);
    if (flags == -1)
    {
        return -1;
    }
    //将表示非阻塞模式的那一个标志位设置为1
    flags |= O_NONBLOCK;
    //                设置状态
    if (fcntl(sockfd, F_SETFL, flags) == -1)
    {
        return -1;
    }
    return 0;
}

class socketSend
{
public:
    bool running;
    socketSend(int fd) : client_fd(fd), running(true) {}

    void sendData() // 发送数据
    {
        char buf[1024]{};
        while (running)
        {
            memset(buf, 0, sizeof(buf));
            if (fgets(buf, sizeof(buf), stdin) == nullptr)
                continue;
            buf[strcspn(buf, "\n")] = '\0'; // 结束符替换换行符
            if (send(client_fd, buf, strlen(buf), 0) == -1)
            {
                perror("send()");
                continue;
            }

            if (strcmp(buf, "exit") == 0)
            {
                running = false;
                break;
            }
        }
    }

private:
    int client_fd;
};

void *sendDataHelper(void *arg)//发送数据
{
    socketSend *c1 = (socketSend *)arg;
    c1->sendData();
    return nullptr;
}

#endif

服务端代码

#include "TCP.hpp"
using namespace std;

int main(int argc, const char *argv[])
{
    if (argc != 2)
    {
        cout << "需要传入端口号" << endl;
        return -1;
    }

    // 创建套接字
    int server_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (server_fd == -1)
    {
        perror("socket()");
        return -1;
    }

    // 设置服务端ip和端口
    sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); // 主机字节序=>网络字节序
    serverAddr.sin_port = htons(atoi(argv[1]));     // 字符串=>整数=>16位网络字节序二进制值

    // 绑定套接字
    if (bind(server_fd, (sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
    {
        perror("bind()");
        close(server_fd);
        return -1;
    }

    // 设置监听
    if (listen(server_fd, 5) == -1)
    {
        perror("listen()");
        close(server_fd);
        return -1;
    }
    cout << "等待连接......" << endl;

    // 接收连接
    sockaddr_in clientAddr;
    memset(&clientAddr, 0, sizeof(sockaddr_in));
    socklen_t len = sizeof(clientAddr);
    int client_fd = accept(server_fd, (sockaddr *)&clientAddr, &len);
    if (client_fd == -1)
    {
        perror("accept()");
        close(client_fd);
        close(server_fd);
        return -1;
    }
    // 32位二进制网络字节序=>点分十进制字符串 0x7F000001=>“127.0.0.1”
    cout << "ip: " << inet_ntoa(clientAddr.sin_addr) << " 端口: "
         // 16位二进制网络字节序=>主机字节序 0x1234=>4660
         << ntohs(clientAddr.sin_port) << " 已连接到服务器!" << endl;

    setNonBlocking(client_fd); // 设置套接字为非阻塞

    socketSend c1(client_fd);
    pthread_t t1;
    pthread_create(&t1, nullptr, sendDataHelper, &c1);

    char msg[1024]{};
    while (c1.running) // 读
    {
        memset(msg, 0, sizeof(msg));
        if (recv(client_fd, msg, sizeof(msg), 0) == -1)
        {
            continue;
        }
        cout << "来自客户端:" << msg << endl;

        if (strcmp(msg, "exit") == 0)
            break;
    }

    pthread_cancel(t1);
    pthread_join(t1, nullptr);

    // 关闭套接字
    close(client_fd);
    close(server_fd);
}

客户端代码

#include "TCP.hpp"
using namespace std;

int main(int argc, char *argv[])
{
    if (argc != 3)
    {
        cerr << "请传入服务器的ip和port!" << endl;
        return -1;
    }

    // 创建套接字
    int client_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (client_fd < 0)
    {
        perror("sock()");
        return -1;
    }

    // 设置服务端信息
    sockaddr_in serverAddr;
    memset(&serverAddr, 0, sizeof(serverAddr));
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_addr.s_addr = inet_addr(argv[1]); // 字符串=>32位网络字节序二进制值
    serverAddr.sin_port = htons(atoi(argv[2]));      // 字符串=>整数=>16位网络字节序二进制值

    // 发起连接
    if (connect(client_fd, (sockaddr *)&serverAddr, sizeof(serverAddr)) == -1)
    {
        perror("connect()");
        close(client_fd);
        return -1;
    }

    setNonBlocking(client_fd);// 设置套接字为非阻塞

    socketSend s1(client_fd);
    pthread_t t1;
    pthread_create(&t1, nullptr, sendDataHelper, &s1);

    char msg[1024]{};
    while (s1.running)//读
    {
        memset(msg, 0, sizeof(msg));
        if (recv(client_fd, msg, sizeof(msg), 0) == -1)
        {
            continue;
        }
        cout << "来自服务端:" << msg << endl;

        if (strcmp(msg, "exit") == 0)
            break;
    }

    pthread_cancel(t1);
    pthread_join(t1, nullptr);

    // 关闭套接字
    close(client_fd);
}

运行结果

任意一方输入exit,结束连接

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

头发乌黑茂密

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

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

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

打赏作者

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

抵扣说明:

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

余额充值