Chinaunix首页 | 论坛 | 博客
  • 博客访问: 391802
  • 博文数量: 80
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 560
  • 用 户 组: 普通用户
  • 注册时间: 2015-03-10 08:38
文章分类
文章存档

2016年(32)

2015年(48)

我的朋友

分类: LINUX

2015-09-14 09:14:03

    近两天有个需求,client端需要通过发送一段私有的udp报文协议给server端,server端根据过来的报文来对自己做一些配置。server端在对自己配置成功之后,需要给client端发送配置成功的例子。同时, client端能够读取server端当前的配置信息。
    报文是udp报文,我最开始想当然的以为udp client也是可以接收server发过来的信息的,后来发现udp应该是属于单向传输的。也就是说,如果client想要接收server发过来的信息,那么也需要bind一个端口,然后接受来着这个端口的数据,这样一来client也就变成了另外一个udp会话的server。
    但事实是,因为client之前给server发消息,系统给client临时分配了一个端口,是否可以仍然利用这个端口来进行server发给client消息这个方向的会话。
    答案是可以, 看下面的例子:
    
  1. server端的代码:

  2. /*************************************************************************
  3.  > File Name: server.c
  4.  > Author: SongLee
  5.  ************************************************************************/
  6. #include<sys/types.h>
  7. #include<sys/socket.h>
  8. #include<unistd.h>
  9. #include<netinet/in.h>
  10. #include<arpa/inet.h>
  11. #include<stdio.h>
  12. #include<stdlib.h>
  13. #include<errno.h>
  14. #include<netdb.h>
  15. #include<stdarg.h>
  16. #include<string.h>
  17.   
  18. #define SERVER_PORT 8000
  19. #define BUFFER_SIZE 1024
  20. #define FILE_NAME_MAX_SIZE 512
  21.   
  22. int main()
  23. {
  24.     /* 创建UDP套接口 */
  25.     struct sockaddr_in server_addr;
  26.     bzero(&server_addr, sizeof(server_addr));
  27.     server_addr.sin_family = AF_INET;
  28.     server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  29.     server_addr.sin_port = htons(SERVER_PORT);

  30.     /* 创建socket */
  31.     int server_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  32.     if(server_socket_fd == -1)
  33.     {
  34.         perror("Create Socket Failed:");
  35.         exit(1);
  36.      }
  37.   
  38.      /* 绑定套接口 */
  39.     if(-1 == (bind(server_socket_fd,(struct sockaddr*)&server_addr,sizeof(server_addr))))
  40.     {
  41.         perror("Server Bind Failed:");
  42.         exit(1);
  43.     }

  44.       char bufReceived[50] = {"I have received you message"};
  45.     /* 数据传输 */
  46.     while(1)
  47.     {
  48.         /* 定义一个地址,用于捕获客户端地址 */
  49.         struct sockaddr_in client_addr;
  50.         socklen_t client_addr_length = sizeof(client_addr);

  51.         /* 接收数据 */
  52.         char buffer[BUFFER_SIZE];
  53.         bzero(buffer, BUFFER_SIZE);
  54.         if(recvfrom(server_socket_fd, buffer, BUFFER_SIZE,0,(struct sockaddr*)&client_addr, &client_addr_length) == -1)
  55.         {
  56.             perror("Receive Data Failed:");
  57.             exit(1);
  58.         }
  59.         
  60.         /* 从buffer中拷贝出file_name */
  61.         char file_name[FILE_NAME_MAX_SIZE+1];
  62.         bzero(file_name,FILE_NAME_MAX_SIZE+1);
  63.         strncpy(file_name, buffer, strlen(buffer)>FILE_NAME_MAX_SIZE?FILE_NAME_MAX_SIZE:strlen(buffer));
  64.         printf("filename is %s\n", file_name);

  65.         if(sendto(server_socket_fd, bufReceived, 50,0,(struct sockaddr*)&client_addr,sizeof(client_addr)) < 0)
  66.         {
  67.             perror("Send File Name Failed:");
  68.             exit(1);
  69.         }
  70.         
  71.     }
  72.     close(server_socket_fd);
  73.     return 0;
  74. }



  1. client端的代码:

  2. /*************************************************************************
  3.  > File Name: client.c
  4.  > Author: SongLee
  5.  ************************************************************************/
  6. #include<sys/types.h>
  7. #include<sys/socket.h>
  8. #include<unistd.h>
  9. #include<netinet/in.h>
  10. #include<arpa/inet.h>
  11. #include<stdio.h>
  12. #include<stdlib.h>
  13. #include<errno.h>
  14. #include<netdb.h>
  15. #include<stdarg.h>
  16. #include<string.h>
  17.   
  18. #define SERVER_PORT 8000
  19. #define BUFFER_SIZE 1024
  20. #define FILE_NAME_MAX_SIZE 512
  21.   
  22. int main()
  23. {
  24.     int ret;
  25.     int i;
  26.     struct sockaddr_in self_addr;
  27.     socklen_t socketLen = sizeof(self_addr);
  28.     bzero(&self_addr, sizeof(self_addr));
  29.     char bufReceive[BUFFER_SIZE];
  30.     bzero(bufReceive, BUFFER_SIZE);
  31.     socklen_t self_addr_length = sizeof(self_addr);

  32.      /* 服务端地址 */
  33.     struct sockaddr_in server_addr;
  34.     bzero(&server_addr, sizeof(server_addr));
  35.     server_addr.sin_family = AF_INET;
  36.     server_addr.sin_addr.s_addr = inet_addr("1.1.1.1");
  37.     server_addr.sin_port = htons(SERVER_PORT);

  38.     /* 创建socket */
  39.     int client_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
  40.     if(client_socket_fd < 0)
  41.     {
  42.         perror("Create Socket Failed:");
  43.         exit(1);
  44.     }

  45. #if 1
  46.     if(-1 == (bind(client_socket_fd,(struct sockaddr*)&self_addr,sizeof(self_addr))))
  47.     {
  48.         perror("Server Bind Failed:");
  49.         exit(1);
  50.     }
  51. #endif
  52.     
  53.     ret = getsockname(client_socket_fd,(struct sockaddr*)&self_addr,&socketLen);
  54.     printf("\r\n 1 ret is : %d ",ret);
  55.     printf("\r\n 1 my ip is : 0x%x",self_addr.sin_addr.s_addr);
  56.     printf("\r\n 1 my l4port is : %d",self_addr.sin_port);
  57.     printf("\r\n 1 my client_socket_fd is : %d \r\n",client_socket_fd);
  58.     
  59.     /* 输入文件名到缓冲区 */
  60.     char file_name[FILE_NAME_MAX_SIZE+1];
  61.     bzero(file_name, FILE_NAME_MAX_SIZE+1);
  62.     printf("Please Input File Name On Server:\t");
  63.     scanf("%s", file_name);

  64.     char buffer[BUFFER_SIZE];
  65.     bzero(buffer, BUFFER_SIZE);
  66.     strncpy(buffer, file_name, strlen(file_name)>BUFFER_SIZE?BUFFER_SIZE:strlen(file_name));

  67. //for(i=0;i<5;i++){
  68.     /* 发送文件名 */

  69.     if(sendto(client_socket_fd, buffer, BUFFER_SIZE,0,(struct sockaddr*)&server_addr,sizeof(server_addr)) < 0)
  70.     {
  71.         perror("Send File Name Failed:");
  72.         exit(1);
  73.     }

  74.     ret = getsockname(client_socket_fd,(struct sockaddr*)&self_addr,&socketLen);
  75.     printf("\r\n 2 ret is : %d ",ret);
  76.     printf("\r\n 2 my ip is : 0x%x ",self_addr.sin_addr.s_addr);
  77.     printf("\r\n 2 my l4port is : %d ",self_addr.sin_port);
  78.     printf("\r\n 2 my client_socket_fd is : %d \r\n",client_socket_fd);
  79.     printf("\r\n");
  80. //}

  81. #if 0
  82.     if(-1 == (bind(client_socket_fd,(struct sockaddr*)&self_addr,sizeof(self_addr))))
  83.     {
  84.         perror("Server Bind Failed:");
  85.         exit(1);
  86.     }
  87. #endif

  88.     if(recvfrom(client_socket_fd, bufReceive, BUFFER_SIZE,0,(struct sockaddr*)&self_addr, &self_addr_length) == -1)
  89.     {
  90.         perror("Receive Data Failed:");
  91.         exit(1);
  92.     }
  93.     printf("\r\n %s \r\n",bufReceive);
  94.     
  95.     close(client_socket_fd);
  96.     return 0;
  97. }
    这两段代码, 实现了client给server发送一个文件名, 然后server给client回复一个消息的功能。 主要看client端, 发送报文和接受报文, 用的是同一个socket建立起来的fd和临时端口。注意client代码中, 两个“#if 1 ... #endif和#if 0 ... #endif"的地方, 如果把上面那个注掉,下面那个放开, 会发现bind失败。  如果把下面那个注掉,上面那个放开, 则可以正常运行,而且这时候getsockname可以正常获取到fd的临时端口号, 所以, 我怀疑临时端口号就是在bind这个函数执行的时候会分配的。  但是, 把bind放在一次sendto函数之后, 想再去bind就bind不上了, 因此临时端口号的分配也有可能会发生在sendto执行的时候, 分配好了之后再去bind就bind不上了。
    
    因此, 如果有udp做交互的时候, client端的bind, 需要在sendto之前就执行一次, 否则会bind失败。

    
阅读(1865) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~