recv用TCP和TUDP下的区别
recv是可以用在UDP套接字上的,前提是该套接字调用过bind或者connect,那它用在TCP和UDP套接字上时有什么区别呢?
下面做一个测试,分别使用UDP、TCP实现一对C/S、客户端发送12字节数据,服务器接受的时候用10字节大小的buffer去接受。
TCP
tcpSvr:
1 /* 2 * gcc -o tcpSvr ./tcpSvr.c 3 */ 4 #include <unistd.h> 5 #include <sys/socket.h> 6 #include <sys/types.h> 7 #include <arpa/inet.h> 8 #include <netinet/in.h> 9 #include <stdio.h> 10 #include <string.h> 11 #include <errno.h> 12 13 const int PORT = 8080; 14 15 int main(int argc, char **argv) 16 { 17 int fd = socket(AF_INET, SOCK_STREAM, 0); 18 if (fd == -1) 19 { 20 perror("socket"); 21 return errno; 22 } 23 24 struct sockaddr_in addr; 25 memset(&addr, 0, sizeof(addr)); 26 addr.sin_family = AF_INET; 27 addr.sin_addr.s_addr = INADDR_ANY; 28 addr.sin_port = htons(PORT); 29 30 if (-1 == bind(fd, (struct sockaddr*)&addr, sizeof(addr))) 31 { 32 perror("bind"); 33 return errno; 34 } 35 36 if (-1 == listen(fd, 5)) 37 { 38 perror("listen"); 39 return errno; 40 } 41 42 struct sockaddr_in cli_addr; 43 socklen_t len = sizeof(cli_addr); 44 int client = accept(fd, (struct sockaddr*)&cli_addr, &len); 45 if (client == -1) 46 { 47 perror("accept"); 48 return errno; 49 } 50 51 printf("accept an client\n"); 52 53 char buf[1024]; 54 while(1) 55 { 56 memset(buf, 0, 1024); 57 int iRet = recv(client, buf, 10, 0); 58 printf("recv data len [%d]\n", iRet); 59 printf("recv content [%s]\n", buf); 60 sleep(2); 61 } 62 close(fd); 63 64 return 0; 65 }
tcpCli:
1 #include <unistd.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <stdio.h> 7 #include <string.h> 8 #include <errno.h> 9 10 const int PORT = 8080; 11 12 int main(int argc, char **argv) 13 { 14 int fd = socket(AF_INET, SOCK_STREAM, 0); 15 if (fd == -1) 16 { 17 perror("socket"); 18 return errno; 19 } 20 21 struct sockaddr_in addr; 22 memset(&addr, 0, sizeof(addr)); 23 addr.sin_family = AF_INET; 24 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 25 addr.sin_port = htons(PORT); 26 27 if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) 28 { 29 perror("connect"); 30 return errno; 31 } 32 33 char buf[] = "hello world!"; 34 while (1) 35 { 36 int iRet = send(fd, buf, strlen(buf), 0); 37 printf("Send data len [%d]\n", iRet); 38 printf("Send content [%s]\n", buf); 39 sleep(2); 40 } 41 close(fd); 42 43 return 0; 44 }
结果:
UDP
udpSvr
1 #include <unistd.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> 4 #include <arpa/inet.h> 5 #include <netinet/in.h> 6 #include <stdio.h> 7 #include <string.h> 8 #include <errno.h> 9 10 const int PORT = 8080; 11 12 int main(int argc, char **argv) 13 { 14 int fd = socket(AF_INET, SOCK_DGRAM, 0); 15 if (fd == -1) 16 { 17 perror("socket"); 18 return errno; 19 } 20 21 struct sockaddr_in addr; 22 memset(&addr, 0, sizeof(addr)); 23 addr.sin_family = AF_INET; 24 addr.sin_addr.s_addr = INADDR_ANY; 25 addr.sin_port = htons(PORT); 26 27 if (-1 == bind(fd, (struct sockaddr*)&addr, sizeof(addr))) 28 { 29 perror("bind"); 30 return errno; 31 } 32 33 char buf[1024]; 34 while(1) 35 { 36 memset(buf, 0, 1024); 37 int iRet = recv(fd, buf, 10, 0); 38 printf("recv data len [%d]\n", iRet); 39 printf("recv content [%s]\n", buf); 40 } 41 close(fd); 42 43 return 0; 44 }
udpCli:
1 #include <unistd.h> 2 #include <sys/socket.h> 3 #include <sys/types.h> 4 #include <netinet/in.h> 5 #include <arpa/inet.h> 6 #include <stdio.h> 7 #include <string.h> 8 #include <errno.h> 9 10 const int PORT = 8080; 11 12 int main(int argc, char **argv) 13 { 14 int fd = socket(AF_INET, SOCK_DGRAM, 0); 15 if (fd == -1) 16 { 17 perror("socket"); 18 return errno; 19 } 20 21 struct sockaddr_in addr; 22 memset(&addr, 0, sizeof(addr)); 23 addr.sin_family = AF_INET; 24 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 25 addr.sin_port = htons(PORT); 26 27 char buf[] = "hello world!"; 28 while (1) 29 { 30 int iRet = sendto(fd, buf, strlen(buf), 0, (struct sockaddr*)&addr, sizeof(addr)); 31 printf("Send data len [%d]\n", iRet); 32 printf("Send content [%s]\n", buf); 33 break; 34 } 35 close(fd); 36 37 return 0; 38 }
结果:
结论:
当收到的数据大于传入recv的buffer大小时,多余的字节,UDP会丢弃,TCP可以在下次调用recv的时候读取到,这也是为什么说TCP是基于流的协议。
所以当读取UDP数据时一定要注意buffer的大小,应使它大于IP层的数据报大小。
还需要注意的是,发送UDP数据的时候包的大小不应该导致IP分片,否则会造成乱序、丢包。