补充知识,TCP--send/recv函数
1、网络发送数据:send() / wirte()
功能:
客户和服务器都用send函数来向另一端发送数据。客户端一般用send函数向服务器发送请求,而服务器则通常用send函数来向客户端发送应答。
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 4 ssize_t send(int sockfd, const void *buf, size_t len, int flags); 5 //参数 6 //sockfd:发送端的套接字描述符 7 //buf :存放所要发送的数据的缓冲区 8 //len :实际要发送的数据的字节数 9 //flags :一般设为0
对比write()
1 #include <unistd.h> 2 3 ssize_t write(int fd, const void *buf, size_t count); 4 5 //send比write函数多一个参数flags,其他相同 6 //flags为0时,与write相同
flags参数:
1 0 2 //当发送数据时,若发送的内核缓冲区满,且数据没有发送出去,就发生阻塞; 3 4 MSG_DONTWAIT 5 //非阻塞方式 6 7 MSG_OOB 8 //Sends out-of-band data on sockets that support this notion (e.g., //of type SOCK_STREAM); 9 //the underlying protocol must also support out-of-band data. 10 //用于发送TCP类型的带外数据
2、网络接收数据:recv() / read()
功能:
从另一端接收数据
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 4 ssize_t recv(int sockfd, void *buf, size_t len, int flags); 5 //参数: 6 //接收端的套接字描述符 7 //存放recv接收到的数据 8 //buf长度 9 //一般设为0
对比read
1 #include <unistd.h> 2 3 ssize_t read(int fd, void *buf, size_t count);
flags参数
1 0 2 //一般为0,阻塞操作 3 MSG_DONTWAIT 4 //非阻塞操作 5 MSG_OOB 6 //用于发送TCP类型的带外数据 7 MSG_PEEK 8 // 9 // 10 //
MSG_PEEK标志可以用来读取套接字接收队列中可读的数据,一些情况会用到它,比如为了避免不阻塞而先检查套接字接收队列中可读的数据长度,再采取相应操作。 当然,不阻塞也可采取其他的方法,例如非阻塞式I/O。 MSG_PEEK标志会将套接字接收队列中的可读的数据拷贝到缓冲区,但不会使套接子接收队列中的数据减少,常见的是:例如调用recv或read后,导致套接字接收队列中的数据被读取后而减少,而指定了MSG_PEEK标志,可通过返回值获得可读数据长度,并且不会减少套接字接收缓冲区中的数据,所以可以供程序的其他部分继续读取。
内核从网络接收数据并填充缓冲区,填充了200字节数据,读走了100字节后,后续的数据会向前移动,下次读取数据就又从头开始了,即TCP是以字符流方式读取的,没有边界。有时有些协议数据是完整的,有头有尾
将flags设置为MSG_PEEK,第一次读取数据后,不会将buf读走的数据移除,再次调用读函数就可以读到刚才读到的数据。
TCP与UDP的区别
TCP传输时建立可靠的连接,而UDP是面向无连接的协议。使用UDP协议时,不需要建立连接,只需知道对方的IP地址和端口号,就可以直接发数据包。但是可能会丢包。
虽然UDP传输数据不可靠,但是相对于TCP,其传输速度快,所需系统资源少。对于不要求可靠到达的数据可以用UDP协议。
sendto与recvfrom---UDP网络编程
sendto
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 4 ssize_t send (int sockfd, const void *buf, size_t len, int flags); 5 6 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, 7 const struct sockaddr *dest_addr, socklen_t addrlen); 8 //该函数比send()多了两参数, 9 //to表示目地机的IP地址和端口号信息, 10 //而tolen常常被赋值为sizeof(struct sockaddr)。 11 //Sendto 函数也返回实际发送的数据字节长度或在出现发送错误时返回-1。
recvfrom
1 #include <sys/types.h> 2 #include <sys/socket.h> 3 4 ssize_t recv (int sockfd, void *buf, size_t len, int flags); 5 6 ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, 7 struct sockaddr *src_addr, socklen_t *addrlen); 8 9 //from是一个struct sockaddr类型的变量,该变量保存源机的IP地址及端口号。 10 //fromlen常置为sizeof(struct sockaddr)。当recvfrom返回时,fromlen包含实际存入from中的数据字节数。 11 //recvfrom()函数返回接收到的字节数或当出现错误时返回-1,并置相应的errno。
测试代码:
1 #include "net.h" 2 3 int main(void) 4 { 5 int fd = -1; 6 struct sockaddr_in sin; //网络环境下套接字的地址形式,对其进行操作 7 8 /*1.创建套接字描述符fd*/ 9 if((fd = socket(AF_INET,SOCK_DGRAM,0)) < 0) //udp套接字 10 { 11 perror("socket"); 12 exit(1); 13 } 14 15 /*优化1:允许绑定地址快速重用*/ 16 int b_reuse = 1; 17 setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); 18 19 /*2.绑定*/ 20 /*2.1 填充struct sockaddr_in结构体变量*/ 21 bzero(&sin, sizeof(sin)); //初始值置零 22 sin.sin_family = AF_INET; //协议,ipv4 23 sin.sin_port = htons(SERV_PORT); //将端口号转为NBD 24 25 /*优化2:让服务器能绑定在任意IP上*/ 26 sin.sin_addr.s_addr = htonl(INADDR_ANY); 27 if(inet_pton(AF_INET, SERV_IP_ADDR, (void *)&sin.sin_addr)!=1) 28 { 29 perror("inet_pton"); 30 exit(1); 31 } 32 /*2.2绑定*/ 33 if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0) 34 { 35 perror("bind"); 36 exit(1); 37 } 38 39 /*TCP--SOCK_STREAM是面向连接的,每次收发数据前必须通过connect 40 * 建立双向连接确定任一方都可以收发数据 */ 41 /*UDP--SOCK_DGRAM无连接不可靠,通讯双方不知道对方是否收到数据 42 * 任一方建立socket后就可以用recvfrom与sendto收发数据 43 */ 44 /*使用recvfrom方法返回数据和客户端的地址与端口 45 * 这样,服务器收到数据后,可直接调用sendto把数据发送给客户端*/ 46 47 char buf[BUFSIZ]; 48 struct sockaddr_in cin; //让系统填充发送方的信息 49 socklen_t addrlen = sizeof(cin); 50 printf(" UDP server started! "); 51 while(1) 52 { 53 bzero(buf,BUFSIZ); 54 if(recvfrom(fd, buf, BUFSIZ-1, 0, (struct sockaddr *)&cin, &addrlen) < 0) 55 { 56 perror("recvfrom"); 57 continue; 58 } 59 60 //将发送方的HBD信息转为NBD 61 char ipv4_addr[16]; 62 if(!inet_ntop(AF_INET, (void *)&cin.sin_addr,ipv4_addr,sizeof(cin))) 63 { 64 perror("inet_ntop"); 65 exit(1); 66 } 67 printf("Receive from(%s,%d),data:%s",ipv4_addr,ntohs(sin.sin_port),buf); 68 69 //输入exit,退出 70 if(!strncasecmp(buf,QUIT_STR, strlen(QUIT_STR))) 71 { 72 printf("client(%s,%d) is exiting! ",ipv4_addr,ntohs(sin.sin_port)); 73 } 74 } 75 76 close(fd); 77 return 0; 78 }
1 /*./client serv_ip serv_port 启动输入参数 */ 2 #include "net.h" 3 void usage(char *s) 4 { 5 printf(" This is udp demo! "); 6 printf(" Usage: %s serv_ip serv_port",s); 7 printf(" serv_ip: udp server ip address!"); 8 printf(" serv_port: udp serv_port "); 9 } 10 11 int main(int argc, char *argv[]) 12 { 13 int fd = -1; 14 15 int port = SERV_PORT; 16 port = atoi(argv[2]); 17 if(port < 0 || (port > 0 && port <= 5000)) 18 { 19 usage(argv[0]); 20 exit(1); 21 } 22 23 struct sockaddr_in sin; 24 //输入参数提示 25 if(argc != 3) 26 { 27 usage(argv[0]); 28 exit(1); 29 } 30 /*1.创建socket fd */ 31 if((fd = socket(AF_INET, SOCK_DGRAM, 0))<0) 32 { 33 perror("socket"); 34 exit(1); 35 } 36 37 /* */ 38 /*2.1 填充struct sockaddr_in结构体变量*/ 39 bzero(&sin, sizeof(sin)); //初始值置零 40 sin.sin_family = AF_INET; // 41 sin.sin_port = htons(SERV_PORT); //转化为NBD 42 43 if(inet_pton(AF_INET, argv[1],(void *)&sin.sin_addr.s_addr) != 1) 44 { 45 perror("inet_pton"); 46 exit(1); 47 } 48 49 printf("UDP client starting...ok! "); 50 char buf[BUFSIZ]; 51 while(1) 52 { 53 printf("Please input the string to server:"); 54 bzero(buf, BUFSIZ); 55 if(fgets(buf, BUFSIZ-1, stdin) == NULL) 56 { 57 perror("fgets"); 58 continue; 59 } 60 /*获取到数据后就可以调用sendto函数了 */ 61 sendto(fd, buf, strlen(buf), 0, (struct sockaddr *)&sin, sizeof(sin)); 62 if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))) 63 { 64 printf("Clinet is exiting! "); 65 break; 66 } 67 68 } 69 close(fd); 70 return 0; 71 }
测试结果:
参考文章:
TCP和UDP的最完整的区别
UDP编程
1