2.编写自己的Socket

MySocket基类:

#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <cstring>
#include <string>
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/tcp.h>

using namespace std;

class MySocket{
public:
    MySocket();
    MySocket(int fd);
    int send(const char* buf, int len);
    int recv(char* buf, int len);
    void close();
    bool set_non_blocking();              //设置读写非阻塞
    bool set_send_buffer(int size);     //设置socket的发送缓冲区
    bool set_recv_buffer(int size);      //设置socket的接收缓冲区
    bool set_linger(bool active, int seconds); //若还有数据未发送,socket等待一段时间再关闭
    bool set_keepalive();                      //是否发送心跳包
    bool set_reuseaddr();                  //设置端口复用
    int fd();
protected:
    int m_sockfd = 0;     //连接描述符
    const char *m_host = "";
    unsigned int m_port = 0;
};
#include <mysocket.h>

MySocket::MySocket(){
    m_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(m_sockfd < 0){
        perror("socket error:");
    }
}

MySocket::MySocket(int fd){
    m_sockfd = fd;
}

int MySocket::send(const char* buf, int len){
    int slen = ::send(m_sockfd, buf, len, 0);
    if(slen < 0){
        perror("send");
    }
    return slen;
}

int MySocket::recv(char* buf, int len){
    int rlen = ::recv(m_sockfd, buf, len, 0);
    if(rlen < 0){
        perror("recv");
    }
    return rlen;
}

void MySocket::close(){
    ::close(m_sockfd);
}

int MySocket::fd(){
    return m_sockfd;
}

