日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

套接字编程--1(UDP协议编程,端口号,传输层协议,网络字节序)

發布時間:2023/11/30 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 套接字编程--1(UDP协议编程,端口号,传输层协议,网络字节序) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

傳輸層的協議:

ip地址:

在網絡中唯一標識一臺主機

  • IPV4:uint32_t DHCP NAT
  • IPV6 : uint8_t addr[16] —向前并不兼容IPV4
    每一條數據都必須包含源地址和目的地址:因為每條網絡中的數據都必須確定是從那個主機來到那個主機去
  • 端口號

  • 在一臺主機上唯一標識一個進程
  • 在一臺主機上,當網卡收到網絡數據,操作系統能夠通過端口分辨出,這個數據該交給拿個進程處理
  • uint16_t:端口號范圍:0~65535,0~1023這些端口不推薦用戶使用,因為0~1023這些端口已經被一些知名協議所占用,或作為預留
  • 一個端口號只能被一個進程所占用,一個進程可以使用多個端口號
  • 每一條數據都必須包含源端口和目的端口;因為每條網絡中的數據都必須確定自己是哪個進程發送的,因該哪個進程進行處理
  • tcp協議;

    傳輸控制協議—面向連接,可靠傳輸,面向字節流,實現數據可靠傳輸,傳輸靈活但是會有粘包問題

    udp協議

    用戶數據報協議–無連接,不可靠,面向數據報,實現不可靠傳輸,傳輸不夠靈活,但是不會存在粘包問題

    網絡字節序

    內存中的多字節數據相對于內存地址有大端和小端之分
    磁盤文件中的多字節數據相對于文件中的偏 移地址也有大端小端之分,
    網絡數據流同樣有大端小端之分
    那么如何定義網絡數據流的地址呢?

  • 發送主機通常將發送緩沖區中的數據按內存地址從低到高的順序發出;
  • 接收主機把從網絡上接到的字節依次保存在接收緩沖區中,也是按內存地址從低到高的順序保存;
  • 因此,網絡數據流的地址應這樣規定:先發出的數據是低地址,后發出的數據是高地址.
  • TCP/IP協議規定,網絡數據流應采用大端字節序,即低地址高字節.
  • 不管這臺主機是大端機還是小端機, 都會按照這個TCP/IP規定的網絡字節序來發送/接收數據;
  • 如果當前發送主機是小端, 就需要先將數據轉成大端; 否則就忽略, 直接發送即可;
  • 字節序

    cpu在內存中對數據的存取順序
    大段字節序:低地址存高位
    小端字節序:低地址存高位
    主機字節序的大小端取決于cpu架構:x86-little,mips-big
    如何判斷主機字節序:union聯合體
    網絡字節序:為了避免兩臺不同字節序主機之間通信會造成數據二義性的情況,因此規定在網絡中進行通信的時候,使用網絡字節序;而網絡字節序就是大端字節序
    網絡通信時,關注的數據字節序轉換:存儲大于一個字節類型的數據

    socket編程

    socket 常見API

    // 創建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器) int socket(int domain, int type, int protocol);

    // 綁定端口號 (TCP/UDP, 服務器) int bind(int socket, const struct sockaddr *address, socklen_t address_len);

    // 開始監聽socket (TCP, 服務器) int listen(int socket, int backlog);

    // 接收請求 (TCP, 服務器) int accept(int socket, struct sockaddr* address, socklen_t* address_len);

    // 建立連接 (TCP, 客戶端) int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

    sockaddr結構

  • IPv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結構體表示,包括16位地址類型, 16 位端口號和32位IP地址.
  • IPv4、IPv6地址類型分別定義為常數AF_INET、AF_INET6. 這樣,只要取得某種sockaddr結構體的首地址, 不需要知道具體是哪種類型的sockaddr結構體,就可以根據地址類型字段確定結構體中的內容.
  • socket API可以都用struct sockaddr *類型表示, 在使用的時候需要強制轉化成sockaddr_in; 這樣的好 處是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各種類型的sockaddr結構體指針做為 參數;
  • UDP編程

    UDP編程接口

  • 創建套接字----通過套接字使進程與網卡之間建立聯系(內核中創建socket結構體)
    int socket(int domain, int type, int protocol);
    參數1:地址域。 AF_INET–使用IPv4網絡協議地址域
    參數2:套接字類型

    SOCK_STREAM 流式套接字-提供字節流服務,默認使用TCP協議SOCK_DGRAM 數據報套接字---提供數據報傳輸服務,默認使用UDP協議

    參數3:傳輸層協議

    0 根據套接字類型使用默認協議IPPROTO_TCP TCP協議IPPROTO_UDP UDP協議

    返回值:套接字描述符 失敗返回-1

  • 為套接字綁定地址信息
    int bind(int socket, const struct sockaddr *address, socklen_t address_len);
    參數1:創建套接字返回的套接字描述符
    參數2:地址信息
    參數3:地址信息長度
    返回值:成功:0 失敗:-1

  • 接收數據

    ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);

    參數1:套接字描述符
    參數2:用于接受數據的緩沖區
    參數3:想要接受的數據長度(限制buf越界)
    參數4:選項標志(0表示阻塞接受數據)
    參數5:發送端地址信息
    參數6:地址信息長度(輸入輸出型參數,防止參數5越界。指定想要的長度,返回實際的長度)
    返回值:返回實際接受的數據長度 失敗返回-1

  • 發送數據:

    ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);

    參數1:套接字描述符
    參數2:要發送的數據
    參數3:要發送的數據長度
    參數4:要發送的長度
    參數5:0默認阻塞發送
    參數6:目的端地址
    返回值:實際的發送長度,失敗返回-1

  • 關閉套接字

    int close(int fd);

    fd :套接字描述符
    返回值:0,失敗返回-1

  • 通信模型

    c++封裝UDP–socket類

    udpsocket.hpp

    /* *實現一個UdpSocket類,向外提供方便的套接字操作接口**bool Socket() 創建套接字*bool Bind(std::string &ip,uint16_t port)*bool Recv(std::string &buf,std::string &ip,uint16_t port)*bool Send(std::string &buf,std::string &ip,unit16_t port)*bool Close()**/ #include<iostream> #include<stdio.h> #include<unistd.h> #include<errno.h> #include<string> #include<netinet/in.h> #include<sys/socket.h> #include<arpa/inet.h> #define CHECK_RET(q) if((q)==false){return -1;}class UdpSocket {public:UdpSocket():_sockfd(-1){}~UdpSocket(){close(_sockfd);}bool Socket(){//int socket(int domain, int type, int protocol);_sockfd=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);if(_sockfd<0){ perror("socket error");return false;}}bool Bind(std::string &ip,uint16_t port){struct sockaddr_in addr;//addr.sin_family=AF_INET;//地址域//因為端口和地址信息都是要在網絡上傳輸,所以需要地址轉換//uint32_t htonl(uint32_t hostlong);//將32的數據從主機字節序轉換為網絡字節序//uint16_t htons(uint16_t hostshort);//將16位的數據從主機字節序轉換為網絡字節序//uint32_t ntohl(uint32_t netlong);//將32位的數據從網絡字節序轉換位主機字節序//uint16_t ntohs(uint16_t netshort);//將16位的數據從網絡字節序轉換為主機字節序addr.sin_port=htons(port);//端口信息//in_addr_t inet_addr(const char *cp);//將字符串點分十進制IP地址轉換為網絡字節序IP地址// char *inet_ntoa(struct in_addr in);// 將網絡字節序IP地址轉換成為字符串點分十進制IP地址addr.sin_addr.s_addr=inet_addr(ip.c_str());// int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);socklen_t len=sizeof(struct sockaddr_in);int ret=bind(_sockfd,(sockaddr *)&addr,len);if(ret<0){perror("bind error");return false;}return true;}bool Recv(std::string &buf,std::string &ip,uint16_t &port){//ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,// struct sockaddr *src_addr, socklen_t *addrlen);char tmp[4096]={0};struct sockaddr_in addr;socklen_t len=sizeof(struct sockaddr_in);//不但接受數據,還接受誰給他發的數據int ret=recvfrom(_sockfd,tmp,4096,0,(sockaddr*)&addr,&len);if(ret<0){perror("recv error");return false;}//截取一段數據,放到buf里buf.assign(tmp,ret);ip=inet_ntoa(addr.sin_addr);port=ntohs(addr.sin_port);return true;} bool Send(std::string &buf,std::string &ip,uint16_t port){//ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,// const struct sockaddr *dest_addr, socklen_t addrlen); 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=sendto(_sockfd,buf.c_str(),buf.size(),0,(struct sockaddr*)&addr,len);if(ret<0){perror("sendto error");return false;}return true;}bool Close(){if(_sockfd>=0){close(_sockfd);_sockfd=-1;}}private:int _sockfd;};

    服務端程序:

    #include"udpsocket.hpp"int main(int argc,char *argv[]) {if(argc!=3){printf("./udp_srv ip port\n");return -1; } std::string srv_ip=argv[1];uint16_t srv_port =atoi(argv[2]);UdpSocket sock;CHECK_RET(sock.Socket());//創建套接字CHECK_RET(sock.Bind(srv_ip,srv_port));//綁定地址信息while(1){std::string cli_ip;uint16_t cli_port;std::string buf;sock.Recv(buf,cli_ip,cli_port);printf("client-[%s:%d]--say:%s\n",cli_ip.c_str(),cli_port,buf.c_str());buf.clear();printf("server say:");fflush(stdout);std::cin>>buf;sock.Send(buf,cli_ip,cli_port);} sock.Close();return 0;}

    客戶端程序:

    #include"udpsocket.hpp"int main(int argc,char *argv[]) {if(argc!=3){std::cout<<"./udp_cli ip port";return -1; } std::string srv_ip=argv[1];uint16_t srv_port=atoi(argv[2]);UdpSocket sock;CHECK_RET(sock.Socket());//創建套接字while(1){std::string buf;std::cout<<"client say:",fflush(stdout);std::cin>>buf;sock.Send(buf,srv_ip,srv_port);buf.clear();sock.Recv(buf,srv_ip,srv_port);std::cout<<"server say:"<<buf<<std::endl;} sock.Close();return 0; }


    總結

    以上是生活随笔為你收集整理的套接字编程--1(UDP协议编程,端口号,传输层协议,网络字节序)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。