套接字编程--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,一经查实,立即删除!

相关文章

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系统编程---6(信号的机制,信号4要素,Linu常规信号表,定时器)

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

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

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

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\"…

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;而 …

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

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

Linux系统编程----12(线程概念,Linux线程实现原理,栈中ebp指针和ebp指针,线程的优缺点和共享资源)

线程概念 什么是线程 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列” 一切进程至少都有一个执行线程线程在进程内部运行&#xff0c;本质是在进程地址空间内运行在Linux系统中&#xff0…

Linux系统编程---13(线程控制函数,创建线程,循环创建多个线程,线程间共享全局变量)

线程控制 操作系统并没有提供创建线程的系统调用接口&#xff0c;因此大佬们封装了一个线程的接口库实现线程控制。意为着用户创建线程都使用的是库函数&#xff08;所以有时候我们说创建的线程是一个用户态线程&#xff0c;但是在内核中对应有一个轻量级进程实现线程程序的调…

Linux系统编程---14(回收子线程,回收多个子线程,线程分离,杀死线程)

回收子线程 pthread_join 函数 阻塞等待线程退出&#xff0c;获取线程退出状态 其作用&#xff0c;对应进程中 waitpid() 函数。 int pthread_join (pthread_t thread,void** retval); 成功&#xff1a;0&#xff0c;失败&#xff1a;错误号 参数&#xff1a;thread&#x…

Linux系统编程----15(线程与进程函数之间的对比,线程属性及其函数,线程属性控制流程,线程使用注意事项,线程库)

对比 进程 线程 fork pthread_create exit (10) pthread_exit &#xff08;void *&#xff09; wait (int *) pthread_join &#xff08;&#xff0c;void **&#xff09;阻塞 kill pthread_cancel ();必须到取消点&#xff08;检查点&#xff09;&#xff1a;…

Linux系统编程----16(线程同步,互斥量 mutex,互斥锁的相关函数,死锁,读写锁)

同步概念 所谓同步&#xff0c;即同时起步&#xff0c;协调一致。不同的对象&#xff0c;对“同步”的理解方式略有不同。如&#xff0c;设备同步&#xff0c;是指在两 个设备之间规定一个共同的时间参考&#xff1b;数据库同步&#xff0c;是指让两个或多个数据库内容保持一致…

Linux系统编程---17(条件变量及其函数,生产者消费者条件变量模型,生产者与消费者模型(线程安全队列),条件变量优点,信号量及其主要函数,信号量与条件变量的区别,)

条件变量 条件变量本身不是锁&#xff01;但它也可以造成线程阻塞。通常与互斥锁配合使用。给多线程提供一个会合的场所。 主要应用函数&#xff1a; pthread_cond_init 函数pthread_cond_destroy 函数pthread_cond_wait 函数pthread_cond_timedwait 函数pthread_cond_signa…

Linux系统编程---18(线程池相关概念及其实现)

线程池 概念&#xff1a; 一堆线程任务队列 作用 避免大量线程频繁的创建/销毁时间成本避免瞬间大量线程创建耗尽资源&#xff0c;程序崩溃危险 实现 创建固定数量的线程创建一个线程安全的任务队列 一种线程使用模式。 线程过多会带来调度开销&#xff0c;进而影响缓…