`
memorymyann
  • 浏览: 266335 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

13.UDP编程

阅读更多

虽然UDP不保证传输的可靠性,但UDP无链接性相对于TCP仍然有其它方面的优势。比如之前的聊天程序,基于TCP实现的聊天程序在群聊方面就很麻烦,因为链接总是双方的通信,很难加入第3方(或许应该说更本就不能加入)。其次UDP没有建立链接过程速度相对来说更快一点(至少它没有三路握手,发送数据也不需要对方确认收到)。

 

UDP编程过程:

服务器端:

1.创建socket

2.绑定端口

3.调用recvfrom获取数据

4.处理请求

5.调用sendto发送数据

 

客户端:

1.创建socket

2.sendto发送数据

3.recvfrom接受数据

4.close

 

没有建立链接的过程,只管接受发送数据(数据丢失,UDP不提供重发,也不会报错,当然不会报错也不是绝对的。比如发送包太大超过了UDP承载限度,则会返回错误。但可惜就是这点报错,不同的地方也不一定都是支持的)。所以相对来说UDP发送小批量数据更为有效,也更为方便。

 

下面介绍下几个之前UDP重要的函数。

 

int socket(AF_INET, SOCK_DGRAM,0)这个之前曾经说过,前2个参数不同的组合则代表不同的协议。

 

#include "sys/types.h"

#include "sys/socket.h"

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

返回值 如果成功则返回发送字节数,返回-1出错。

参数buf是要发送的内容,len发送的长度,flags在这里总是为0, to要发送的目的套接口描述结构,to_len指的是to的长度。

 

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

很好理解,稍微不同的地方是from指的是发送者的套接口描述结构。如果你要处理完后,有消息向该套接口发送则建议取出,如果只是取数据,并不对来源发送任何响应,则直接放NULL即可。

 

(注:这个2个函数也可以用于TCP中,用于替代read和write,但一般没有这个必要)。

 

例子就给之前聊天程序用UDP改写下:

[root@liumengli net]# cat chat_udp_server.c

#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "sys/wait.h"
#include "sys/select.h"
#include "sys/poll.h"

#define MAXSIZE 100

int main(int argc, char ** argv) {
        int sockfd;
        struct sockaddr_in serv_socket;
        struct sockaddr_in * client_socket = NULL;
        char send[MAXSIZE + 1];
        char recv[MAXSIZE + 1];

        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        bzero(&serv_socket, sizeof(serv_socket));
        serv_socket.sin_family = AF_INET;
        serv_socket.sin_addr.s_addr = htonl(INADDR_ANY);
        serv_socket.sin_port = htons(atoi(argv[1]));
        bind(sockfd, (struct sockaddr *)&serv_socket, sizeof(serv_socket));
        socklen_t len = sizeof(client_socket);

        for(;;) {
                fd_set rest;
                FD_ZERO(&rest);
                FD_SET(0, &rest);
                FD_SET(sockfd, &rest);
                int flag = select(sockfd + 1,&rest, NULL, NULL, NULL);
                if(flag <= 0)
                        continue;
                if(FD_ISSET(0, &rest) && client_socket) {
                        int n = read(0, send, MAXSIZE);
                        int flag = sendto(sockfd, send, n, 0, (struct sockaddr *)client_socket, sizeof(struct sockaddr_in));
                        if(flag == -1)
                                printf("system message:send failed!\n");
                }

                if(FD_ISSET(sockfd, &rest)) {
                        client_socket = (struct sockaddr_in *) malloc (sizeof(struct sockaddr_in));
                        socklen_t len = sizeof(struct sockaddr_in);//这步一定要有,一定要使len长度确定,不然就会发现无法正确的到客户端信息,如果不需要返回数据给客户端,则无所谓,当然聊天是双方,得不到客户端信息,则没法“聊”下去
                        int n = recvfrom(sockfd, recv, MAXSIZE, 0, (struct sockaddr *)client_socket, &len);
                        recv[n] = '\0';
                        printf("client message:%s", recv);
                }
        }
}

 

[root@liumengli net]# cat chat_udp_client.c
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "sys/select.h"

#define MAXSIZE 100

int main(int argc, char ** argv) {
        int sockfd;
        struct sockaddr_in serv_socket;
        int maxfdpl;
        char send[MAXSIZE];
        char recv[MAXSIZE];

        if(argc != 2) {
                printf("please input port");
                exit(1);
        }

        sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        bzero(&serv_socket, sizeof(serv_socket));
        serv_socket.sin_family = AF_INET;
        serv_socket.sin_port = htons(atoi(argv[1]));
        inet_pton(AF_INET, "192.168.1.235", &serv_socket.sin_addr);

        for(;;) {
                fd_set rest;
                FD_ZERO(&rest);
                FD_SET(0, &rest);
                FD_SET(sockfd, &rest);
                int flag = select(sockfd + 1, &rest, NULL, NULL, NULL);
                if(flag <= 0)
                        continue;
                if(FD_ISSET(0, &rest)) {
                        int n = read(0, send, MAXSIZE);
                        int temp = sendto(sockfd, send, n, 0, (struct sockaddr *)&serv_socket, sizeof(struct sockaddr_in));
                        if(temp == -1)
                                printf("system message:send failed\n");
                }

                if(FD_ISSET(sockfd, &rest)) {
                        int n = recvfrom(sockfd, recv, MAXSIZE, 0, (struct sockaddr *)NULL, NULL);
                        recv[n] = '\0';
                        printf("server message:%s", recv);
                }
        }
}

 

相对于TCP,UDP的要简单很多。因为UDP没有链接,所有链接带来了很多麻烦在这里就不复存在,首先不用为释放链接烦恼。其次,服务器设计方面要简单,TCP是以链接为基础的传输服务,因此TCP服务器都是并发多进程为每一个链接创立一个线程然后为每一个客户服务。而UDP没有链接,UDP服务器常常采用迭代式设计不需要多进程。(当然,如果你设计成多进程为每一个客户服务,也似可以的,而且会提高效率,当然给自己写代码也带来麻烦)。

 

其次,有点注意,UDP不提供确认。如果在聊天过程中,我们把服务器关了。客户端不关,你仍然会发现客户端不会报任何错误(UDP不提供确认)。如果想提供保证,则需要自己实现。

 

TCP在调用read去读套接口数据时候,如果读到0,一般则意味着对方发送fin包,需要关闭链接。UDP如果调用recvfrom读到0,没有关闭链接一说,所以也不存在所谓的FIN,UDP接受到0则表示数据端内确实没有数据。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics