tcp网络编程(基础)

目录

一.编程前的一些基础

二.tcp网络编程

1.一个服务器只能有一个客户端连接(下面代码)

Socket.hpp

TcpServer.hpp

TcpServerMain.cc

TcpClientMain.cc

2.一个服务器可以有多个客户端连接(多线程)


看这篇文章前,先把有关udp和一些网络基础看了(最好敲一下代码)

网络基础1-CSDN博客

一.编程前的一些基础

1.tcp是面向连接的,面向字节流的,在通信前要先建立连接(将服务端设置为监听状态(listen),服务端等待连接的到来(accept),客户端主动建立连接(connect),建立连接后就可以通信(send,recv)了)

二.tcp网络编程

1.一个服务器只能有一个客户端连接(下面代码)

Socket.hpp

cpp 复制代码
#pragma once

#include<iostream>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include<cstring>
#include<string>
#include <unistd.h>


#define Convert(addr) ((struct sockaddr*)addr)

enum
{
    SocketError = 1,
    BindError,
    ListenError,
};

const static int defaultbackflag = 5;
const int defaultsockfd = -1;


//简单解释下:Socket.hpp用了继承,多态的知识
//我们将创建socket,bind, listen, accept, connect等方法都单独封装起来



class Socket
{
public:
    Socket(){}
    ~Socket(){}

    virtual void CreateSocket() = 0; //创建socket套接字
    virtual void BindSocket(uint16_t port) = 0; //绑定套接字
    virtual void ListenSocket(int backflag = defaultbackflag) = 0; //将服务端设置为监听状态
    virtual Socket* AcceptConnection(std::string *peerip, uint16_t *peerport) = 0; //服务端接收客户端发来的连接请求
    virtual bool ConnectServer(std::string& serverip, uint16_t serverport) = 0; //客户端准备连接服务端
    virtual int GetSockfd() = 0; //获取sockfd
    virtual void SetSockfd(int sockfd) = 0;
    virtual void CloseSockfd() = 0; //关闭文件描述符
public:
    //将服务端设置为监听状态 //tcpserver.hpp调用
    void BuildListenSocketMethod(uint16_t port, int backflag = defaultbackflag)
    {
        CreateSocket();
        BindSocket(port);
        ListenSocket(backflag);
    }
    //客户端去连接服务端  //tcpclient.hpp调用
    bool BuildConnectSocketMethod(std::string& serverip, uint16_t serverport)
    {
        CreateSocket();
        return ConnectServer(serverip, serverport);
    }

    void BuildNormalSocketMethod(int sockfd)
    {
        SetSockfd(sockfd);
    }
};

//继承并将具体方法实现
class TcpSocket : public Socket
{
public:
    TcpSocket(int sockfd = defaultsockfd):_sockfd(sockfd){}
    void CreateSocket() override
    {
        //创建socket套接字
        //tcp用SOCK_STREAM,
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)//失败
        {
            exit(SocketError);
        }
    }
    void BindSocket(uint16_t port) override
    {
        //方法同udp
        struct sockaddr_in local;
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;

        int n = bind(_sockfd, Convert(&local), sizeof(local)); //绑定本机socket
        if(n != 0) exit(BindError);
    }
    void ListenSocket(int backflag = defaultbackflag) override
    {
        //设置服务端为监听状态
        int n = listen(_sockfd, backflag);
        if(n != 0) exit(ListenError);
    }

    //peerip,peerport是输出型参数,目的是获取有关client的一些信息,并为我们所用
    Socket* AcceptConnection(std::string* peerip, uint16_t* peerport) override
    {
        //client的一些信息
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        //服务端获取客户端的connect请求,进行连接(此函数阻塞等待)
        //accept成功会返回一个新的文件描述符(>0),我们使用新的文件描述符进行通信
        int newsockfd = ::accept(_sockfd, Convert(&peer), &len);
        if(newsockfd < 0) return nullptr;

        *peerip = inet_ntoa(peer.sin_addr);
        *peerport = ntohs(peer.sin_port);

        Socket* s = new TcpSocket(newsockfd);
        return s;
    }
    bool ConnectServer(std::string& serverip, uint16_t serverport) override
    {
        //客户端发送连接请求
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(serverport);

        inet_pton(AF_INET, serverip.c_str(), &server.sin_addr); //更安全
        //或者server.sin_addr.s_addr = inet_addr(serverip.c_str());
        //失败-1,并且我们发起连接时,client会被os自动地进行bind,我们不需要自己调用bind函数
        int n = ::connect(_sockfd, Convert(&server), sizeof(server));
        if(n == 0) return true;
        else return false;
    }
    int GetSockfd() override
    {
        return _sockfd;
    }
    void SetSockfd(int sockfd) override
    {
        _sockfd = sockfd;
    }
    void CloseSockfd() override  //关闭文件描述符
    {
        if(_sockfd > defaultsockfd) 
        {
            close(_sockfd);
        }
    }

