网络套接字编程

1.ip地址  2.port  3.网络字节序 4.浅层次tcp和udp区别  5.udp协议实现网络通信  6.tcp协议实现网络通信 

 

1.ip地址

a.作用:在网络中唯一标识一台主机

b.本质:uint32_t  无符号4个字节

c.源ip和目的ip地址

5元组 = 源ip+源端口+目的ip+目的端口+协议

ipv6:a.ipv4和ipv6制的不同版本的ip协议

          b.ipv6并不兼容ipv4,两者的ip协议的报头格式不同

          c.ipv6  16个字节

 

2.port

a.端口在一台主机中表示一个进程

b.本质:uint16_t  范围65535

c.知名端口:0-1023   mysql--3306   oracle--1521 ssh--20  http--80 

d.网络通信的时候双方都需要端口

3.网络字节序

字节序:CPU对内存当中的数据进行存取的顺序

大端字节序:低地址存放高位

小端字节序:低地址存放低位

网络字节序:大端字节序进行传输;网络标准

小端字节序转换成网络字节序:

uint32_t htonl(uint32_t hostlong)

uint32_t ntohl(uint32_t netlong)

uint16_t htons(uint16_t hostshort)

uint16_t ntohs(uint16_t netshort)

#include <iostream>
#include <stdio.h>

int main()
{
    union Data
    {
        int a;
        char b;
    }data;

    //0x00000001
    data.a = 1;

    if(data.b == 1)
    {
        printf("小端\n");
    }
    else
    {
        printf("大端\n");
    }
    return 0;
}

 

4.浅层次tcp和udp区别

    TCP

         面向连接:tcp的通信双方在发送数据之前,需要先建立连接,才可以发送数据

         可靠传输:保证TCP数据包可靠到达对端

         面向字节流:对于TCP数据可以任意存取

   UDP

        无连接:UDP通信双方在发送数据之前,不需要建立连接,只需要知道对方的ip和port就可以直接发送数据

        不可靠:如果UDP数据在网络当中传输的时候,丢失掉了,则不会保证udp数据一定到达对端

        面向数据包:UDP在发送数据的时候,是整条发送整条接收的

5.udp协议实现网络通信

UDP套接字流程接口

 服务端:

     1.创建套接字:将进程和网卡建立连接

     2.绑定地址信息:将端口和进程联系起来;绑定IP地址+Port

     3.接收数据

     4.发送数据

     5.关闭套接字

客户端( client):

     1.创建套接字

     2绑定地址信息(不推荐在客户端绑定地址信息, 1.操作系统会帮我们绑定地址信息2.如果绑定地址信息,也就是意味着固定客户端的端口了,由于端口不能同时被多个进程所占用。当前机器就只能启动一个客户端程序了; )

     3.发送数据

     4.接收数据

     5.关闭套接字

接口:

1.创建套接字的接口
     int socket(int domain, int type, int protocol)
             domain:地址域,传入协议的版本
             网络层: AF_ _INT-->ipv4版本的ip协议
                         AF_ INET6-->ipv6版本的ip协议
             type:套接字的类型
             传输层: tcp/udp ;
                         tcp : SOCK STREAM:流式套接字;默认的协议就是tcp ,不支持udp的

                         udp: SOCK DGRAM:数据报套接字;默认读的协议就是udp ,不支持tcp的protocol:协议
                         0 :采用套接字默认协议
                            tcp: IPPROTO _TCP(6)
                            udp:IPPROTO_ UDP(17)
           返回值:
                    返回套接字的操作句柄,其实就是-一个文件描述符; - -般称之为套接字描述符
2.绑定地址信息
int bind(int sockfd, const struct sockaddr* addr, socklen. _t addrlen)
          sockfd:套接字操作句柄。
         addr:
            struct sockaddr

           {
            sa_ family. _t sa. _family;//用来填充地址域的(AF_ INET),占用两个字节的;

             char sa_ data[14]; 填充地址信息; IP+ PORT

           }
             为了很好的兼容不同协议的地址信息;
           ipv4 : struct sockaddr. _in
           addrlen:地址信息长度

3.发送数据

               ssize_ _t sendto(int sockfd, const void* buf, size_ t len, int flags, const struct sockaddr* dest addr, socklen. _t addrlen);

     sockfd:套接字描述符
     buf:待发送的数据
     len:发送的数据长度
     falgs: .
           0:阻塞发送
     dest_ _addr:目标主机地址信息
     addrlen:地址信息长度


4.接收接口
    ssize_ t recvfrom(int sockfd, void* buf, size. _t len, int flags, struct sockaddr* src_ addr, socklen_ t* addrlen);

        sockfd:套接字描述符
        buf:从接收缓冲区当中拿到的数据放到哪一个buffer当中
        len:buffer的最大长度,意味着最大可以接收多少数据,预留“\0” 的位置
        flags:
            0 :阻塞接收
            src_ addr:源主机的地址信息(标识这条数据从哪一个主机上面哪一个进程来 )
            addrlen:地址信息长度,同时是一个输入输出型参数;

5.关闭套接字的接口
    close(int sockfd);
 


//udpsvr.hpp

