TCP/IP

16 阅读3分钟

image.png

image.png

image.png

image.png

image.png

SERVER:

#include <iostream>
#include <sys/socket.h>
#include <string>
int main()
{
    //创建socket
    int sockfd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
    if(sockfd < 0)
    {
        printf("create %d  %s", errno, strerror(errno));
        return -1;
    }
    
    //绑定socket
    string ip = "127.0.0.1";
    int port = 8080;
    
    struct sockaddr_in sockaddr;
    std::memset(&sockaddr,0,sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    sockaddr.sin_addr.s_addr = inet_addr(ip.c_str());//转成网络地址
    sockaddr.sin_port = htons(port);//
    
    if (bind(sockfd, (struct sockaddr *)&sockaddr,sizeof(sockaddr))) < 0)
    {
        printf("bind %d  %s", errno, strerror(errno));
        close(sockfd);
        return -1;
    }
    //监听
    if(listen(sockfd, 1024) < 0)
    {
        printf("listen %d  %s", errno, strerror(errno));
        close(sockfd);
        return -1;
    }
    while(true)
    {
        //接收客户端链接
        int connfd = accept(sockfd,nullptr,nullptr);
        if(connfd < 0)
        {
            printf("accept %d  %s", errno, strerror(errno));
            break;
            //return -1;
        }
        //接收数据
        char buff[1024] = {0};
        size_t len = recv(connfd,buff,sizeof(buff),0);
        //发送数据
        char sendbuff[1024] = "4444444";
        send(connfd,sendbuff,sizeof(sendbuff),0);
        //需要的话可以close
        close(connfd);
    }
    close(sockfd);
    return 0;
}

CLIENT:

#include <iostream>

int main()
{
   //创建socket
   int sockfd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
   if(sockfd < 0)
   {
        printf("create %d  %s", errno, strerror(errno));
        return -1;
   }
   //链接服务器
   string ip = "127.0.0.1";
   int port = 8080;
   struct sockaddr_in sockaddr;
   std::memset(&sockaddr,0,sizeof(sockaddr));
   sockaddr.sin_fanily = AF_INET;
   sockaddr.sin_addr.s_addr = inet_addr(ip.c_str());
   sockaddr-sin_port = htons(port);
   if(connect(sockfd,(struct sockaddr*)&sockaddr,sizeof(sockaddr)) < 0)
   {
       printf("connect %d  %s", errno, strerror(errno));
       close(sockfd);
        return -1;
   }
   //发送数据
   string data = "33333";
   send(sockfd, data.c_str(),data.size(),0)
   //接收数据
   char buff[1024] = {0};
   recv(sockfd,buff,sizeof(buff),0);
   close(sockfd);
   return 0;
}

SOCK封装

class Socket
{
public:
    Socket();
    ~Socket();
    bool bind(const string &ip, int port);
    bool listen(int flag);
    bool connect(const string &ip, int port);
    int accept();
    int send(const char* buff, int bufflen);
    int recv(char* buff, int bufflen);
    void close();
private:
    string m_ip;
    int m_port;
    int m_sockfd;
};
Socket::Socket():m_ip(""), m_port(0),m_sockfd(0)
{
   m_sockfd = socket(AF_INET, SOCK_STREAM,IPPROTO_TCP);
   if(m_sockfd < 0)
   {
        printf("create %d  %s", errno, strerror(errno));
   }
}
Socket::~Socket()
{
    close();
}
bool Socket::bind(const string &ip, int port)
{
    struct sockaddr_in sockaddr;
    std::memset(&sockaddr,0,sizeof(sockaddr));
    sockaddr.sin_family = AF_INET;
    if(ip.empty())
    {
        sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定任意网卡并转成网络地址
    }
    else
    {
         sockaddr.sin_addr.s_addr = inet_addr(ip.c_str());//转成网络地址
    }
    
    sockaddr.sin_port = htons(port);//
        
    if (bind(m_sockfd, (struct sockaddr *)&sockaddr,sizeof(sockaddr))) < 0)
        {
            printf("bind %d  %s", errno, strerror(errno));
            close(sockfd);
            return false;
        }
    m_ip = ip;
    m_port = port;
    return true;
}
void Socket::close()
{
    if(m_sockfd > 0)
    {
        ::close(m_sockfd);
    }
}

阻塞IO 当用户线程发出IO请求之后,内核会去查看数据是否就绪,如果没有就绪就会等待数据就绪,而用户线程就会 处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用 户线程才解除阻塞状态。

非阻塞IO 当用户线程发起read操作后,并不需要等待,而是马上得到结果。如果结果是一个error时,它就知道数据还没 有准备好,于是它可以再次发起read操作。如果内核中的数据准备好了,它就将数据拷贝到用户线程。

在非阻塞IO模型中,用户线程需要不断地轮询内核数据是否就绪,也就是说非阻塞IO不会交出CPU,而会一直占用CPU。

发送快,网卡从缓冲区取得慢,就会阻塞,缓冲区大小可以设置,都有默认大小 setsockopt(fd, SOL_SOCKET,SO_SNDBUF,1024) image.png

setsockopt(fd, SOL_SOCKET,SO_RCVBUF,1024)

image.png

image.png

image.png

image.png

地址复用 image.png

image.png

image.png fcntl函数: 在socket编程中,我们常用的是F_GETFL和F_SETFL来设置非阻塞(O_NONBLOCK)等标志。

示例:将文件描述符设置为非阻塞

int flags = fcntl(fd, F_GETFL, 0);
if (flags == -1) {
// 错误处理
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
// 错误处理
}

I/O多路复用

image.png

image.png

image.png

所有异常均未处理
#define MAX_EVENTS 10
#define PORT 8080
#define BUFFER_SIZE 1024
int main()
{ 
    struct epoll_event ev, events[MAX_EVENTS];
    int listen_sock, conn_sock, nfds, epollfd;
    struct sockaddr_in address;
    listen_sock = socket(AF_INET,SOCK_STREAM,0);
    //设置socket选项
    int opt = 1;
    setsockopt(listen_sock,SOL_SOCKET,SO_REUSEADDR,&opt);//地址复用
    address.sin_famiy = AF_INET;
    address.sin_addr.s_addr = INADDR_ANT;
    address.sin_port = htons(PORT);
    bind(listen_sock, (atruct socladdr*)&address,sizeof(address));
    listen(listen_sock,10);
    
    //创建epoll
    epollfd = epoll_create(0);
    //创建事件
    ev.events = EPOLLIN;
    ev.data.fd = listen_sock;
    //添加到epoll
    epoll_ctl(epollfd,EPOLL_CTL_ADD,listen_sock,&ev);
    
    while(true)
    {
        nfds = epoll_wait(epollfd,events,MAX_EVENTS,-1);
        for(int i = 0;i < nfds;i++)
        {
            if(events[i].data.fd == listen_sock)
            {
                conn_sock = accept(listen_sock,NULL,NULL);
                struct epoll_event epoll_e;
                epoll_e.events = EPOLLIN | EPOLLET;
                epoll_e.data.fd = conn_sock;
               epoll_ctl(epollfd,EPOLL_CTL_ADD,conn_sock,&epoll_e);
            }
            else if(events[i].data.fd == conn_sock)
            {
                char buff[1024] = {0};
                read(events[i].data.fd,buff,sizeof(buff));
            }

        }
        
    }
}