UDP网络编程

补充知识,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标志,可通过返回值获得可读数据长度,并且不会减少套接字接收缓冲区中的数据,所以可以供程序的其他部分继续读取。
MSG_PEEK

 

 内核从网络接收数据并填充缓冲区,填充了200字节数据,读走了100字节后,后续的数据会向前移动,下次读取数据就又从头开始了,即TCP是以字符流方式读取的,没有边界。有时有些协议数据是完整的,有头有尾

将flags设置为MSG_PEEK,第一次读取数据后,不会将buf读走的数据移除,再次调用读函数就可以读到刚才读到的数据。

TCP与UDP的区别

TCP传输时建立可靠的连接,而UDP是面向无连接的协议。使用UDP协议时,不需要建立连接,只需知道对方的IP地址和端口号,就可以直接发数据包。但是可能会丢包。

虽然UDP传输数据不可靠,但是相对于TCP,其传输速度快,所需系统资源少。对于不要求可靠到达的数据可以用UDP协议。

recv,write,send,read,recvfrom,sendto区别,详解

 

 

 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("\nUDP server started!\n");
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!\n",ipv4_addr,ntohs(sin.sin_port));
73         }
74     }
75 
76     close(fd);
77     return 0;
78 }
server.c
 1 /*./client serv_ip serv_port 启动输入参数 */
 2 #include "net.h"
 3 void usage(char *s)
 4 {
 5     printf("\nThis is udp demo!\n");
 6     printf("\nUsage:\n\t %s serv_ip serv_port",s);
 7     printf("\n\t serv_ip: udp server ip address!");
 8     printf("\n\t serv_port: udp serv_port\n\n");
 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!\n");
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!\n");
65             break;
66         }    
67 
68     }
69     close(fd);
70     return 0;
71 }
client.c

测试结果:

 

 

参考文章:

TCP和UDP的最完整的区别

UDP编程

 

1

 

posted @ 2020-01-25 17:20  朱果果  阅读(262)  评论(0编辑  收藏  举报