网络编程中,recv、recvfrom、read;send、sendto、write在哪种场景下可以相互转化?以及connect的作用

一、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);

其中recv(sockfd,buf,sizeof(buf),flags)等价于recvfrom(sockfd,buf,sizeof(buf),flags,NULL,NULL)。
当recv中的标志位置0时,即recv(sockfd,buf,sizeof(buf),0);就等价于read(sockfd,buf,sizeof(buf));同时也等价于recvfrom(sockfd,buf,sizeof(buf),0,NULL,NULL);

在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);

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
其中send(sockfd,buf,sizeof(buf),flags);等价于sendto(sockfd,buf,sizeof(buf),flags,NULL,0)。

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

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值