套接字编程--1(UDP协议编程,端口号,传输层协议,网络字节序)
傳輸層的協議:
ip地址:
在網絡中唯一標識一臺主機
每一條數據都必須包含源地址和目的地址:因為每條網絡中的數據都必須確定是從那個主機來到那個主機去
端口號
tcp協議;
傳輸控制協議—面向連接,可靠傳輸,面向字節流,實現數據可靠傳輸,傳輸靈活但是會有粘包問題
udp協議
用戶數據報協議–無連接,不可靠,面向數據報,實現不可靠傳輸,傳輸不夠靈活,但是不會存在粘包問題
網絡字節序
內存中的多字節數據相對于內存地址有大端和小端之分
磁盤文件中的多字節數據相對于文件中的偏 移地址也有大端小端之分,
網絡數據流同樣有大端小端之分
那么如何定義網絡數據流的地址呢?
字節序
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結構
UDP編程
UDP編程接口
創建套接字----通過套接字使進程與網卡之間建立聯系(內核中創建socket結構體)
int socket(int domain, int type, int protocol);
參數1:地址域。 AF_INET–使用IPv4網絡協議地址域
參數2:套接字類型
參數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协议编程,端口号,传输层协议,网络字节序)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux系统编程--2(环境变量,进程
- 下一篇: webpack --watch以后报错e