Chinaunix首页 | 论坛 | 博客
  • 博客访问: 367715
  • 博文数量: 90
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 113
  • 用 户 组: 普通用户
  • 注册时间: 2016-01-27 19:56
文章分类

全部博文(90)

文章存档

2017年(12)

2016年(78)

分类: LINUX

2016-02-18 14:02:44

基于无连接的UDP程序设计

       同样,在开发基于UDP的应用程序时,其主要流程如下:

       对于面向无连接的UDP应用程序在开发过程中服务端和客户端的操作流程基本差不多。对比面向连接的TCP程序,服务端少了listenaccept函数。前面我们也说过listen函数最主要的作用就是将一个socket套接字描述符转为被动监听模式,然后调用accept主要是用于等待客户端(connect)来连接服务器。connect函数不仅可以用于流式套接字还可用于数据报式套接字。在TCP中,客户端调用connect函数会向服务器端触发一个TCP3次握手过程,去建立一条TCP连接;而在UDP中,客户端调用该函数主要的作用是告诉后面将要调用的recvfrom函数,仅仅只接受在connect函数中指明的服务器发来的数据,这样当后面调用recvfrom时最后两个参数就可以置为NULL了。也就说对UDP编程来说,客户端调用connect是可选的:如果调用了connect函数,recvfrom就可以省掉最后两个参数;如果不调用connectrecvfrom必须指明从哪儿收数据。

       对于UDP的编程其实主要在数据的收发处理上,而面向无连接的UDP编程中收发数据用到的最多的函数就是recvfrom()sendto(),其原型如下:

ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

ssize_t sendto(int s, const void *buf, size_t len, int flags, struct sockaddr *to, socklen_t tolen);

       recvfrom函数主要用于从s所指定的套接字中接收数据,并将其存储在buf所指向的缓冲区里。如果from参数不为NULL,那么其中便会携带消息发送端的地址信息,fromlen则指明了信息发送方地址信息结构体的大小。如果接收方对发送发的地址不感兴趣,将fromfromlen置为NULL即可。返回值:小于0,有错误;大于0,实际收到的字节数;等于0,对端主动关闭。

       sendto函数,主要是buf所指向的数据发送到套接字描述符s中,len为要发送的数据长度,to中存储了对端的地址信息,即数据该发往何处,tolento所占的字节数。返回值:小于0,有错误;大于0,实际发送的字节数。

      另外我们还知道,sendto是可以用于面向连接的流式套接字的,在TCP开发章节我们已经提过。这里在罗嗦一点,如果sendto用于面向流式的套接字编程中,totolen参数都会被忽略,如果发送数据时连接还未建立相应的提示错误为ENOTCONN

      这里也没有哪个规定说是不准在TCP程序中用sendto,但我们一般都不这么做,自己体会一下就明白了,除非你的项目开发中有特殊需求必须用。一句话:记住sendtorecvfrom既可以用于面向连接的流式套接字中收发数据,也可以用于面向无连接的数据报式套接字sendto()recvfrom()一般用在面向无连接的数据报式套接字的程序开发中。

      看个小例子:

      UDP服务器端代码:udpsrv.c

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <netdb.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <sys/types.h>
  10. #include <arpa/inet.h>
  11. #define MAX_MSG_SIZE 1024

  12. int main(int argc,char** argv){
  13.     int skfd,addrlen,ret;
  14.     struct sockaddr_in addr,cltaddr;
  15.     char buf[MAX_MSG_SIZE]={0};
  16.     char sndbuf[MAX_MSG_SIZE]={0};

  17.     //创建数据报式套接字skfd
  18.     if(0>(skfd=socket(AF_INET,SOCK_DGRAM,0))){
  19.          perror("Create Error");
  20.          exit(1);
  21.     }

  22.     bzero(&addr,sizeof(struct sockaddr_in));
  23.     addr.sin_family = AF_INET;
  24.     addr.sin_addr.s_addr=htonl(INADDR_ANY);
  25.     addr.sin_port=htons(atoi(argv[1]));

  26.     //将socket文件描述符skfd和本地端口和地址绑定起来
  27.     if(0>(bind(skfd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in)))){
  28.          perror("Bind Error");
  29.          exit(1);
  30.     }

  31.     //开始收发数据
  32.     while(1){
  33.          ret=recvfrom(skfd,buf,MAX_MSG_SIZE,0,(struct sockaddr*)&cltaddr,&addrlen);
  34.          if(ret < 0){
  35.             printf("recv data from %s:%d error!",inet_ntoa(cltaddr.sin_addr),ntohs(cltaddr.sin_port));
  36.          }else if(ret == 0){
  37.             perror("client has been closing socket!");
  38.          }else{
  39.             printf("From %s:%d,%s",inet_ntoa(cltaddr.sin_addr),ntohs(cltaddr.sin_port),buf);
  40.             memset(sndbuf,0,MAX_MSG_SIZE);
  41.             switch(buf[0]){
  42.                   case 'a':
  43.                        strcpy(sndbuf,"After u ,lady...");
  44.                   break;
  45.                   case 'b':
  46.                        strcpy(sndbuf,"Before u ,sir...");
  47.                   break;
  48.                   case 'c':
  49.                        strcpy(sndbuf,"Can u?");
  50.                        break;
  51.                   default:
  52.                        strcpy(sndbuf,"I dont't know what u want!");
  53.             }
  54.             sendto(skfd,sndbuf,strlen(sndbuf),0,(struct sockaddr*)&cltaddr,addrlen);
  55.          }
  56.          memset(buf,0,MAX_MSG_SIZE);
  57.     }
  58.     return 0;
  59. }
       UDP客户端代码:udpclt.c

