欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

linux网络中编写一个TCP的客户端和服务端的流程总结

程序员文章站 2022-03-04 08:49:32
...

**

因为本人之前一直写的是云笔记,对自己学会的东西作一个总结,所以基本都是文字,本来想全发成博客的形式,发现全发成博客比较花费时间,而且一直发博客质量不是很好,而且通过发博客学到的东西也会变少,所以准备先把笔记发出来,后续再将它们改成博客的形式,争取2天至少改一篇博客,觉得我总结的还行的可以先关注我,后续会发成博客形式,内容也会更加完善

**

TCP的服务端:
在进行套接字编程时,通常将需要的用到套接字函数在类中进行封装,增加一些其它的功能,或者是做一些防错措施,以及完善这些函数的流程,所以将这些函数集中在类中是很有作用的
我们在搭建TCP服务端时,通常是用main函数自带的三个参数来获取用户的输入信息,这个输入信息是我们在发送数据时要用的的IP地址和所用到的端口号,然后分别创建两个字符串对象来存储它们,方便一会在进行TCP传输时,传入参数,然后我们一般创建一个之前写好的,TCP套接字函数对象(通过调用里面的函数来实现TCP数据报传输),我们可以先调用之前封装好的socket函数,进行套接字的创建,然后再调用封装好的bind函数,来进行地址信息的绑定,因为我们现在是服务端,所以一般情况下自己绑定自己的IP地址,然后再绑定自己规定的端口号(因为要让客户端知道服务端在哪接收数据),所以这两个信息我们要传入bind函数中,当地址信息绑定完成后,我们就要开始监听了,这时我们调用封装好的listen函数就可以进行监听了,因为我们是服务端,所以不用请求和客户端建立连接,只要等客户端请求和我们建立连接,然后我们再进行三次握手就可以了,当客户端请求和我们建立的连接成功后,我们就可以调用封装好的accept函数,来获取约定好的新的socket了(因为我们并不知道客户端什么时候要和我们建立连接,所以我们得一直等,所以通常用while(1)来让服务一直等待,但TCP是要建立连接的,也就是说只有建立了连接才可以继续下面的通信,所以一个在accept函数处,我们会进行一个判断,如果连接创建成功,我们的程序就可以继续往下走,但如果没成功,程序就要一直卡在accept函数处,不能继续执行下面的代码,所以当连接没有创建成功时,我们使用continue让这个程序不往下走,直接循环到上面去,当创建连接成功时,就往下走其它的代码就可以了,在获取到新的socket后,我们就可以开始发送和接收数据了,因为我们是服务端,所以一般是先等待客户端给我们发送请求数据,然后我们再获取客户端的地址信息,发送对应的数据包给它们,所以我们先调用封装好的recv函数进行接收数据包,在获取到数据包的同时,我们就获取到了客户端的地址信息,进而我们就可以调用封装好的send函数,进行数据的发送,在所有信息都传递完成,且后续不需要再接收客户端信息时,我们就可以调用封装好的close函数,关闭套接字
我们在搭建TCP客户端时,通常是用main函数自带的三个参数来获取用户的输入信息,这个输入信息是服务端的IP地址和所用到的端口号,然后分别创建两个字符串对象来存储它们,方便一会在进行TCP传输时传入参数,然后我们一般创建一个之前写好的,TCP套接字函数对象(通过调用里面的函数来实现TCP数据报传输)我们可以先调用之前封装好的socket函数,进行套接字的创建,另外有一点要说明,一般情况下TCP的客户端不建议自己绑定地址信息,服务端需要绑定地址信息的原因是,如果服务端不绑定地址信息,那么客户端就不能确定服务端到底是用什么地址来接收数据包的,所以客户就不能向服务传输数据包了,但是客户端的话就不一样了,因为客房端是向服务请求数据包的,也就是客户端先向服务端发送数据包,服务端在接收到数据包后,可以获取客户端的地址信息,进而也就可以给客户端发送数据包了,所以客户端可以不绑定地址信息,而且客户端不绑定地址信息的好处也有很多,比如我第一次向服务端请求数据包时,用的是一个网卡上的IP地址,但过了一阵时间,我换了一个网卡,我的地址信息变了,但是由于我没有绑定地址信息,所以我仍可以向服务端请求数据包,所以客户端一般不建议绑定地址信息,所以我们就可以使用封装好的connect函数,直接申请和服务端建立连接了,在建立连接好,我们不需要使用accept去已建立连接的队列去找,因为是我们和服务端建立连接,所以在建立连接完毕后,就可以直接进行通信了,所以现在我们就可以直接根据服务端的地址信息,利用我们封装好的send函数,向服务端发送数据包了,我们给服务端发送完数据包后,就可以等待服务端给我们回复数据包了,因为我们不知道服务端的数据包什么时候过来,所以就得一直等待服务端,所以一般情况下我们要接收服务端的数据时,就写个while(1),让它一直循环接收,如果接收到了,我们就获取到了服务端的地址信息在所有信息都传递完成,且后续不需要再接收客户端信息时,我们就可以调用封装好的close函数,关闭套接字

需要自己封装的TCP函数类:
#include
#include
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define CHECK_RET(q) if((q) == false){ return -1; }

struct _person_info_t{
char name[16];
char sex[4];
int age;
};

class TcpSocket
{
public:
TcpSocket():_sockfd(-1){
}
~TcpSocket(){
}
int GetFd() {
return _sockfd;
}
bool Socket() {
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sockfd < 0) {
std::cout<<“socket error\n”;
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());
socklen_t len = sizeof(struct sockaddr_in);

        int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
        if (ret < 0) {
            std::cout<<"bind error\n";
            return false;
        }
        return true;
    }

    bool Listen(int baklog = 5) {
        int ret = listen(_sockfd, baklog);
        if (ret < 0) {
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(ip.c_str());
        socklen_t len = sizeof(struct sockaddr_in);

        int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
        if (ret < 0) {
            std::cout<<"bind error\n";
            return false;
        }
        return true;
    }
    bool Listen(int baklog = 5) {
        int ret = listen(_sockfd, baklog);
        if (ret < 0) {
            std::cout<<"listen error\n";
            return false;
        }
        return true;
    }
    bool Accept(TcpSocket &sock, struct sockaddr_in *addr = NULL){
        struct sockaddr_in _addr;
        socklen_t len = sizeof(struct sockaddr_in);
        int newfd = accept(_sockfd, (struct sockaddr*)&_addr, &len);
        if (newfd < 0) {
            std::cout<<"accept error\n";
            return false;
        }
        sock.SetFd(newfd);
        if (addr != NULL) {
            memcpy(addr, &_addr, len);
        }
        return true;
    }
    bool Connect(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());
        socklen_t len = sizeof(struct sockaddr_in);
        int ret = connect(_sockfd, (struct sockaddr*)&addr, len);
        if (ret < 0) {
            std::cout<<"connect error\n";
            return false;
        }
        return true;
    }
    bool Recv(std::string &buf) {
        char tmp[1024] = {0};
        int ret = recv(_sockfd, tmp, 1024, 0);
        if (ret == 0) {
            std::cout<<"peer shutdown\n";
            return false;
        }else if (ret < 0) {
            std::cout<<"recv errno\n";
            return false;
        }
        buf.assign(tmp, ret);
        return true;
    }
    bool Send(std::string &buf) {
        int ret = send(_sockfd, buf.c_str(), buf.size(), 0);
        if (ret < 0) {
            std::cout<<"send error\n";
            return false;
        }
        return true;
    }
    bool Close() {
        close(_sockfd);
        _sockfd = -1;
        return true;
    }
    void SetFd(int sockfd) {
        _sockfd = sockfd;
    }
private:
    int _sockfd;

};

