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函数一般用于TCP链接,sendto函数一般用于UDP连接。
来看看man手册里怎么说send与sendto:
The send() call may be used only when the socket is in a connected state (so that the
intended recipient is known).
The only difference between send() and write(2) is the presence of flags. With a zero
flags argument, send() is equivalent to write(2). Also, the following
callsend(sockfd, buf, len, flags);is equivalent tosendto(sockfd, buf, len, flags, NULL, 0);
If sendto() is used on a connection-mode (SOCK_STREAM, SOCK_SEQPACKET) socket, the arguments dest_addr and addrlen are ignored (and the error EISCONN may be returned when they are not NULL and 0), and the error ENOTCONN is returned when the socket was not actually connected. Otherwise, the address of the target is given by dest_addr with addrlen specifying its size.
解释:
send()调用只能在套接字处于连接状态时使用(以便知道对端)。send()和write(2)唯一的的区别在于标志位flag。当flag==0时,send()等价于write(2)写。send()也等价于sendto(sockfd,buf,len,flags,NULL,0);
如果sendto()用于连接模式(SOCK_STREAM, SOCK_SEQPACKET)套接字,则dest_addr和addrlen参数被忽略(当它们不是NULL和0时,可能返回错误EISCONN),当socket没有建立连接(即udp场景时),目标的地址由dest_addr给出,addrlen指定其地址大小。
所以说其实sendto()也是可以用于tcp通信的,只是我们一般不这么做。
下面对参数为什么被忽略做一个解释:
send函数只有一个能联想到客户端地址的参数,就是第一个参数sockfd。当客户端与服务端链接建立成功后,在服务器端每一个客户都有一个专用的socket,毫无疑问就可以把它看作这个客户端的地址映射。
sendto能联想到客户端地址的参数有两个:sockfd 和 dest_addr,但在实际应用中对所有UDP客户sockfd参数都使用同一个值,所以可以推断这个socket是为所有UDP客户使用的,不能看做目的客户端的地址映射,只有dest_addr才是目的客户端的地址映射(ip地址和端口号)。
recv()和recvfrom同理,我只将接口列在下面,大家再琢磨琢磨,很容易就能弄明白。
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);