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

传输层的协议:

ip地址:

在网络中唯一标识一台主机

  1. IPV4:uint32_t DHCP NAT
  2. IPV6 : uint8_t addr[16] —向前并不兼容IPV4
    每一条数据都必须包含源地址和目的地址:因为每条网络中的数据都必须确定是从那个主机来到那个主机去

端口号

  1. 在一台主机上唯一标识一个进程
  2. 在一台主机上,当网卡收到网络数据,操作系统能够通过端口分辨出,这个数据该交给拿个进程处理
  3. uint16_t:端口号范围:0~65535,0~1023这些端口不推荐用户使用,因为0~1023这些端口已经被一些知名协议所占用,或作为预留
  4. 一个端口号只能被一个进程所占用,一个进程可以使用多个端口号
  5. 每一条数据都必须包含源端口和目的端口;因为每条网络中的数据都必须确定自己是哪个进程发送的,因该哪个进程进行处理

tcp协议;

传输控制协议—面向连接,可靠传输,面向字节流,实现数据可靠传输,传输灵活但是会有粘包问题

udp协议

用户数据报协议–无连接,不可靠,面向数据报,实现不可靠传输,传输不够灵活,但是不会存在粘包问题

网络字节序

内存中的多字节数据相对于内存地址有大端和小端之分
磁盘文件中的多字节数据相对于文件中的偏 移地址也有大端小端之分,
网络数据流同样有大端小端之分
那么如何定义网络数据流的地址呢?

  1. 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  2. 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  3. 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址.
  4. TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节.
  5. 不管这台主机是大端机还是小端机, 都会按照这个TCP/IP规定的网络字节序来发送/接收数据;
  6. 如果当前发送主机是小端, 就需要先将数据转成大端; 否则就忽略, 直接发送即可;

字节序

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结构

在这里插入图片描述

  1. IPv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位地址类型, 16 位端口号和32位IP地址.
  2. IPv4、IPv6地址类型分别定义为常数AF_INET、AF_INET6. 这样,只要取得某种sockaddr结构体的首地址, 不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容.
  3. socket API可以都用struct sockaddr *类型表示, 在使用的时候需要强制转化成sockaddr_in; 这样的好 处是程序的通用性, 可以接收IPv4, IPv6, 以及UNIX Domain Socket各种类型的sockaddr结构体指针做为 参数;

UDP编程

UDP编程接口

  1. 创建套接字----通过套接字使进程与网卡之间建立联系(内核中创建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

  2. 为套接字绑定地址信息
    int bind(int socket, const struct sockaddr *address, socklen_t address_len);
    参数1:创建套接字返回的套接字描述符
    参数2:地址信息
    参数3:地址信息长度
    返回值:成功:0 失败:-1

  3. 接收数据

    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

  4. 发送数据:

    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

  5. 关闭套接字

     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;
}

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/383248.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ARP简单介绍

ARP简介 ARP&#xff08;Address Resolution Protocol&#xff09;用于将IP地址解析为MAC地址 1. ARP地址解析的必要性 IP地址不能直接用来进行通信&#xff0c;因为网络设备只能识别MAC地址。IP地址只是主机在网络层中的地址&#xff0c;如果要将网络层中传送的数据报交给…

Linux系统编程--3(exec 函数族,僵尸进程和孤儿进程,wait和wait_pid回收子进程)

exec 函数族 fork 创建子进程后执行的是和父进程相同的程序&#xff08;但有可能执行不同的代码分支&#xff09; &#xff0c;子进程往往要调用一种 exec 函数以执行另一个程序。当进程调用一种 exec 函数时&#xff0c;该进程的用户空间代码和数据完全被新程序替换&#xff…

交换机MAC地址学习和转发数据帧的原理

1 &#xff1a;交换机 MAC 地址学习在交换机初始化的&#xff0c;也就是刚启动的时候&#xff0c;交换机的MAC地址表是没有任何MAC地址和端口的映射条目的 当PCA要想和PCC&#xff0c;PCB,PCD进行通信时&#xff0c;当该二层数据帧通过端口E1/0/1发送到交换机上时&#xff0c…

Linux系统编程---4(进程间通信IPC,管道)

进程间通信目的 数据传输&#xff1a;一个进程需要将它的数据发送给另一个进程资源共享&#xff1a;多个进程之间共享同样的资源。通知事件&#xff1a;一个进程需要向另一个或一组进程发送消息&#xff0c;通知它&#xff08;它们&#xff09;发生了某种事件&#xff08;如进…

冲突域 广播域简单解释

网络互连设备可以将网络划分为不同的冲突域、广播域。但是&#xff0c;由于不同的网络互连设备可能工作在OSI模型的不同层次上。因此&#xff0c;它们划分冲突域、广播域的效果也就各不相同。如中继器工作在物理层&#xff0c;网桥和交换机工作在数据链路层&#xff0c;路由器工…

Linux系统编程---5(共享存储映射,存储映射I/O,mmap函数,父子进程间通信,匿名映射)

共享存储映射 文件进程间通信 使用文件也可以完成 IPC&#xff0c;理论依据是&#xff0c;fork 后&#xff0c;父子进程共享文件描述符。也就共享打开的文件。 编程&#xff1a;父子进程共享打开的文件。借助文件进行进程间通信。 测试代码 /*** 父子进程共享打开的文件描述…

变量的存取

