首页 > 编程语言 >一个CPP Socket C/S程序

一个CPP Socket C/S程序

时间:2023-08-13 21:33:10浏览次数:33  
标签:Socket -- CXX 程序 server int client CPP socket

虽然学了很多回了,但总是记不住。 这里直接给个可以使用的 CPP TCP socket 程序吧。 使用cmake管理,但是我是个半吊子水平。在学习,例如CMake Tutorial

程序结构

PROG_HOME
├── build			# 可以不自己创建,直接运行run.sh会给生成的。
├── CMakeLists.txt		# 
├── docs			# 好吧,我还没习惯写文档
├── include			# 实际上我并没用到include,我也不知道怎么去使用,cmake还在学习中。
├── libs			# 库文件
│   ├── CMakeLists.txt
│   └── socket			# 自己写的socket库
│       ├── CMakeLists.txt
│       ├── socket.cpp
│       └── socket.h
├── run.sh			# 这个脚本用于简化编译运行步骤
└── src
    ├── client.cpp
    └── server.cpp

CMakeLists.txt文件

在这里有3个CMakeLists.txt。至于怎么管理的搞不太清楚的,需要先学习,我也是半吊子。

项目的CMakeLists.txt源码

cmake_minimum_required(VERSION 3.5)
project(socket_test)

set(CMAKE_CXX_STANDARD 11)

# 虽然没有用到,还是给加进来了,主要是我也没有刻意区分了
include_directories(include)

# 添加库文件
add_subdirectory(libs)

# C/S可执行程序
add_executable(client ./src/client.cpp)
add_executable(server ./src/server.cpp)

# 链接socket库
target_link_libraries(client PUBLIC socket)
target_link_libraries(server PUBLIC socket)

libs下的CMakeLists.txt源码

# 只需要加上需要的库(实际上也只有这一个)
add_subdirectory(socket)

socket下的CMakeLists.txt源码

# 添加库
add_library(socket STATIC socket.cpp)
# 添加引用文件,以便于让client和server引入(但是总感觉这样用怪怪的)
target_include_directories(socket PUBLIC ".")

socket库

socket流程,在CPP和C中没有太大区别。大致流程如下,至于分析三次握手协议,不在这里说了。

  1. server:socket() => bind() => listen() => accept() => read/write => close()
  2. client: socket() [=> bind()] => connect() => read/write => close()

以下是一个阻塞型的TCP socket程序。 要改为非阻塞型或者UDP,就需要另外学习其他的了。

socket.h

/******************************************************
 * File Name:    socket.h
 * Author:       [email protected]
 * Created Time: 2023-08-13 09:15:29
 * Description:  socket库头文件,定义数据结构
 ******************************************************/

#ifndef SOCKET_H
#define SOCKET_H

#include <string>

using std::string;

// 套两层,实际上在这里套不套都可以,反正是自己写的,只是为了代码隔离。
// 但是貌似无所谓了,套吧
namespace basil
{
    namespace socket
    {
        class Socket
        {
            public:
                Socket();
                Socket(int sockfd);
                ~Socket();

		// 把socket程序的基础函数都拿过来了
                bool bind(const string &ip, const int port);
                bool listen(const int backlog);
                bool connect(const string &ip, const int port);
                int accept();

                int send(const char *buf, int len);
                int recv(char *buf, int len);

                void close();

            private:
                string m_ip;
                int m_port;
                int m_sockfd;
        };
    }
}

#endif // SOCKET_H

socket.cpp

/*****************************************************
 * File Name:    socket.cpp
 * Author:       [email protected]
 * Created Time: 2023-08-13 09:18:11
 * Description:  实现。应该使用日志文件,但是我没写,简单点就直接打印吧。
 ******************************************************/

#include <iostream>
#include <string>

#include <cstring>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "socket.h"


using namespace std;

using namespace basil::socket;

/* 创建socket */
Socket::Socket() : m_ip(""), m_port(0), m_sockfd(-1)
{
    m_sockfd = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    if (m_sockfd < 0)
    {
        perror("socket()");
        return;
    }
    printf("socket() create successfully!\n");
}

/* 创建socket,用于server的接收 */
Socket::Socket(int sockfd) : m_ip (""), m_port(0), m_sockfd(sockfd)
{}

/* 销毁socket,就是关闭 */
Socket::~Socket()
{
    close();
}