private:
    int _sockfd;
};

TcpServer.hpp

cpp 复制代码
#pragma once

#include<iostream>
#include"Socket.hpp"

class TcpServer
{
public:
    TcpServer(uint16_t port) :_port(port), _listensocket(new TcpSocket())
    {
        _listensocket->BuildListenSocketMethod(_port, defaultbackflag); //设置为监听状态
    }

    void Loop()
    {
        char bufferin[1024];
        std::string peerip;
        uint16_t peerport;
        Socket* s = _listensocket->AcceptConnection(&peerip, &peerport); //服务端接收客户端发来的连接请求
        if(s == nullptr) exit(0);
        std::cout << "accept success" << std::endl;
        while(true) //接收client发的信息
        {


            ssize_t n = recv(s->GetSockfd(), bufferin, sizeof(bufferin)-1, 0); //接收client发送的信息
            //ssize_t n = (s->GetSockfd(), bufferin, sizeof(bufferin)-1);
            if(n > 0)
            {
                bufferin[n] = 0;
                std::string ret;
                ret += peerip;
                ret += " ";
                ret += std::to_string(peerport);
                std::cout << ret << " " << "client say#" << bufferin << std::endl;
            }
            else
            {
                break;
            }
        }
    }

    ~TcpServer()
    {
        delete _listensocket;
    }
private:
    int _port;
    Socket* _listensocket;
};

TcpServerMain.cc

cpp 复制代码
#include<iostream>
#include<memory>
#include"TcpServer.hpp"

//./tcpserver 8888
int main(int argc, char* argv[])
{
    if(argc != 2)
    {
        std::cout << "Usage:" << "./tcpserver " << "serverport" << std::endl;
    }
    uint16_t localport = std::stoi(argv[1]);
    std::unique_ptr<TcpServer> svr(new TcpServer(localport));
    svr->Loop();
    return 0;
}

TcpClientMain.cc

cpp 复制代码
#include<iostream>
#include "Socket.hpp"
//./tcpclient 127.0.0.1 8888
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        std::cout << "Usage:" << "./tcpclient " << "serverip serverport" << std::endl;
    }
    std::string serverip = argv[1];
    uint16_t serverport = std::stoi(argv[2]);

    Socket* s = new TcpSocket();
    if(!s->BuildConnectSocketMethod(serverip, serverport)) //向服务端申请建立连接
    {
        std::cerr << "connect " << serverip << ":" << serverport << " failed" << std::endl;
    }
    std::cout << "connect " << serverip << ":" << serverport << " success" << std::endl;

    std::string buffer;
    while(true)//发信息给服务端
    {
        std::getline(std::cin, buffer);
        std::cout << "Enter:" << std::endl;
        ssize_t n = send(s->GetSockfd(), buffer.c_str(), buffer.size(), 0); //向服务端发送信息
        //ssize_t n = write(s->GetSockfd(), buffer.c_str(), buffer.size());
        if(n < 0)
        {
            break;
        }
    }

    s->CloseSockfd();
    return 0;
}

2.一个服务器可以有多个客户端连接(多线程)

server端创建多个线程,每个线程与不同的client端建立连接。

tcp网络编程------2-CSDN博客

相关推荐
秦jh_3 分钟前
【Linux】多线程(概念,控制)
linux·运维·前端
娅娅梨22 分钟前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
兵哥工控27 分钟前
MFC工控项目实例二十九主对话框调用子对话框设定参数值
c++·mfc
我爱工作&工作love我34 分钟前
1435:【例题3】曲线 一本通 代替三分
c++·算法
Hacker_Nightrain37 分钟前
网络安全CTF比赛规则
网络·安全·web安全
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
lexusv8ls600h1 小时前
探索 C++20:C++ 的新纪元
c++·c++20
lexusv8ls600h1 小时前
C++20 中最优雅的那个小特性 - Ranges
c++·c++20
白-胖-子1 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯1 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法