一、预备知识―程序的内存分配 一个由c/C编译的程序占用的内存分为以下几个部分 1、栈区&#xff08;stack&#xff09;― 由编译器自动分配释放 &#xff0c;存放函数的参数值&#xff0c;局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区&#xff08;heap&#xff…

Linux下文件的多进程拷贝

大文件拷贝 假设有一个超大文件&#xff0c;需对其完成拷贝工作。为提高效率&#xff0c;可采用多进程并行拷贝的方法来实现。假设文件 大小为 len&#xff0c;共有 n 个进程对该文件进行拷贝。那每个进程拷贝的字节数应为 len/n。但未必一定能整除&#xff0c;我们可 以选择让…

linux下cron定时任务的总结

cron是linux系统下一个自动执行指定任务的程序&#xff0c;即包含“时间”、“路径”、“自动执行脚本”等要素 当我们要增加全局性的计划任务时&#xff0c;一种方式是直接修改/etc/crontab。但是&#xff0c;一般不建议这样做&#xff0c;/etc/cron.d目录就是为了解决这种问…

Linux系统编程---6(信号的机制,信号4要素,Linu常规信号表,定时器)

信号的概念 信号在我们的生活中随处可见&#xff0c; 如&#xff1a;古代战争中摔杯为号&#xff1b;现代战争中的信号弹&#xff1b;体育比赛中使用的信号枪… 他们都有共性&#xff1a; 简单不能携带大量信息&#xff0c;只能带一个标志。满足某个特设条件才发送。 Unix 早…

python httplib2的安装

window下python安装httplib2 https://pypi.python.org/pypi/httplib2地址下下载httplib2安装包&#xff0c;并解压缩 方法一、我的电脑->属性->高级->环境变量->系统变量 在系统变量里找到PATH&#xff0c;双击PATH&#xff0c;在结尾加上 ";C:\Python25&…

Linux系统编程----7(信号集,信号屏蔽,信号捕捉)

信号集操作函数 内核通过读取未决信号集来判断信号是否应被处理。信号屏蔽字 mask 可以影响未决信号集。而我们可以在应 用程序中自定义 set 来改变 mask。已达到屏蔽指定信号的目的。 信号集设定 sigset_t set; //typedef unsigned long sigset_t;int sigemptyset(sigset_t…

Linux系统编程----8(竞态条件,时序竞态,pause函数,如何解决时序竞态)

竞态条件(时序竞态)&#xff1a; pause 函数 调用该函数可以造成进程主动挂起&#xff0c;等待信号唤醒。调用该系统调用的进程将处于阻塞状态(主动放弃 cpu) 直 到有信号递达将其唤醒&#xff0c;等不到一直等 int pause(void); 返回值&#xff1a;-1 并设置 errno 为 EINTR…

Linux系统编程---8(全局变量异步I/O,可重入函数)

全局变量异步 I/O 分析如下父子进程交替 数数 程序。当捕捉函数里面的 sleep 取消&#xff0c;程序即会出现问题。请分析原因。 #include<stdio.h> #include<signal.h> #include<unistd.h> #include<stdlib.h>intn0,flag0; void sys_err(char* s…

http使用post上传文件时,请求头和主体信息总结

请求头必须配置如下行&#xff1a; Content-Type : multipart/form-data; boundary---12321 boundary---12321位文件的分界线 body如下&#xff1a; "-----12321\r\n" //分割文件时加-- "Content-Disposition: form-data; name\"…

iconv 文件编码转换

iconv 文件编码转换 http://qq164587043.blog.51cto.com/261469/63349 linux shell 配置文件中默认的字符集编码为UTF&#xff0d;8 。UTF&#xff0d;8是unicode的一种表达方式&#xff0c;gb2312是和unicode都是字符的编码方式&#xff0c;所以说gb2312跟utf&#xff0d;8的…

Linu系统编程---9(SIGCHLD 信号,信号传参,中断系统调用)

SIGCHLD 信号 SIGCHLD 的产生条件 子进程终止时子进程接收到 SIGSTOP 信号停止时子进程处在停止态&#xff0c;接受到 SIGCONT 后唤醒时 借助 SIGCHLD 信号回收子进程 子进程结束运行&#xff0c;其父进程会收到 SIGCHLD 信号。该信号的默认处理动作是忽略。可以捕捉该信号…

Linu系统编程---10(Linux的终端,线路规程,网络终端,进程组)

终端 输入输出设备的总称 在 UNIX 系统中&#xff0c;用户通过终端登录系统后得到一个 Shell 进程&#xff0c;这个终端成为 Shell 进程的控制终端&#xff08;Controlling Terminal&#xff09;&#xff0c; 进程中&#xff0c;控制终端是保存在 PCB 中的信息&#xff0c;而 …

PCRE函数简介和使用示例

PCRE是一个NFA正则引擎&#xff0c;不然不能提供完全与Perl一致的正则语法功能。但它同时也实现了DFA&#xff0c;只是满足数学意义上的正则。 PCRE提供了19个接口函数&#xff0c;为了简单介绍&#xff0c;使用PCRE内带的测试程序(pcretest.c)示例用法。 1. pcre_compile 原型…

Linux系统编程---11(会话,守护进程,创建守护进程)

会话 创建会话 创建一个会话需要注意以下6点注意事项 调用进程不能是进程组组长&#xff0c;该进程变成新会话首进程该进程成为一个新进程组的组长进程需要root权限&#xff08;nbuntu不需要&#xff09;新会话丢弃原有的控制终端&#xff0c;该会话没有控制终端该调用进程是…