#pragma once
#include <unistd.h>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>


class UdpSvr
{
    public:
        UdpSvr()
        {
            sock_ = -1;
        }
        ~UdpSvr()
        {

        }
        //1.Socket
        bool CreateSocket()
        {
            sock_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
            if(sock_ < 0)
            {
                perror("socket");
                return false;
            }
            return true;
        }
        //2.Bind
        bool Bind(std::string& ip, uint16_t port)
        {
            struct sockaddr_in addr;
            addr.sin_family = AF_INET;
            addr.sin_port = htons(port);
            //inet_addr:1.将点分十进制的ip转换成uint32 2.将主机字节序转换为网络字节序
            addr.sin_addr.s_addr = inet_addr(ip.c_str());
            int ret = bind(sock_, (struct sockaddr*)&addr, sizeof(addr));
            if(ret < 0)
            {
                perror("bind");
                return false;
            }
            return true;
        }
        //3.Send
        bool Send(std::string& data, struct sockaddr_in* destaddr)
        {
            int sendsize = sendto(sock_, data.c_str(), data.size(), 0, (struct sockaddr*)destaddr, sizeof(struct sockaddr_in));
            if(sendsize < 0)
            {
                perror("sendto");
                return false;
            }
            return true;
        }
        //4.Recv
        //buf : 出参, 返回给调用者接收到了什么数据
        //srcaddr : 这条数据从哪里来的,地址信息是数据源端主机的地址信息
        bool Recv(std::string* buf, struct sockaddr_in* srcaddr)
        {
            char tmp[1024];
            memset(tmp, '\0', sizeof(tmp));
            socklen_t socklen = sizeof(struct sockaddr_in);
            //如果接收缓冲区当中没有数据,且flags为0,则recvfrom会阻塞等待
            int recvsize = recvfrom(sock_, tmp, sizeof(tmp) - 1, 0, (struct sockaddr*)srcaddr, &socklen);
            if(recvsize < 0)
            {
                perror("recvfrom");
                return false;
            }

            (*buf).assign(tmp, recvsize);
            return true;
        }
        //5.Close
        void Close()
        {
            close(sock_);
            sock_ = -1;
        }
    private:
        int sock_;
};



//udpsvr.cpp
#include "udpsvr.hpp"

int main(int argc, char* argv[])
{
    // ./svr [ip] [port]
    if(argc != 3)
    {
        printf("Use this process, please \"./svr [ip] [port]\"");
        return 0;
    }
    std::string ip = argv[1];
    uint16_t port = atoi(argv[2]);

    UdpSvr us;
    if(!us.CreateSocket())
    {
        return 0;
    }

    if(!us.Bind(ip, port))
    {
        return 0;
    }

    while(1)
    {
        //接收数据
        std::string buf;
        //peeraddr 对端地址信息
        struct sockaddr_in peeraddr;
        us.Recv(&buf, &peeraddr);

        printf("cli say: %s\n", buf.c_str());
        //发送数据
        printf("server say: ");
        fflush(stdout);
        std::cin >> buf;
        us.Send(buf, &peeraddr);
    }

    us.Close();
    return 0;
}


//udpcli.cpp
#include "udpsvr.hpp"

//对于客户端程序而言,命令行参数当中的ip和port需要指定为服务端的ip和端口
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("./cli [svr_ip] [svr_port]\n");
        return 0;
    }

    std::string svr_ip = argv[1];
    uint16_t svr_port = atoi(argv[2]);

    UdpSvr us;
    if(!us.CreateSocket())
    {
        return 0;
    }

    //组织服务端的地址信息,以便sendto使用
    struct sockaddr_in destaddr;
    destaddr.sin_family = AF_INET;
    destaddr.sin_port = htons(svr_port);
    destaddr.sin_addr.s_addr = inet_addr(svr_ip.c_str());

    while(1)
    {
        //发送数据
        printf("Client Say: ");
        fflush(stdout);
        std::string buf;
        std::cin >> buf;

        us.Send(buf, &destaddr);
        //接收数据
        struct sockaddr_in peeraddr;
        us.Recv(&buf, &peeraddr);
        printf("svr say: %s\n", buf.c_str());
    }
    us.Close();
    return 0;
}

6.tcp协议实现网络通信 

TCP编程接口:

 1.创建套接字

2.bind

3.监听

      int listen(int sockfd, int backlog)

      sockfd:套接字描述符

      baclog:已完成连接的队列大小

                 backlog可以指定已完成连接的队列大小,如果当已完成连接队列被打满的,处理方式就是丢弃新来的连接;使用接收新连接,从已完成连接队列当中获取创建完成的新连接;

  会创建新的socket和客户端通信:如果只有一个socket无法区分那个是客户端发送的数据,如果有一个socket,则无法接收一个TCP连接之后,通信起来,在接受新的连接。

接收连接
    int accept(int sockfd, struct sockaddr* addr, socklen_ t* addrlen);
         sockfd:侦听sockfd ;从侦听sockfd接收新的连接请求
         addr:客户端地址信息
         addrlen:客户端地址信息长度

         返回值:
             返回新创建出来的socket ,通过新创建出来的socket和客户端进行数据收发;
             注意:因为该接口是从已完成连接队列当中获取新的连接,所以,当已完成连接队列当中没有已经完成的新的连接的时候,  该接口如果调用就会阻塞;直到获取一个新的已完成连接到来;
 

