TCP编程小Demo

封装好的接口
#pragma once

#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <stdlib.h>


class TcpSer
{
public:
    TcpSer(int s = -1) : _sockfd(s)
    {}
    ~TcpSer()
    {}
public:
    //创建套接字
    bool CreateSocket()
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);    //创建流式套接字
        if (_sockfd < 0)
        {
            perror("socket");
            return false;
        }
        return true;
    }
    //绑定地址信息
    bool Bind(std::string& ip, uint16_t port)
    {
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());

        int ret = bind(_sockfd, (struct sockaddr*)&addr, sizeof(addr));
        if (ret < 0)
        {
            perror("bind");
            return false;
        }
        return true;
    }
    //监听
    bool Listen(int backlog = 5)    //默认已连接队列大小为5
    {
        int ret = listen(_sockfd, backlog);
        if (ret < 0)
        {
            perror("listen");
            return false;
        }
        return true;
    }
    //获取连接
    /*
    //peeraddr:出参,返回客户端的地址信息
    bool Accept(struct sockaddr_in* peeraddr, int* sock_fd)
    {
        socklen_t addrlen =  sizeof(struct sockaddr_in);
        int server_fd = accept(_sockfd, (struct sockaddr*)peeraddr, &addrlen));
        if (server_fd < 0)
        {
            perror("accept");
            return false;
        }
        *sock_fd = server_fd;
        return true;
    }
    */
    //ts:出参,返回一个TcpSer类的实例化指针,在这个指针中保存accpet新创建出来的套接字描述符server_fd
    //    上层调用者可以使用返回的指针和客户端进行通信,因为后面的send和recv都是用这个新的_sockfd操作的
    bool Accept(struct sockaddr_in* peeraddr, TcpSer* ts)
    {
        socklen_t addrlen =  sizeof(struct sockaddr_in);
        int server_fd = accept(_sockfd, (struct sockaddr*)peeraddr, &addrlen);
        if (server_fd < 0)
        {
            perror("accept");
            return false;
        }
        ts->_sockfd = server_fd;
        return true;
    }
    //发起连接(针对client来连接server)
    bool Connect(std::string& ip, uint16_t port)
    {
        struct sockaddr_in dest_addr;
        dest_addr.sin_family = AF_INET;
        dest_addr.sin_port = htons(port);
        dest_addr.sin_addr.s_addr = inet_addr(ip.c_str());

        int ret = connect(_sockfd, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
        if (ret < 0)
        {
            perror("connect");
            return false;
        }
        return true;
    }
    //发送数据
    bool Send(std::string& data)
    {
        int sendsize = send(_sockfd, data.c_str(), data.size(), 0);
        if (sendsize < 0)
        {
            perror("send");
            return false;
        }
        return true;
    }
    //接收数据
    //data:出参,将接收到的数据返回给调用者
    bool Recv(std::string* data)
    {
        char buf[1024] = {0};
        int recvsize = recv(_sockfd, buf, sizeof(buf) - 1, 0);
        if (recvsize < 0)
        {
            perror("recv");
            return false;
        }
        else if(recvsize == 0)
        {
            std::cout << "peer shutdown connect" << std::endl;
            return false;
        }
        (*data).assign(buf, recvsize);  //将buf内容拷贝到data对象中
        return true;

    }   
    //关闭套接字
    void Close()
    {
        close(_sockfd);
        _sockfd = -1;
    }
private:
    int _sockfd;
};
服务端代码:

这个Demo程序可以实现多个客户端同时向服务端发送请求,所以使用了多线程进行处理。

#include "tcpser.hpp"
#include <pthread.h>

//拿到新创建的sockset和客户端进行TCP通信
void* thread_fun(void* arg)
{
    //线程函数一开始,就将自己与主线程分离。这样主线程就不用等待子线程结束了
    pthread_detach(pthread_self());

    TcpSer* ts = (TcpSer*)arg;
    while (1)
    {
        std::string buf;
        ts->Recv(&buf);
        std::cout << "client did say:" << buf.c_str() << std::endl;
        std::cout << "please return a message to client:";
        fflush(stdout);

        std::cin >> buf;
        ts->Send(buf);
    }
    ts->Close();
    delete ts;
}

int main(int argc, char* argv[])
{
    if (argc != 3)
    {
        std::cout << "start server: ./ser [ip] [port]" << std::endl;
        return 0;
    }

    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    TcpSer ts;

    if (!ts.CreateSocket())
        return 0;

    if (!ts.Bind(ip, port))
        return 0;

    if (!ts.Listen())
        return 0;

    while (1)
    {
        TcpSer* newts = new TcpSer();   //在堆上申请一个指向TcpSer的实例化指针;如果用对象进行操作,出作用域后对象就被释放了,而在线程函数里仍然在使用,就会造成错误。

        struct sockaddr_in peeraddr;

        if (!ts.Accept(&peeraddr, newts))
            continue;
        
        //inet_ntoa():将uint16_t的整数转换为点分十进制的ip地址,并将网络字节序转换为主机字节序
        //ntohs():将port的网络字节序转换为主机字节序
        std::cout << "Have a new connection ip = " << inet_ntoa(peeraddr.sin_addr) << "," << ntohs(peeraddr.sin_port) << std::endl;
        
        pthread_t tid;
        int ret = pthread_create(&tid, NULL, thread_fun, (void*)newts);
        if (ret < 0)
        {
            perror("pthread_create");
            return 0;
        }
    }

    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值