一、UDP编程
UDP特点:
UDP的特点:不要连接,只管发送,数据因此不稳定,易丢包。
UDP与TCP不同之处:
1>没有服务器跟客户端的观念
2>没有accept和connect
3>UDP实际对于网络需求略高一点
UDP框架:
发送端 :
1>创建socket套接字
2>绑定自己的IP地址和端口号
3>声明别人的IP地址和端口号
4>发送数据(函数有变化)
接收端:
1>创建socket套接字
2>绑定自己的IP地址和端口号
3>声明别人的IP地址和端口号
4>接收数据(函数有变化)
UDP函数学习
1>sendto
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int sockfd, const void *buf, int len, unsigned int flags,
const struct sockaddr *dest_addr, int addrlen);
功能:
发送数据(用第五参数定位对方的IP地址和端口号)
参数:
sockfd:套接字
buf:发送缓冲区
len: 发送缓冲区的长度
flags:默认为0
dest_addr:结构体(包含目标的IP和端口号)
addrlen:结构体的长度
返回值:
成功返回发送字节数
失败,返回-1,并设置错误码
2>recvfrom
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int sockfd, const void *buf, int len, unsigned int flags,
const struct sockaddr *src_addr, socklen_t *addrlen);
功能:
接收数据(用第五参数定位对方的IP地址和端口号)
参数:
sockfd:套接字
buf:接收缓冲区
len: 接收缓冲区的长度
flags:默认为0
src_addr:结构体(包含目标的IP和端口号)
addrlen:结构体的长度的指针
返回值:
成功返回接收字节数
失败,返回-1,并设置错误码
发送端代码案例如下:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int main(int argc,char *argv[]) { if(argc<5) { printf("请输入<./可执行> <自己IP> <自己端口号> <目标IP> <目标端口号>\n"); return -1; } //1>创建socket套接字 int sfd; sfd = socket(AF_INET,SOCK_DGRAM,0);//注意换UDP类型 if(sfd<0) { perror("socket"); return -1; } //2>绑定自己的IP和端口号 struct sockaddr_in myself; myself.sin_family = AF_INET; myself.sin_port = htons(atoi(argv[2])); //9000 myself.sin_addr.s_addr = inet_addr(argv[1]); if(bind(sfd,(struct sockaddr *)&myself,sizeof(myself))<0) { perror("bind"); return -1; } //3>声明别人的IP和端口号 struct sockaddr_in other; other.sin_family = AF_INET; other.sin_port = htons(atoi(argv[4])); //8888 other.sin_addr.s_addr = inet_addr(argv[3]); //4>发送数据 char buf[50]; while(1) { bzero(buf,sizeof(buf)); scanf("%s",buf); sendto(sfd,buf,strlen(buf),0,(struct sockaddr *)&other,sizeof(other)); } close(sfd); return 0; }
接收端代码案例如下:
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <netinet/in.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <pthread.h> int main(int argc,char *argv[]) { if(argc<5) { printf("请输入<./可执行> <自己IP> <自己端口号> <目标IP> <目标端口号>\n"); return -1; } //1>创建socket套接字 int sfd; sfd = socket(AF_INET,SOCK_DGRAM,0);//注意换UDP类型 if(sfd<0) { perror("socket"); return -1; } //2>绑定自己的IP和端口号 struct sockaddr_in myself; myself.sin_family = AF_INET; myself.sin_port = htons(atoi(argv[2])); //8888 myself.sin_addr.s_addr = inet_addr(argv[1]); if(bind(sfd,(struct sockaddr *)&myself,sizeof(myself))<0) { perror("bind"); return -1; } //3>声明别人的IP和端口号 struct sockaddr_in other; other.sin_family = AF_INET; other.sin_port = htons(atoi(argv[4])); //9000 other.sin_addr.s_addr = inet_addr(argv[3]); //4>接受数据 char buf[50]; int len = sizeof(other); while(1) { bzero(buf,sizeof(buf)); recvfrom(sfd,buf,sizeof(buf),0,(struct sockaddr *)&other,&len); printf("发送方说:%s\n",buf); } close(sfd); return 0; }
二、多路复用
前提讲述
IO阻塞和IO非阻塞
int fcntl(int fd, int cmd, long arg); //改变文件描述符的特性
int flag;
flag = fcntl(sockfd, F_GETFL, 0);//F_GETFL获取文件描述符的特性
flag |= O_NONBLOCK;//配置非阻塞
fcntl(sockfd, F_SETFL, flag);//设置文件描述符特性为非阻塞
select
select int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); 功能:就是将你要关心的文件描述符放入一个集合中,将这个集合交给内核判断,当集合中 某个文件描述符被触发时,解除阻塞。 参数: nfds:最大文件描述符+1 readfds:读集合 writefds:写集合 exceptfds:异常集合 timeout:超时时间 返回值: 成功返回那个被触发的文件描述符 失败,返回-1,并设置错误码 struct timeval { long tv_sec; /* seconds */ 秒 long tv_usec; /* microseconds */ 微妙 }; void FD_ZERO(fd_set *fdset) 清除文件描述符集合 void FD_SET(int fd,fd_set *fdset) 将你要关心的文件描述符放入集合中 void FD_CLR(int fd,fd_set *fdset) 将文件描述符移除集合 int FD_ISSET(int fd,fd_set *fdset) 判断文件描述符是否在集合中 fd_set是一个数据类型,本质是一个字节数组。长度为1024. readfds:读集合,往读集合放入我们关心的0,serfd描述符 当select解除阻塞,说明这两个文件描述符有一个被触发了。 一旦有文件描述符被触发,将移除集合中未触发的。再利用FD_ISSET去判断
poll
poll #include <poll.h> int poll(struct pollfd* fds, nfds_t nfds, int timeout) 功能: 多路复用,看哪个文件描述符就绪,做对应操作,看结构体第三参数是否被内核改变 参数: fds:结构体数组,记 nfds:要判断的文件描述符个数 timeout:超时时间,0表示不阻塞;>0,阻塞的时间;默认设置为-1表示阻塞 返回值: 成功返回0 失败返回-1,并设置错误码 struct pollfd { int fd; // 委托内核检测的文件描述符 你希望监听文件描述符0 用户设置 short events; // 委托内核检测文件描述符的什么事件 监听它被触发,也就是有数据可读POLLIN 用户设置 short revents // 文件描述符实际发生的事件 内核设置 }
三、图解如下
总结
关于C/C++网络编程基础知识超详细讲解第二部分的详解,懒大王就先分享到这里了,如果你认为这篇文章对你有帮助,请给懒大王点个赞点个关注吧,如果发现什么问题,欢迎评论区留言!!💕💕