第四章 Linux套接字通信:6.TCP 套接字通信封装(C/C++)

在掌握了 TCP 套接字通信流程的基础上,对相关通信操作进行了函数级与类级封装,并通过示例程序展示其使用方式。


📖 目录

    1. 通信流程总览
    1. C语言函数封装
    • 2.1 函数声明
    • 2.2 函数定义(含详细注释)
    1. C++ 封装类
    • 3.1 客户端封装类 TcpClient(版本1)
    • 3.2 服务器封装类 TcpServer(版本1)
    • 3.3 通信类 TcpSocket(版本2)
    • 3.4 精简服务器类 TcpServer(版本2)
    1. 测试程序
    1. 多语言对照
    1. 总结

1. 通信流程总览

客户端通信流程

  1. 创建 socket
  2. 连接服务器
  3. 通信(发送/接收)
  4. 关闭 socket

服务器端通信流程

  1. 创建 socket
  2. 绑定 IP + 端口
  3. 启动监听
  4. 接受连接
  5. 通信
  6. 关闭 socket

2. C语言函数封装

2.1 函数声明

// 服务器端
int bindSocket(int lfd, unsigned short port);
int setListen(int lfd);
int acceptConn(int lfd, struct sockaddr_in *addr);

// 客户端
int connectToHost(int fd, const char* ip, unsigned short port);

// 公用
int createSocket();
int sendMsg(int fd, const char* msg, int len);
int recvMsg(int fd, char** msg);
int closeSocket(int fd);
int readn(int fd, char* buf, int size);
int writen(int fd, const char* msg, int size);

2.2 函数定义(含详细注释)

创建 socket
int createSocket()
{
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1) {
        perror("socket");
        return -1;
    }
    printf("套接字创建成功, fd=%d\n", fd);
    return fd;
}
绑定 IP 和端口
int bindSocket(int lfd, unsigned short port)
{
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    saddr.sin_addr.s_addr = INADDR_ANY;

    int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));
    if(ret == -1) {
        perror("bind");
        return -1;
    }
    printf("套接字绑定成功, ip: %s, port: %d\n",
           inet_ntoa(saddr.sin_addr), port);
    return 0;
}
启动监听
int setListen(int lfd)
{
    int ret = listen(lfd, 128);
    if(ret == -1) {
        perror("listen");
        return -1;
    }
    printf("设置监听成功...\n");
    return 0;
}
接受连接
int acceptConn(int lfd, struct sockaddr_in *addr)
{
    socklen_t addrlen = sizeof(struct sockaddr_in);
    int cfd = accept(lfd, (struct sockaddr*)addr, &addrlen);
    if(cfd == -1) {
        perror("accept");
        return -1;
    }       
    printf("成功和客户端建立连接...\n");
    return cfd; 
}
客户端连接
int connectToHost(int fd, const char* ip, unsigned short port)
{
    struct sockaddr_in saddr;
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(port);
    inet_pton(AF_INET, ip, &saddr.sin_addr.s_addr);

    int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
    if(ret == -1) {
        perror("connect");
        return -1;
    }
    printf("成功和服务器建立连接...\n");
    return 0;
}
发送数据
int sendMsg(int cfd, const char* msg, int len)
{
   if(msg == NULL || len <= 0) {
       return -1;
   }
   char* data = (char*)malloc(len+4);
   int bigLen = htonl(len);
   memcpy(data, &bigLen, 4);
   memcpy(data+4, msg, len);

   int ret = writen(cfd, data, len+4);
   free(data);
   return ret;
}
接收数据
int recvMsg(int cfd, char** msg)
{
    if(msg == NULL || cfd <= 0) {
        return -1;
    }

    int len = 0;
    if(readn(cfd, (char*)&len, 4) != 4) return -1;

    len = ntohl(len);
    printf("数据块大小: %d\n", len);

    char* buf = (char*)malloc(len + 1);
    if(readn(cfd, buf, len) != len) {
        free(buf);
        return -1;
    }
    buf[len] = '\0';
    *msg = buf;
    return len;
}
精确读写
int readn(int fd, char* buf, int size)
{
    int nread = 0;
    int left = size;
    char* p = buf;
    while(left > 0) {
        if((nread = read(fd, p, left)) > 0) {
            p += nread;
            left -= nread;
        } else if(nread == 0) {
            break;
        } else {
            return -1;
        }
    }
    return size - left;
}

int writen(int fd, const char* msg, int size)
{
    int left = size;
    int nwrite = 0;
    const char* p = msg;
    while(left > 0) {
        if((nwrite = write(fd, p, left)) > 0) {
            p += nwrite;
            left -= nwrite;
        } else if(nwrite == 0) {
            continue;
        } else {
            return -1;
        }
    }
    return size;
}
关闭套接字
int closeSocket(int fd)
{
    int ret = close(fd);
    if(ret == -1) {
        perror("close");
    }
    return ret;
}