bool MySocket::set_non_blocking(){
    int flags = fcntl(m_sockfd, F_GETFL, 0);
    if(flags<0){
        printf("socket et_non_blocking error: errno=%d, errmsg=%s", errno, strerror(errno));
    }
    flags |= O_NONBLOCK;
    if(fcntl(m_sockfd, F_SETFL, flags)<0){
        printf("socket set_non_blocking error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }
    return true;
}

bool MySocket::set_send_buffer(int size){
    int buff_size=size;
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_SNDBUF, &buff_size, sizeof(buff_size)) < 0){
        printf("socket set_send_buffer error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }
    return true;
}

bool MySocket::set_recv_buffer(int size){
    int buff_size=size;
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_RCVBUF, &buff_size, sizeof(buff_size)) < 0){
        printf("socket set_send_buffer error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }
    return true;
}

bool MySocket::set_linger(bool active, int seconds){
    struct linger l;
    memset(&l, 0, sizeof(l));
    l.l_onoff = active?1:0;  //开启或关闭linger
    l.l_linger = seconds;     //linger的时间
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0){
        printf("socket set_linger error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }
    return true;
}

bool MySocket::set_keepalive(){
    int flag = 1;    //开启心跳包模式
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) < 0){     
        printf("socket set_keepalive error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }

    int idle = 10;    //10秒无数据,发送心跳包
    if(setsockopt(m_sockfd, SOL_TCP, TCP_KEEPIDLE, &idle, sizeof(idle)) < 0){     
        printf("socket set_tcpidle error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }

    int intv = 5;    //无回应,5秒后发送心跳包
    if(setsockopt(m_sockfd, SOL_TCP, TCP_KEEPINTVL, &intv, sizeof(intv)) < 0){     
        printf("socket set_tcpintv error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }

    int cnt =3;    //连续3次没有收到回应,关闭连接
    if(setsockopt(m_sockfd, SOL_TCP, TCP_KEEPCNT, &cnt, sizeof(cnt)) < 0){     
        printf("socket set_tcpcnt error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }
    return true;
}

bool MySocket::set_reuseaddr(){
    int flag = 1;
    if(setsockopt(m_sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) < 0){
        printf("socket set_reuseaddr error: errno=%d, errmsg=%s", errno, strerror(errno));
        return false;
    }
    return true;
}

SO_REUSEADDR可以用在以下四种情况下:

1、当有一个有相同本地地址和端口的socket1处于TIME_WAIT状态时,而你启动的程序的socket2要占用该地址和端口,你的程序就要用到该选项。

2、SO_REUSEADDR允许同一port上启动同一服务器的多个实例(多个进程)。但每个实例绑定的IP地址是不能相同的。在有多块网卡或用IP Alias技术的机器可以测试这种情况。

3、SO_REUSEADDR允许单个进程绑定相同的端口到多个socket上,但每个socket绑定的ip地址不同。这和2很相似,区别请看UNPv1。

4、SO_REUSEADDR允许完全相同的地址和端口的重复绑定。但这只用于UDP的多播,不用于TCP。

需要注意的是,设置端口复用函数要在绑定之前调用,而且只要绑定到同一个端口的所有套接字都得设置复用

ClientSocket类:

#include <mysocket.h>

class ClientSocket : public MySocket{
public:
    ClientSocket() : MySocket(){}
    ClientSocket(int sockfd) : MySocket(sockfd){}
    bool connect(char *ip, unsigned int port);
};
#include <clientsocket.h>

bool ClientSocket::connect(char *ip, unsigned int port){
    struct sockaddr_in sockaddr;
    memset(&sockaddr, 0, sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = inet_addr(ip);
    sockaddr.sin_port = htons(port);

    if(::connect(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0){
        perror("connect");
        return false;
    }
    else{
        m_host = ip;
        m_port = port;
        return true;
    }
}

ServerSocket类:

#include <mysocket.h>

class ServerSocket : public MySocket{
public:
    ServerSocket() : MySocket(){}
    ServerSocket(int sockfd) : MySocket(sockfd){}
    bool bind(const char *host, const int port);
    bool listen(int backlog = 128);
    int accept();
};
#include <serversocket.h>

bool ServerSocket::bind(const char *host, const int port){
    struct sockaddr_in sockaddr;
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = inet_addr(host);
    sockaddr.sin_port = htons(port);

    if(::bind(m_sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) < 0){
        perror("bind");
        return false;
    }
    else{
        m_host = (char*)host;
        m_port = port;
        return true;
    }
}

bool ServerSocket::listen(int backlog){
    if(::listen(m_sockfd, backlog) < 0){
        perror("listen");
        return false;
    }
    else{
        return true;
    }
}

int ServerSocket::accept(){
    int cfd = ::accept(m_sockfd, nullptr, nullptr);
    if(cfd < 0){
        perror("accept");
    }
    return cfd;
}

客户端程序:

#include <clientsocket.h>
#include <iostream>

using namespace std;

int main(){
    ClientSocket csocket;
    if(csocket.fd() < 0){
        exit(1);
    }

    char host[] = "127.0.0.1";
    int port = 9998;
    if(!csocket.connect(host, port)){
        csocket.close();
        exit(1);
    }

    char sendbuf[] = "This is message from client";
    if(csocket.send(sendbuf, sizeof(sendbuf)) < 0){
        csocket.close();
        exit(1);
    }

    char recvbuf[1024];
    if(csocket.recv(recvbuf, sizeof(recvbuf)) < 0){
        csocket.close();
        exit(1);
    }
    cout << recvbuf <<endl;
    csocket.close();
    return 0;
}

服务端程序:

#include <serversocket.h>
#include <clientsocket.h>
#include <iostream>

using namespace std;

int main(){
    ServerSocket ssocket;
    if(ssocket.fd() < 0){
        exit(1);
    }

    char host[] = "127.0.0.1";
    int port = 9998;
    if(!ssocket.bind(host, port)){
        ssocket.close();
        exit(1);
    }
    if(!ssocket.listen(128)){
        ssocket.close();
        exit(1);
    }

    while(true){
        int cfd = ssocket.accept();
        ClientSocket csocket(cfd);

        char recvbuf[1024];
        if(csocket.recv(recvbuf, sizeof(recvbuf)) < 0){
            csocket.close();
            continue;
        }
        cout << "Recieve: clientfd = " << cfd <<", message = " << recvbuf << endl;

        char sendbuf[] = "Server recieved your message";
        if(csocket.send(sendbuf, sizeof(sendbuf)) < 0){
            csocket.close();
            continue;
        }

        csocket.close();
    }
    ssocket.close();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值