点击(此处)折叠或打开

  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <errno.h>
  4. #include <string.h>
  5. #include <unistd.h>
  6. #include <netdb.h>
  7. #include <sys/socket.h>
  8. #include <netinet/in.h>
  9. #include <sys/types.h>
  10. #include <arpa/inet.h>
  11. #define MAX_MSG_SIZE 1024

  12. int main(int argc,char** argv){
  13.     int skfd,ret,len;
  14.     struct sockaddr_in srvaddr;
  15.     char buf[MAX_MSG_SIZE]={0};
  16.     char sndbuf[MAX_MSG_SIZE]={0};
  17.     struct in_addr addr;

  18.     //创建数据报式套接字skfd
  19.     if(0>(skfd=socket(AF_INET,SOCK_DGRAM,0))){
  20.          perror("Create Error");
  21.          exit(1);
  22.     }

  23.     if(0 == inet_aton(argv[1],&addr)){
  24.          perror("server addr invalid!");
  25.          exit(1);
  26.     }

  27.     bzero(&srvaddr,sizeof(struct sockaddr_in));
  28.     srvaddr.sin_family = AF_INET;
  29.     srvaddr.sin_addr=addr;
  30.     srvaddr.sin_port=htons(atoi(argv[2]));

  31.     //我们的客户端只接收从服务器地址是srvaddr的主机发来的数据
  32.     if(0>(connect(skfd,(struct sockaddr*)&srvaddr,sizeof(struct sockaddr_in)))){
  33.           perror("Connect Error");
  34.           exit(1);
  35.     }
  36.    
  37.     //开始收发数据
  38.     while(1){
  39.         memset(sndbuf,0,MAX_MSG_SIZE);
  40.         len=read(0,sndbuf,MAX_MSG_SIZE);
  41.         ret=sendto(skfd,sndbuf,strlen(sndbuf),0,(struct sockaddr*)&srvaddr,sizeof(struct sockaddr));
  42.         if(ret == len){
  43.               memset(buf,0,MAX_MSG_SIZE);
  44.               //我们已经知道服务器地址信息了,所以最后两个参数为NULL
  45.               ret=recvfrom(skfd,buf,MAX_MSG_SIZE,0,NULL,NULL);
  46.        
  47.               if(ret < 0){
  48.                      perror("read error from server!");
  49.               }else if(ret == 0){
  50.                      perror("server has been closing socket!");
  51.               }else{
  52.                      buf[ret]='\0';
  53.                      printf("From Server:%s\n",buf);
  54.               }
  55.         }
  56.     }
  57.     return 0;
  58. }
      测试结果:

      我们客户端接收用户命令行输入的指令,然后将其发给UDP服务器端;服务器端收到不同的指令后给客户端予以不同的提示信息,整个流程如上所示。

      udpclt.c的示例代码中我是调用了connect,勤奋好学的童鞋可以动手试验哈不调用connect然后将程序调通吧。

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