/* 绑定 */
bool Socket::bind(const string &ip, const int port)
{
    struct sockaddr_in sockaddr;

    if (ip.empty())
    {
        sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    }
    else
    {
        sockaddr.sin_addr.s_addr = inet_addr(ip.c_str());
    }

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(port);

    if (::bind(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0)
    {
        perror("bind()");
        return false;
    }

    m_ip = ip;
    m_port = port;

    printf("bind() successfully\n");

    return true;
}

/* 监听 */
bool Socket::listen(const int backlog)
{
    if (::listen(m_sockfd, backlog) < 0)
    {
        perror("listen()");
        return false;
    }

    printf("listen() at [%s:%d] successfully\n", m_ip.c_str(), m_port);

    return true;
}

/* client主动连接 */
bool Socket::connect(const string &ip, const int port)
{
    struct sockaddr_in sockaddr;

    sockaddr.sin_family = AF_INET;
    sockaddr.sin_port = htons(port);
    sockaddr.sin_addr.s_addr = inet_addr(ip.c_str());

    if (::connect(m_sockfd, (struct sockaddr *)&sockaddr, sizeof(struct sockaddr)) < 0)
    {
        perror("connect()");
        return false;
    }

    printf("connect() successfully\n");

    m_ip = ip;
    m_port = port;

    return true;
}

/* server等待连接 */
int Socket::accept()
{
    int connfd = ::accept(m_sockfd, nullptr, nullptr);

    if (connfd < 0)
    {
        perror("accept()");
        return -1;
    }

    printf("accept() successfully\n");

    return connfd;
}

/* 发送 */
int Socket::send(const char *buf, int len)
{
    return ::send(m_sockfd, buf, len, 0);
}

/* 接收 */
int Socket::recv(char *buf, int len)
{
    return ::recv(m_sockfd, buf, len, 0);
}

/* 关闭socket */
void Socket::close()
{
    if (m_sockfd > 0)
    {
        ::close(m_sockfd);
        m_sockfd = 0;
    }
}

程序源码

客户端

/******************************************************
 * File Name:    src/client.cpp
 * Author:       [email protected]
 * Created Time: 2023-08-13 02:28:10
 * Description:
 ******************************************************/

#include <string>

#include <cstring>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "socket.h"

using namespace basil::socket;
using std::string;

int main(int argc, char *argv[])
{
    Socket client;
    bool connected = client.connect("127.0.0.1", 8008);

    if (!connected)
    {
        fprintf(stderr, "connected failed\n");
        return -1;
    }
    string data;
    char buf[1024] = {0};

    while (1)
    {
        printf("[C->S]: ");
        scanf("%s", buf);
        data = buf;
        client.send(data.c_str(), data.size());

        if (strncmp(buf, "quit", 4) == 0)
        {
            printf("byebye~\n");
            break;
        }

        client.recv(buf, 1024);
        printf("[S->C]: %s\n", buf);
   }

    return 0;
}

服务端

/******************************************************
 * File Name:    server.cpp
 * Author:       [email protected]
 * Created Time: 2022-10-09 07:34:59
 * Description:  使用CPP风格的socket,不要再使用C风格的socket。
 ******************************************************/

#include <cstring>

#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#include "socket.h"

using namespace basil::socket;
using std::string;

int main()
{
    Socket server;

    string ip = "127.0.0.1";
    int port = 8008;
    server.bind(ip, port);

    server.listen(1024);

    while (true)
    {
        int connfd = server.accept();
        if (connfd < 0)
            break;
        Socket client(connfd);

        while(true)
        {
            char buf[1024] = {0};
            size_t len = client.recv(buf, sizeof(buf));
            printf("[C->S]: %s\n", buf);

            if (strncmp(buf, "quit", 4) == 0)
            {
                bzero(buf, 1024);
                len = strlen("byebye~");
                snprintf(buf, len, "byebye~");
                client.send(buf, len);
                printf("[client: %d] byebye~\n", connfd);
                break;
            }

            client.send(buf, len);
        }
        client.close();
    }

    server.close();
}

运行测试

#!/bin/bash

mkdir -p build

rm -rf build/*
cd build
cmake ..
make

if [ 0 -ne $# ]
then
    ./$@
fi
  1. 起服务端
$ ./run.sh server
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/basil/code/cpp/server_dev/sockets/build
[ 16%] Building CXX object libs/socket/CMakeFiles/socket.dir/socket.cpp.o
[ 33%] Linking CXX static library libsocket.a
[ 33%] Built target socket
[ 50%] Building CXX object CMakeFiles/client.dir/src/client.cpp.o
[ 66%] Linking CXX executable client
[ 66%] Built target client
[ 83%] Building CXX object CMakeFiles/server.dir/src/server.cpp.o
[100%] Linking CXX executable server
[100%] Built target server
socket() create successfully!
bind() successfully
listen() at [127.0.0.1:8008] successfully

  1. 起客户端
$ ./run.sh client
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/basil/code/cpp/server_dev/sockets/build
[ 16%] Building CXX object libs/socket/CMakeFiles/socket.dir/socket.cpp.o
[ 33%] Linking CXX static library libsocket.a
[ 33%] Built target socket
[ 50%] Building CXX object CMakeFiles/client.dir/src/client.cpp.o
[ 66%] Linking CXX executable client
[ 66%] Built target client
[ 83%] Building CXX object CMakeFiles/server.dir/src/server.cpp.o
[100%] Linking CXX executable server
[100%] Built target server
socket() create successfully!
connect() successfully
[C->S]: 

至于测试,就是在client输入,server输出以及返回。当client输入为quit时,就会退出client端,server端也会断开连接。如果不想自己写client,或者只是简单测试,其实可以使用nc 127.0.0.1 8008命令来测试,不过需要安装下ncat。

不过这里只能输入一些字符串,而不能输入int等其它类型(更多时候可能是二进制的)得再改改。

参考

  1. 高级篇---网络编程基础

标签:Socket,--,CXX,程序,server,int,client,CPP,socket
From: https://blog.51cto.com/basilguo/7069687

相关文章

  • 微信小程序PDF签名
    如何在不通过电脑,不下载手机APP的情况下进行PDF签名?打开微信,搜索《简单签名》即可方便快捷进行签名。下面是手机的操作步骤:1.微信搜索《简单签名》   2.打开简单签名,选择PDF。  3.选择聊天对象,这儿以“文件传输助手”为例。4.签名   5.点击“保存”,点击......
  • 1-1汇编语言程序上机调试
    .MODELTINY.STACK100.DATA.CODESTART: MOVAX,@DATA MOVDS,AX MOVES,AX NOP MOVCX,100HMOVSI,3000HMOVDI,6000HCALLMoveMOVCX,100HMOVSI,3000HMOVDI,......
  • 常见的程序设计语言概述
    程序设计语言的发展是一个不断演化的过程,其根本的推动力就是对抽象机制的更高要求,以及对程序设计活动更好地支持。具体地说,就是把机器能够理解的语言提升到也能够很好地模仿人类思考问题的形式。常见的程序设计语言如下所示:1、FORTRAN这是第一个高级程序设计语言,在数值计算领域积累......
  • 利用C语言实现简单的计算器程序
    利用C语言实现简单的计算器程序在日常生活中,计算器是一个不可或缺的工具。它可以帮助我们进行各种数学计算,从简单的加减乘除到复杂的三角函数和指数运算。而使用C语言编写一个简单的计算器程序,则是一个很有挑战性和有趣的任务。1利用C语言实现简单的计算器程序首先,我们需要明确计算......
  • 系统和服务通讯(Topshelf+TouchSocket)
    服务不是单独的,总要和其他系统进行信息交互,记录一个解决方案(方便,好用)Topshelf秒建Windows服务推荐一个超轻量级的.NET网络通信框架新建控制台,然后安装Topshelf和TouchSocket,作为服务示例代码:namespaceTestServer{classProgram{staticvoidMain(s......
  • 利用C语言实现简单的计算器程序
    利用C语言实现简单的计算器程序在日常生活中,计算器是一个不可或缺的工具。它可以帮助我们进行各种数学计算,从简单的加减乘除到复杂的三角函数和指数运算。而使用C语言编写一个简单的计算器程序,则是一个很有挑战性和有趣的任务。1利用C语言实现简单的计算器程序首先,我们需要明确......
  • 本地启动 ABAP Platform Trial 的 Docker 命令行程序
    Docker是一个开源的容器化平台,用于轻松地构建、发布和运行应用程序。DockerDesktop是适用于Windows和Mac的Docker桌面应用程序,它允许用户在本地运行和管理容器化应用程序。在本文中,我们将详细介绍以下两个命令行并解释每个参数的含义:dockerpullsapse/abap-platform-trial:190......
  • 微信小程序视图容器 swiper
    滑块视图容器。属性类型默认值必填说明最低版本属性类型默认值必填说明最低版本indicator-dotsbooleanfalse否是否显示面板指示点1.0.0indicator-colorcolorrgba(0,0,0,.3)否指示点颜色1.1.0indicator-active-colorcolor#000000否当前选中的指......
  • #yyds干货盘点# LeetCode程序员面试金典:数组中的第K个最大元素
    题目:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例1:[3,2,1,5,6,4],示例 2:[3,2,3,1,2,4,5,5,6],代码实现:class......
  • #yyds干货盘点# LeetCode程序员面试金典:查找和最小的 K 对数字
    1.简述:给定两个以 非递减顺序排列 的整数数组 和  , 以及一个整数  。nums1nums2k定义一对值 ,其中第一个元素来自 ,第二个元素来自  。(u,v)nums1nums2请找到和最小的  个数对 , k(u1,v1) (u2,v2)(uk,vk) 示例1:输入:nums1=[1,7,11],nums2=[2,4,6],k=3......