对UDP socket缓冲区的理解

对UDP socket缓冲区的理解

     UDP套接字的收发报文要用sendto 和 recvfrom,可以类比TCP套接字的connect和accept,参数里面会标识要发往的对端,或者要接收的对端的IP地址和端口;对UDP套接字connect的行为也只是告诉内核:“帮我做个过滤,我只关心这个对端的报文”,已连接的UDP套接字上可以利用read, write, recv, send函数,通常如果确定了该实体只与一个对端进行通信,那么就选择connect;更重要的是UDP套接字的缓冲区是以一个个报文为单位进行排队的,调用一次recvfrom表示提取一个报文,和TCP基于字节流的方式是不同的。基于这样的原因我们不能在UPD中先读取一定的应用层header,而后在根据头部中长度字段来优雅的读取具体的数据,这样会出错,发生混乱;而在TCP中经常会这么做。

     下面是个例子,客户端以一定长度的内容进行发送,客户端进行接收,先读取首部,而后是具体的数据块,如果用UDP是会出错的。利用TCP可以,代码如下(完整代码可以看这里file-transfer):
typedef struct{
	unsigned char fp[20];
	int chunk_id;
	short flags;
	short chunk_len;
	char data[0];
}TransferUnit;  

// some flags for this chunk transfered.
enum{
        UNIT_NEED_DEDU = 0x0001,   // need deduplication
        UNIT_FILE_START = 0x0002,   // first gram , file metadata
        UNIT_FILE_END = 0x0004,
        CHUNK_OTHER = 0x0008,      // other for extension
};

void recvFile(char name[20],int sockfd){
	
    int ret,fd;
	mode_t fdmode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
    unsigned char mesg[MAX];
    fd_set rdset;
    struct timeval tv;
    int rlen, wlen, chunklen;
    int i;
	TransferUnit *header = (TransferUnit *)malloc(sizeof(TransferUnit));

    fd = open(name,O_RDWR|O_CREAT|O_APPEND,fdmode);
    if(fd == -1)        {
     	  printf("open file %s error:%s \n",name,strerror(errno));
    	  exit(-1);
    }
	while(1){
    	memset(mesg, 0, sizeof(mesg));
		// read the gram header first
      	rlen = read(sockfd, header, sizeof(TransferUnit));
		//printf("%d\n", rlen);
       	if(rlen != sizeof(TransferUnit) ){
       		printf("read sockfd error %s\n",strerror(errno));
            exit(-1);
       	}
		// recv file end 
        if((header->flags ^ UNIT_FILE_END) == 0){
        	printf("recv end.\n");
           	break;
        }
		// recv file metadata 
        if((header->flags ^ UNIT_FILE_START) == 0){
        	int len = header->chunk_len;  // metadata length...
			char metadata[100] = {0};
			printf("==== begin file recv ====,metadata len = %d\n", len);
			if((rlen = read(sockfd, mesg, len)) == len){
				mesg[len] = '\0';
				printf("File name : %s\n", mesg);
			}
           	continue;
        }
		
		chunklen = header->chunk_len;
		// read the actual chunk data 
      	rlen = read(sockfd, mesg, chunklen);
		//printf("chunk_id:%d,chunk_len:%d \n", header->chunk_id, header->chunk_len);
       	if(rlen != chunklen ){
       		printf("read chunk data error %s\n",strerror(errno));
            exit(-1);
       	}
		// write the chunk data to this file
        wlen = write(fd,mesg,rlen);
        if(wlen != rlen ){
        	printf("write error %s\n",strerror(errno));
            exit(-1);
        }        
                
        printf("The %d times write\n",i);
	}
    close(fd);
}


你可能感兴趣的:(Unix网络编程)