3. C++ 封装类

3.1 客户端封装类 TcpClient(版本1)

class TcpClient {
public:
    TcpClient();   // 构造函数:创建 socket
    ~TcpClient();  // 析构函数:关闭 socket

    int connectToHost(string ip, unsigned short port);  // 连接服务器
    int sendMsg(string msg);                            // 发送消息
    string recvMsg();                                   // 接收消息

private:
    int readn(char* buf, int size);                     // 精确读取
    int writen(const char* msg, int size);              // 精确写入

private:
    int cfd; // 通信 socket(客户端)
};

类内部隐藏了 socket,简化使用;构造/析构自动管理资源。


3.2 服务器封装类 TcpServer(版本1)

class TcpServer {
public:
    TcpServer();
    ~TcpServer();

    int setListen(unsigned short port);                // 绑定端口并监听
    int acceptConn(struct sockaddr_in* addr);          // 接收客户端连接
    int sendMsg(string msg);                           // 发送数据
    string recvMsg();                                  // 接收数据

private:
    int readn(char* buf, int size);
    int writen(const char* msg, int size);

private:
    int lfd; // 监听 socket
    int cfd; // 通信 socket(仅支持一个客户端)
};

本版本结构类似客户端,但通信 socket 不支持多个并发。


3.3 通信类 TcpSocket(版本2)

class TcpSocket {
public:
    TcpSocket();                      // 创建新 socket(客户端)
    TcpSocket(int socket);           // 使用已有 socket(服务器)
    ~TcpSocket();

    int connectToHost(string ip, unsigned short port);
    int sendMsg(string msg);
    string recvMsg();

private:
    int readn(char* buf, int size);
    int writen(const char* msg, int size);

private:
    int m_fd; // 通信 socket
};

本类抽象了通信过程,可供客户端或服务器连接使用。


3.4 精简服务器类 TcpServer(版本2)

class TcpServer {
public:
    TcpServer();
    ~TcpServer();

    int setListen(unsigned short port);                         // 设置监听
    TcpSocket* acceptConn(struct sockaddr_in* addr = nullptr); // 接收连接,返回 TcpSocket 对象

private:
    int m_fd; // 监听 socket
};

服务器端专注于监听功能,将通信任务交由 TcpSocket 处理,支持并发客户端。


4. 测试程序

客户端测试(基于 TcpSocket)

int main() {
    TcpSocket tcp;
    if (tcp.connectToHost("192.168.1.100", 10000) == -1) return -1;

    int fd = open("english.txt", O_RDONLY);
    char buf[100] = {0};
    int len = 0;

    while ((len = read(fd, buf, sizeof(buf))) > 0) {
        tcp.sendMsg(string(buf, len));
        memset(buf, 0, sizeof(buf));
        usleep(300);
    }
    close(fd);
    return 0;
}

服务器端测试(多线程 + TcpSocket)

struct SockInfo {
    TcpSocket* tcp;
    struct sockaddr_in addr;
};

void* working(void* arg) {
    SockInfo* info = (SockInfo*)arg;
    char ip[32];
    printf("客户端: %s:%d\n",
        inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip)),
        ntohs(info->addr.sin_port));

    while (1) {
        string msg = info->tcp->recvMsg();
        if (!msg.empty())
            cout << msg << endl;
        else
            break;
    }
    delete info->tcp;
    delete info;
    return nullptr;
}

int main() {
    TcpServer server;
    server.setListen(10000);

    while (1) {
        SockInfo* info = new SockInfo;
        TcpSocket* client = server.acceptConn(&info->addr);
        if (!client) continue;

        info->tcp = client;
        pthread_t tid;
        pthread_create(&tid, NULL, working, info);
        pthread_detach(tid);
    }
    return 0;
}

5. 多语言对照

操作C/C++PythonJava
创建 socketsocket()socket.socket()new Socket()
绑定地址bind()s.bind()serverSocket.bind()
开始监听listen()s.listen()自动
接收连接accept()s.accept()server.accept()
连接服务器connect()s.connect()new Socket(ip, port)
发送数据write()send()OutputStream.write()
接收数据read()recv()InputStream.read()

6. 总结

  • 本文实现了基于 TCP 的 C/C++ socket 通信封装。
  • 函数级别封装适合底层使用,类封装更适合 C++ 项目开发。
  • 使用粘包处理技术(包头 + 包体)确保 TCP 数据完整性。
  • 通信类 TcpSocket 可复用于客户端或服务器通信。
  • 多线程服务器使用组合设计(TcpServer + TcpSocket)支持并发客户端。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值