UDP socket 编程以及IO多路复用
一)什么是UDP
提供无连接,不可靠的信息传输服务,
该协议不对传输的数据包排序,不需要接收方应答确认信息
应用程序需自己构建发送数据的顺序和确认机制
与TCP协议相比具有速度优势
UDP通常用在资源消耗小,处理速度要求快的场合,如音频和视频的传输。
二)UDP socket 编程
服务器:
1.创建服务器套接字用于唯一标识服务器
int connfd = socket(AF_INET,SOCK_DGRAM,0);//SOCK_DGRAM:描述要创建的套接字类型为数据报套接字类型
if(connfd < 0){
perror("socket");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = SOCK_DGRAM;
saddr.sin_port = htons(12345);
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
int ret = bind(connfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret < 0){
perror("bind");
return -1;
}
struct sockaddr_in caddr;
int clen = sizeof(caddr);
char buf[56] = {0};
if(recvfrom(connfd,buf,sizeof(buf),0,(struct sockaddr *)&caddr,&clen) < 0){
perror("recvfrom");
return -1;
}
printf("client->%s\n",buf);
printf("已接收到客户端消息,成功与客户端建立连接!\n");
strcpy(buf,"I Love You");
ret = sendto(connfd,buf,sizeof(buf),0,(struct sockaddr *)&caddr,sizeof(caddr));
if(ret < 0){
perror("sendto");
return -1;
}
客户端
1.创建套接字唯一标识客户端
int connfd = socket(AF_INET,SOCK_DGRAM,0);
if(connfd < 0){
perror("connfd");
return -1;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(12345);
saddr.sin_addr.s_addr = inet_addr("192.168.64.137");
3.向服务器发送信息以及客户端的IP和端口号
char buf[56] = "xieting";
int ret = sendto(connfd,buf,sizeof(buf),0,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret < 0){
perror("sendto");
return -1;
}
4.接收服务器的反馈消息
struct sockaddr_in oaddr;
int olen = sizeof(oaddr);
char back[56] = {0};
ret = recvfrom(connfd,back,sizeof(buf),0,(struct sockaddr *)&oaddr,&olen);
if(ret < 0){
perror("recvfrom");
return -1;
}
printf("server->%s\n",back);
结果
xieting
已接收到客户端消息,成功与客户端建立连接!
server->I Love You
二)IO多路复用
1.什么是IO多路复用 I/O多路复用就是通过一种机制,一个进程可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
2.IO多路复用的基本步骤 -
创建一个文件描述符集合 -
将要关注的文件描述符加入这个集合 -
调用select函数,将不活跃的描述符清除掉, -
循环遍历select后的描述符集合,判断关注的文件描述符是否还在集合里 -
若在,执行相应的操作 -
若不在,continue执行下一次判断
3.代码如下
服务器
/*===============================================================
* 文件名称:server_TCP.c
* 创 建 者:zhongyue
* 创建日期:2020年08月03日
================================================================*/
int main(void)
{
//1.创建一个套接字
int listenfd = socket(AF_INET,SOCK_STREAM,0);
if(listenfd < 0){
perror("socket");
return -1;
}
//2.绑定
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(12345);
saddr.sin_addr.s_addr = inet_addr("0.0.0.0");
int ret = bind(listenfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret < 0){
perror("bind");
return -1;
}
//3.设置监听套接字
int backlog = 20;
ret = listen(listenfd,backlog);
if(ret < 0){
perror("listen");
return -1;
}
//4.创建描述符集合
fd_set readfds,writefds;//创建描述符读集合和写集合
FD_ZERO(&readfds);//清空描述符集合
FD_ZERO(&writefds);//清空描述符集合
FD_SET(listenfd,&readfds);//将监听套接字写入集合
int maxfd = listenfd;//初始化最大文件描述符
fd_set tmpfds = readfds;//定义一个中间变量记录最初的文件间描述符集合,因为readsfds一直在改变
struct sockaddr_in caddr;
int clen = sizeof(caddr);
while(1){
readfds = tmpfds;//readfds重新赋值
int ret = select(maxfd+1,&readfds,NULL,NULL,NULL);//调用select函数清除不活跃的描述符
if(ret < 0){
perror("select");
return -1;
}
for(int i=3;i<maxfd+1;i++){ //循环遍历描述符
if(FD_ISSET(i,&readfds)){
if(i == listenfd){ //如果套接字存在且是监听套接字,则调用accept连接客户端
int connfd = accept(listenfd,(struct sockaddr *)&caddr,&clen);
if(connfd < 0){
perror("accept");
return -1;
}
printf("客户端已连入!\n");
printf("客户端的IP地址为:%s\n",inet_ntoa(caddr.sin_addr));
printf("客户端的端口号为:%d\n",ntohs(caddr.sin_port));
FD_SET(connfd,&tmpfds);//将连接套接字加入描述符集合
maxfd = maxfd > connfd?maxfd:connfd;//更新最大描述符
}
else{ //如果是连接套接字,则处理数据的收发
char buf[128] = {"xieting"};
ret = read(i,buf,sizeof(buf));//接收客户端发来的数据
if(ret < 0){
perror("read");
continue;
}
printf("%s\n",buf);
if(ret == 0){ //如果客户端断开连接,清除这个连接套接字
printf("客户端已断开!\n");
printf("-------------\n");
close(i);
FD_CLR(i,&tmpfds);
//更新最大描述符maxfd
}
write(i,buf,sizeof(buf));//向客户端反馈接收到的数据
if(errno == EDESTADDRREQ){
continue;
}
}
}
}
}
}
客户端
/*===============================================================
* 文件名称:client.c
* 创 建 者:zhongyue
* 创建日期:2020年08月03日
================================================================*/
int main(void)
{
//1.创建一个套接字
int connfd = socket(AF_INET,SOCK_STREAM,0);
if(connfd < 0){
perror("fail to socket");
return -1;
}
//2.连接服务器
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(12345);
saddr.sin_addr.s_addr = inet_addr("192.168.64.137");
int ret = connect(connfd,(struct sockaddr *)&saddr,sizeof(saddr));
if(ret < 0){
perror("connect");
return -1;
}
printf("已连接服务器!\n");
while(1){
char buf[128] = "nice to meet you";
ret = write(connfd,buf,sizeof(buf));
if(ret < 0){
perror("write");
return -1;
}
sleep(1);
read(connfd,buf,sizeof(buf));
printf("%s\n",buf);
}
return 0;
}