发起连接

     int connect(int sockfd, const struct sockaddr *addr, socklen_ t addrlen);sockfd:套接字描述符
             addr:服务端地址信息,需要自己在代码当中填充好,传递给connectaddrlen:地址信息长度
关闭套接字:
    int close(int sockfd);

发送数据
    ssize_ _t send(int sockfd, const void *buf, size_ t len, int flags);
         sockfd:套接字描述符,accept函数的返回值;
         buf:要发送的数据
         len:发送数据的长度
         flags:
             0 :阻塞发送

接收数据
    ssize_ _t recv(int sockfd, void* buf, size_ t len, int flags);
        sockfd:套接字描述符, accept函数的返回值;
        buf :接收到的数据放到哪里去
        len :最大接收长度

       flags:
             0 :阻塞发送

       MSG_ PEEK :探测接收的含义:从接收缓冲区当中复制数据当应用层,但是并不删除接收缓冲区当中的数据
                             相比较之前的阻塞接收而言,阻塞接收也会从接收缓冲区复制数据到应用层,但是会删除接收缓冲区当中的数据

      返回值:
            大于0 :接受了多少字节的数据
            等于0:表示对端关闭了连接,含义就是对端调用了close
            小于0 :接收失败
 

//tcpsvr.hpp
#pragma once
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
#include <string>

class TcpSvr
{
    public:
        TcpSvr()
        {
            sockfd_ = -1;
        }
        ~TcpSvr()
        {

        }
        //创建套接字
        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)
        {
            int ret = listen(sockfd_, backlog);
            if(ret < 0)
            {
                perror("listen");
                return false;
            }
            return true;
        }
        //获取连接
        //bool Accept(struct sockaddr_in* peeraddr, int* sockfd)
        //peeraddr:出参,返回客户端的地址信息
        //ts:出参,返回一个TcpSvr类的实例化指针,在这个类的实例化指针当中保存新创建出来的套接字描述符,上层调用者可以使用返回的类的实例化指针和客户端进行通信
        bool Accept(struct sockaddr_in* peeraddr, TcpSvr* ts)
        {
            socklen_t addrlen = sizeof(struct sockaddr_in);
            int serverfd = accept(sockfd_, (struct sockaddr*)peeraddr, &addrlen);
            if(serverfd < 0)
            {
                perror("accept");
                return false;
            }
            ts->sockfd_ = serverfd;
            return true;
        }
        //发起连接(client)
        bool Connect(std::string& ip, uint16_t port)
        {
            struct sockaddr_in destaddr;
            destaddr.sin_family = AF_INET;
            destaddr.sin_port = htons(port);
            destaddr.sin_addr.s_addr = inet_addr(ip.c_str());
            int ret = connect(sockfd_, (struct sockaddr*)&destaddr, sizeof(destaddr));
            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)
            {
                printf("peer shutdown connect\n");
                return false;
            }
            (*data).assign(buf, recvsize);
            return true;
        }
        //关闭套接字
        void Close()
        {
            close(sockfd_);
            sockfd_ = -1;
        }
    private:
        int sockfd_;
};


//svr.cpp
#include "tcpsvr.hpp"

int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("Start Server: ./svr [ip] [port]\n");
        return 0;
    }

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

    TcpSvr ts;
    if(!ts.CreateSocket())
    {
        return 0;
    }

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

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

    TcpSvr ser_ts;

    while(1)
    {
        struct sockaddr_in clientaddr;
        if(!ts.Accept(&clientaddr, &ser_ts))
        {
            return 0;
        }

        std::string buf;
        ser_ts.Recv(&buf);
        printf("client say: %s\n", buf.c_str());

        printf("please return to client msg: ");
        fflush(stdout);
        std::cin >> buf;
        ser_ts.Send(buf);
    }
    ser_ts.Close();//关闭新创建出来的套接字
    ts.Close();//关闭掉了侦听套接字
    return 0;
}


//cli.cpp
#include "tcpsvr.hpp"

//从命令行当中获取连接服务端的ip和port
int main(int argc, char* argv[])
{
    if(argc != 3)
    {
        printf("Start Client: ./cli [server ip] [server port]\n");
        return 0;
    }

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

    TcpSvr ts;
    if(!ts.CreateSocket())
    {
        return 0;
    }

    if(!ts.Connect(ip, port))
    {
        return 0;
    }

    while(1)
    {
        printf("please enter msg to server: ");
        fflush(stdout);
        std::string buf;
        std::cin >> buf;
        ts.Send(buf);

        //服务端没有返回数据,意味着客户端TCPsocket套接字当中用的接收缓冲区当中并没有数据,没有数据可以接收,recv的flags为0的情况下,则recv接口阻塞等待
        ts.Recv(&buf);
        printf("Server say: %s\n", buf.c_str());
    }
    ts.Close();
    return 0;
}

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值