Linux C Socket UDP编程介绍及实例

转载源

1、UDP网络编程主要流程

UDP协议的程序设计框架,客户端和服务器之间的差别在于服务器必须使用bind()函数来绑定侦听的本地UDP端口,而客户端则可以不进行绑定,直接发送到服务器地址的某个端口地址。框图如图1.3所示

UDP协议的服务器端流程

服务器流程主要分为下述6个部分,即建立套接字、设置套接字地址参数、进行端口绑定、接收数据、发送数据、关闭套接字等。

(1)建立套接字文件描述符,使用函数socket(),生成套接字文件描述符。

(2)设置服务器地址和侦听端口,初始化要绑定的网络地址结构。

(3)绑定侦听端口,使用bind()函数,将套接字文件描述符和一个地址类型变量进行绑定。

(4)接收客户端的数据,使用recvfrom()函数接收客户端的网络数据。

(5)向客户端发送数据,使用sendto()函数向服务器主机发送数据。

(6)关闭套接字,使用close()函数释放资源。UDP协议的客户端流程

UDP协议的客户端流程

UDP协议的客户端流程分为套接字建立、设置目的地址和端口、向服务器发送数据、从服务器接收数据、关闭套接字等5个部分。流程如下:

(1)建立套接字文件描述符,socket();

(2)设置服务器地址和端口,struct sockaddr;

(3)向服务器发送数据,sendto();

(4)接收服务器的数据,recvfrom();

(5)关闭套接字,close()。


图1.3 UDP编程流程

2、相关函数

(1)  int socket(AF_INET, SOCK_DGRAM, 0);

创建udp socket,返回套接字描述符,UDP协议建立套接字的方式同TCP方式一样,使用socket()函数,只不过协议的类型使用SOCK_DGRAM,而不是SOCK_STREAM。

(2) int sendto(int sockfd, const void *data, int data_len, unsigned int flags, struct sockaddr *remaddr,sock_lenremaddr_len)

功能:基于UDP发送数据报,返回实际发送的数据长度,出错时返回-1

参数说明:

sockfd:套接字描述符

data:指向要发送数据的指针

data_len:数据长度

flags:通常为0

remaddr:远端地址:IP地址和端口号

remaddr_len:地址长度

(3) int recvfrom(int sockfd, void *buf,int buf_len,unsigned int flags,struct sockaddr *from,sock_len *fromlen);

功能:从UDP接收数据,返回实际接收的字节数,失败时返回-1

参数说明:

Sockfd:套接字描述符

buf:指向内存块的指针

buf_len:内存块大小,以字节为单位

flags:一般为0

from:远端的地址,IP地址和端口号

fromlen:远端地址长度

(4) ssize_t recv(int s, void*buf,size_t len, int flags);

   连接的UDP可调用recv从服务器读取数据。

ssize_tsend(int s, const void*buf, size_t len, int flags);

连接的UDP可调用send向服务器发送数据。

3、UDPSocket客户服务器通信实例

下面依照通信流程,我们来实现一个UDP回射客户/服务器。


图1.4 UDP回射客户/服务器流程

服务器代码:


   
   
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<unistd.h>
  4. #include<errno.h>
  5. #include<sys/types.h>
  6. #include<sys/socket.h>
  7. #include<netinet/in.h>
  8. #include<string.h>
  9. #define MYPORT 8887
  10. #define ERR_EXIT(m) \
  11. do { \
  12. perror(m); \
  13. exit(EXIT_FAILURE); \
  14. } while (0)
  15. void echo_ser(int sock)
  16. {
  17. char recvbuf[ 1024] = { 0};
  18. struct sockaddr_in peeraddr;
  19. socklen_t peerlen;
  20. int n;
  21. while ( 1)
  22. {
  23. peerlen = sizeof(peeraddr);
  24. memset(recvbuf, 0, sizeof(recvbuf));
  25. n = recvfrom(sock, recvbuf, sizeof(recvbuf), 0,
  26. (struct sockaddr *)&peeraddr, &peerlen);
  27. if (n <= 0)
  28. {
  29. if (errno == EINTR)
  30. continue;
  31. ERR_EXIT( “recvfrom error”);
  32. }
  33. else if(n > 0)
  34. {
  35. printf( “接收到的数据:%s\n”,recvbuf);
  36. sendto(sock, recvbuf, n, 0,
  37. (struct sockaddr *)&peeraddr, peerlen);
  38. printf( “回送的数据:%s\n”,recvbuf);
  39. }
  40. }
  41. close(sock);
  42. }
  43. int main(void)
  44. {
  45. int sock;
  46. if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
  47. ERR_EXIT( “socket error”);
  48. struct sockaddr_in servaddr;
  49. memset(&servaddr, 0, sizeof(servaddr));
  50. servaddr.sin_family = AF_INET;
  51. servaddr.sin_port = htons(MYPORT);
  52. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  53. printf( “监听%d端口\n”,MYPORT);
  54. if (bind(sock, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
  55. ERR_EXIT( “bind error”);
  56. echo_ser(sock);
  57. return 0;
  58. }

客户端代码:


   
   
  1. #include <unistd.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netinet/in.h>
  5. #include <arpa/inet.h>
  6. #include <stdlib.h>
  7. #include <stdio.h>
  8. #include <errno.h>
  9. #include <string.h>
  10. #define MYPORT 8887
  11. char* SERVERIP = "127.0.0.1";
  12. #define ERR_EXIT(m) \
  13. do \
  14. { \
  15. perror(m); \
  16. exit(EXIT_FAILURE); \
  17. } while(0)
  18. void echo_cli(int sock)
  19. {
  20. struct sockaddr_in servaddr;
  21. memset(&servaddr, 0, sizeof(servaddr));
  22. servaddr.sin_family = AF_INET;
  23. servaddr.sin_port = htons(MYPORT);
  24. servaddr.sin_addr.s_addr = inet_addr(SERVERIP);
  25. int ret;
  26. char sendbuf[ 1024] = { 0};
  27. char recvbuf[ 1024] = { 0};
  28. while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
  29. {
  30. printf( "向服务器发送:%s\n",sendbuf);
  31. sendto(sock, sendbuf, strlen(sendbuf), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
  32. ret = recvfrom(sock, recvbuf, sizeof(recvbuf), 0, NULL, NULL);
  33. if (ret == -1)
  34. {
  35. if (errno == EINTR)
  36. continue;
  37. ERR_EXIT( "recvfrom");
  38. }
  39. printf( "从服务器接收:%s\n",recvbuf);
  40. memset(sendbuf, 0, sizeof(sendbuf));
  41. memset(recvbuf, 0, sizeof(recvbuf));
  42. }
  43. close(sock);
  44. }
  45. int main(void)
  46. {
  47. int sock;
  48. if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
  49. ERR_EXIT( "socket");
  50. echo_cli(sock);
  51. return 0;
  52. }

实验结果:



UDP编程注意:

1、UDP报文可能会丢失、重复

2、UDP报文可能会乱序

3、UDP缺乏流量控制

4、UDP协议数据报文截断

5、recvfrom返回0,不代表连接关闭,因为udp是无连接的。

6、ICMP异步错误

7、UDP connect

8、UDP外出接口的确定

9、太大的UDP包可能出现的问题

由于UDP不需要维护连接,程序逻辑简单了很多,但是UDP协议是不可靠的,实际上有很多保证通讯可靠性的机制需要在应用层实现,即123点所提到的。比如 如果发送端速度较快,而接收端较慢,很可能会产生 ICMPSource Quench Error,丢弃一些数据包。


猜你喜欢

转载自blog.csdn.net/ananbei/article/details/81155205