服务端:

#include “tcpsocket.hpp”

int main (int argc, char *argv[])
{
if (argc != 3) {
std::cout<<"./tcp_srv ip port\n";
return -1;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSocket sock;

CHECK_RET(sock.Socket());
CHECK_RET(sock.Bind(ip, port));
CHECK_RET(sock.Listen());

while(1) {
    TcpSocket client;
    if (sock.Accept(client) == false){
        continue;
    }
    std::string buf;
    client.Recv(buf);
    std::cout<<"client say:"<<buf<<std::endl;

    std::cout<<"server say:";
    fflush(stdout);
    std::cin>>buf;
    client.Send(buf);
}
sock.Close();
return 0;

}

客户端:
#include “tcpsocket.hpp”
#include <signal.h>

void sigcb(int no){
std::cout<<“recv signo:”<<no<<std::endl;
}
int main (int argc, char *argv[])
{
if (argc != 3) {
std::cout<<"./tcp_cli ip port\n";
return -1;
}
signal(SIGPIPE, sigcb);
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);

TcpSocket sock;
CHECK_RET(sock.Socket());
CHECK_RET(sock.Connect(ip, port));

while(1) {
    std::string buf;
    std::cout<<"client say:";
    fflush(stdout);
    std::cin>>buf;
    sock.Send(buf);

    buf.clear();
    sock.Recv(buf);
    std::cout<<"server say:"<<buf<<std::endl;
}
sock.Close();
return 0;

}