大家好,欢迎来到IT知识分享网。
一、recv、recvfrom、read
recv通常在面向连接的套接字(TCP编程)中使用,用于接收对端发送的信息。recvfrom通常使用在面向无连接的套接字(UDP编程)中,其多出的两个参数就是用来接收对端的地址信息,以便于发送时知道发给谁。
以下列出recv、recvfrom和read的函数声明:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t read(int fd, void *buf, size_t count);
在TCP编程中,recv可以使用read和recvfrom来替换,将recvfrom后两个参数置NULL就好了;在UDP编程中recvfrom用read和recv替换时有两种场景:
1.当不关心对端地址信息、不需要再发送给对端信息时,可以替换。
2.当UDP中使用connect函数后。
UDP中使用connect的作用以及与TCP中使用connect的区别。
i. TCP中的connect函数会产生三次握手,将client 和 server连接;UDP中的connect函数不会产生连接。UDP中的connect函数仅仅是将对端的IP和端口填充到内核套接字中。此时UDP只能与记录的对端进行通信。
ii. TCP的connect函数只能调用一次;UDP中的connect函数可以调用多次,刷新内核中对端的地址信息。若想清空内核中对端的地址信息,可以将sin_family = AF_UNSPEC.
二、send、sendto、write
send通常在面向连接的套接字(TCP编程)中使用,用于向对端发送信息。sendto通常使用在面向无连接的套接字(UDP编程)中。
以下列出write、send和sendto的函数声明:
ssize_t write(int fd, const void *buf, size_t count);
write(sockfd,buf,sizeof(buf));等价于send(sockfd,buf,sizeof(buf),0);同时也等价于sendto(sockfd,buf,sizeof(buf),flags,NULL,0);
在TCP编程中,send可以使用write和sendto来替换;在UDP编程中sendto用send和write替换时,需要在UDP中使用connect函数,只有将对端IP地址和端口保存到内核中,才能确保发送正确。
三、以下是connect在UDP中的使用代码
UDP服务器
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <stdlib.h> #include <arpa/inet.h> #include <strings.h> #include <string.h> #include <unistd.h> int main(int argc, char const *argv[]) { if (argc != 3) { printf("please input IP and PORT\n"); return -1; } char buf[128] = ""; ssize_t res; //创建报式套接字 int sfd = socket(AF_INET, SOCK_DGRAM, 0); if (sfd < 0) { perror("socket err"); return -1; } printf("The socket was created successfully.\n"); //填充服务器地址信息结构体,真实的地址信息结构体AF_INET:man 7 ip struct sockaddr_in sin; sin.sin_family = AF_INET; //协议族 sin.sin_addr.s_addr = inet_addr(argv[1]); //字符串转点分十进制 sin.sin_port = htons(atoi(argv[2])); // htons:小端转大端 atoi:将数字字符串转换为数值 //bind绑定服务器地址信息 if (bind(sfd, (struct sockaddr *)&sin, sizeof(sin)) < 0) { perror("bind err"); return -1; } printf("bind success.\n"); //存储客户端地址信息 struct sockaddr_in cin; socklen_t cin_addrlen = sizeof(cin); while (1) { //接收客户端数据以及地址信息 bzero(buf, sizeof(buf)); //接收前清空数组 res = recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr *)&cin, &cin_addrlen); if (res < 0) { perror("recvfrom err"); return -1; } //inet_ntoa:网络字节序转点分十进制,ntohs:网络字节序转本机字节序 printf("[%s|%d],buf:%s,line:%d\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf, __LINE__); /*connect,UDP中使用connect,和TCP中不一样,它不产生连接,不会三次握手,只是会将对段的IP地址和 端口号存储到内核中,此时UDP只能与记录的对段进行通信,并且在UDP中connect可以进行多次的调用,来刷新 内核中对段的IP地址和端口号信息*/ if (connect(sfd, (struct sockaddr *)&cin, cin_addrlen) < 0) { perror("connect err"); return -1; } printf("udp connect [%s:%d] success __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), __LINE__); while (1) { strcat(buf, "*"); //此时的sendto也可以使用write或者send来替换, //write(sfd,buf,sizeof(buf)); //send(sfd,buf,sizeof(buf),0); if (sendto(sfd, buf, sizeof(buf), 0, NULL, 0) < 0) { perror("sendto err"); return -1; } printf("sendto success,line:%d\n", __LINE__); bzero(buf, sizeof(buf)); //因为connect后,已经在内核保存了客户端地址信息,所以后两个参数置空即可,此时可以用 //read和recv来替换 res = recvfrom(sfd, buf, sizeof(buf), 0, NULL, NULL); if (res < 0) { perror("recvfrom err"); return -1; } printf("[%s:%d] : %s __%d__\n", inet_ntoa(cin.sin_addr), ntohs(cin.sin_port), buf, __LINE__); } // //清空内核中对段地址信息 // cin.sin_family=AF_UNSPEC; // if(connect(sfd,(struct sockaddr *)&cin,cin_addrlen)<0) // { // perror("connect err"); // return -1; // } // printf("UDP connect AF_UNSPEC success\n"); } if (close(sfd) < 0) //关闭文件描述符 { perror("close err"); return -1; } return 0; }
UDP客户端
#include<stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include<string.h> #include <unistd.h> #include<stdlib.h> #define _ERR(msg) \ { \ fprintf(stderr, "line :%d", __LINE__); \ perror(msg); \ } int main(int argc, char const *argv[]) { if(argc!=3) { printf("please input IP and PORT\n"); return -1; } char buf[128]={0}; ssize_t res=0; //创建报式套接字 int cfd=socket(AF_INET,SOCK_DGRAM ,0); if(cfd<0){ _ERR("socket"); return -1; } printf("socket create success,line:%d\n",__LINE__); //填充服务器地址 struct sockaddr_in sin; sin.sin_family=AF_INET; sin.sin_port=htons(atoi(argv[2])); sin.sin_addr.s_addr=inet_addr(argv[1]); //connect,将服务器的IP地址和端口号存储到内核中去, if(connect(cfd,(struct sockaddr *)&sin,sizeof(sin))<0) { perror("connect err"); return -1; } printf("connect success,line:%d\n",__LINE__); while(1){ //发送sendto bzero(buf,sizeof(buf)); printf("please input\n"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1]='\0'; if(sendto(cfd,buf,sizeof(buf),0,NULL,0)<0){ _ERR("sendto"); return -1; } printf("sendto success,line:%d\n",__LINE__); //接收recvfrom bzero(buf,sizeof(buf)); res=recvfrom(cfd,buf,sizeof(buf),0,NULL,NULL); if(res<0){ _ERR("recvfrom"); return -1; } printf("[%s|%d],%s,line:%d\n",inet_ntoa(sin.sin_addr),ntohs(sin.sin_port),buf,__LINE__); } if(close(cfd)<0){ _ERR("close"); return -1; } return 0; }
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/130205.html