2012年6月

trap linux

trap命令用于指定在接收到信号后将要采取的行动,trap命令的参数分为两部分,前一部分是接收到指定信号时将要采取的行动,后一部分是要处理的信号名.

信     号

说     明

HUP(1)

挂起,通常因终端掉线或用户退出而引发

INT(2)

中断,通常因按下Ctrl+C组合键而引发

QUIT(3)

退出,通常因按下Ctrl+\组合键而引发

ABRT(6)

中止,通常因某些严重的执行错误而引发

ALRM(14)

报警,通常用来处理超时

TERM(15)

终止,通常在系统关机时发送


一. trap捕捉到信号之后,可以有三种反应方式:
  1. 执行一段程序来处理这一信号
  2. 接受信号的默认操作
  3. 忽视这一信号

二. trap对上面三种方式提供了三种基本形式:
第一种形式的trap命令在shell接收到signal list清单中数值相同的信号时,将执行双
引号中的命令串:
trap 'commands' signal-list
trap "commands" signal-list

第二种形式的trap命令,为了恢复信号的默认操作:
trap signal-list

第三种形式的trap命令允许忽视信号:
trap " " signal-list

在第一种形式中,执行命令,对于双引号和单引号是有区别的。

#/bin/bash

#忽略信号
#trap " " 2

#双引号,shell第一次设置信号的时候就执行命令和变量的替换,时间不变
trap "echo `date`:can not terminate by ctrl+C" 2		

#单引号,要在shell探测到信号来的时候才执行命令和变量的替换,时间一直变
trap 'echo `date`:can not terminate by ctrl+C' 2		

while [ 1 ]
do
	echo -n "input a num : "
	read num 
	if [ $num -eq -1 ]
	then
		echo "bye"
		break
	fi
	echo "you have enter $num"
done

shell执行过程简介

[root@fire cgi-bin]# export TEST="Hello
> Wolrd
> From
> Linux"
[root@fire cgi-bin]# echo $TEST 
Hello Wolrd From Linux
[root@fire cgi-bin]# echo "$TEST" 
Hello
Wolrd
From
Linux
[root@fire cgi-bin]# echo '$TEST'
$TEST

上面是测试,一直以来对hard quote(单引号)和soft quote(双引号),弄得不是很懂,今早又碰到这个问题。

下面来解释上面的测试代码:

首先从根本出发,shell的任务的就是解释用户的输入,将一大堆的字符串做处理。处理成

command_program options  arguments

这样的格式,说白了就是把一个整的字符串分割成一个一个的字段("word"),那么shell是通过什么分割的呢?通过把<space>,<enter>,<tabs>这三种作为分隔符,拆分成字段。而shell是通过最后用户输入一个<enter>键来标明用户输入的结束,一旦处理到<enter>键,shell就得到了通知:用户已经输入完成了,我可以执行命令去了。之后shell根据默认的IFS处理,进行变量的替换之类的工作,最后提交给程序执行。

看上面的第一个命令

[root@fire cgi-bin]# export TEST="Hello
> Wolrd
> From
> Linux"

因为soft quote能屏蔽<enter>,<enter>键所产生的CR字符不会被shell当作结束符,所以shell继续等待用户的输入,等到用户输入后面的双引号,再输入<enter>键,这时候shell读到了CR字符,就知道用户输入的结束了,于是就开始执行命令。这个时候TEST里面的内容其实是:

Hello<CR>World<CR>From<CR>Linux

注意“Linux”后面是没有<CR>的,之所以会有换行,是因为echo执行结束后会自动送入一个<CR>!

再看第二条命令:

echo $TEST

这时候没有hard quote或者soft quote,shell得到的命令变成

echo Hello<CR>World<CR>From<CR>Linux

现在再去看shell的执行过程,shell就是“把一个整的字符串拆分成字段,字段分割的标识是<space>,<enter>,<tabs>,然后根据默认的IFS处理”,shell读到<CR>,通通都按照默认的IFS(空格)处理。所以处理的结果就可想而知了:所有的<CR>变成了<space>。最后得到的最终命令,按照

command_program options  argumentscommand_program 就是 echo ,options 是无 ,arguments 就是 Hello World From Linux

再来看第三条命令

echo "$TEST"

这个存在soft quote,shell得到命令就是

echo "Hello<CR>World<CR>From<CR>Linux"

这里shell不能处理什么,soft quote里面就是一个参数,最终的命令是

command_program 就是 echo ,options 是无 ,arguments 就是 Hello<CR>World<CR>From<CR>Linux

最后看第四条命令

echo '$TEST'

这个最简单, TEST变量在hard quote里面,所有的meta都被屏蔽了,所以按照最终的命令就是

command_program 就是 echo ,options 是无 ,arguments 就是 $TEST

unlock login keyring

问题原因


fedora修改登入密码之后,每次登入之后都显示unlock login keyring,提示我输入键盘密码。

解决办法


删除配置文件

rm ~/.gnome2/keyrings/login.keyring 

自己动手学TCP/IP–http协议(http报文格式)

HTTP(HyperText Transport Protocol,超文本传送协议)

HTTP请求报文


http请求数据包的格式:头部(request line + header)+  数据(data)

头部和数据包体通过一个空行来隔开,头部的格式主要包括请求行+请求头部。如下图

HTTP请求头

请求行


请求行由请求方法字段URL字段HTTP协议版本字段3个字段组成,它们用空格分隔如:

GET /index.html HTTP/1.1。

HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。这里介绍最常用的GET方法和POST方法。

GET方式:在URL里面就说明要请求的资源,URL里面包含参数,“?”后面就是参数,而“?”前面就是URL的结束。“?ip=192.168.156.11&active=on”这种就是GET方式的包,而服务器把客户端请求的内容在数据段里面发回给客户端。

POST方式:传输的数据不在URL里面出现,而是在数据段里面出现。但是请求头部多了Content-Type和Content-Length两个字段。

请求头部


请求头部由(关键字:<空格>值)对组成,每行一对,关键字和值用英文冒号“:<空格>”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:

User-Agent:产生请求的浏览器类型。

Accept:客户端可识别的内容类型列表。

Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。

下面是GET包的一个例子:传输的数据在URL里

再看看POST包的例子:传输的数据在数据段里面


HTTP响应报文


HTTP响应也由两个个部分组成,分别是:响应头(状态行+消息报头)+响应正文

状态行格式如下:

HTTP-Version Status-Code Reason-Phrase CRLF

HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。

1xx:指示信息--表示请求已接收,继续处理。

2xx:成功--表示请求已被成功接收、理解、接受。

3xx:重定向--要完成请求必须进行更进一步的操作。

4xx:客户端错误--请求有语法错误或请求无法实现。

5xx:服务器端错误--服务器未能实现合法的请求。

下面是http响应包的例子

linux串口传输文件

需要从FPGA上通过串口把文件传输到PC上面,下面是代码。可以测试下串口的buffer大小,我电脑上的是4k多。

write.c

#include   <stdlib.h>           
#include   <stdio.h>
#include   <unistd.h>   
#include  <string.h>      
#include   <sys/types.h> 
#include   <sys/stat.h> 
#include   <fcntl.h>             
#include   <termios.h>         
#include   <errno.h>             
#include   <sys/time.h> 
#include   <time.h> 

#define BUFFER_SIZE 32               //这里可以优化,一次读取文件可以大点,分次写入串口buffer。
#define FALSE -1
#define TRUE 1

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
	    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300,
	    38400,  19200,  9600, 4800, 2400, 1200,  300, };

void set_speed(int fd, int speed);
int set_Parity(int fd,int databits,int stopbits,int parity);

int main(int argc,char *argv[])
{
	char *interface = NULL,*filename = NULL;

	if (argc < 2)
	{
		interface = "/dev/ttyUSB0";
		filename = "hello.txt"	;
	}
	else 
	{
		interface = argv[1];
		filename = argv[2];	
	}

	int fd=open(interface,O_RDWR|O_NOCTTY|O_NDELAY);   
	if(fd == -1)   
	{   
	    printf("%s Open   Error!\n",interface);   
	    return -1;   
	} 
	printf("open %s successfully !",interface);
	
	set_speed(fd,19200);
		if (set_Parity(fd,8,1,'N')== FALSE)
	{
		printf("Set Parity Error\n");
		exit(1);
	}


	int fp = open(filename,O_RDWR);
	if ( fp == -1 )
	{
		printf("open %s failured\n",filename);
		return -1;
	}
	int nread = 1;
	char buffer[BUFFER_SIZE] = {'\0'};
	int sum = 0;

	while(nread > 0)
	{
		int nwrite = 0;
		int tmp = 0,pos=0;	
		nread = read(fp,buffer,BUFFER_SIZE);	//一次要写进去的字节数
		printf("读取字节数 : %d bytes \n",nread);
		int size_write = nread;			//size_write是要本次要写入的总字节数	
		while (size_write > 0)
		{	
	//		int pos = 0>=nwrite?0:nwrite ;			//偏移
//			printf("pos = %d\n",pos);
			tmp = (nwrite > tmp)? nwrite : tmp;			//
			pos = tmp ;
			//printf("tmp = %d,pos %d \n",tmp,pos);
		        nwrite = write(fd,buffer + pos,size_write);	//nwrite一次写进去的字节数		
			if (nwrite == -1)
			{
				sleep (2);
				continue;
			}
			sum = sum + nwrite;			//nwrite 8
			printf("写入字节数 : %d,剩余字节数 : %d \n",sum,size_write);	
			size_write = size_write-nwrite;		//剩下要写进去的字节数				
		}
	}
	printf("sussfully  write %s\n",filename);
	close(fp);
	close(fd);
	return 0; 
}  

void set_speed(int fd, int speed)
{
	int   i;
	int   status;
	struct termios   Opt;
	tcgetattr(fd, &Opt);
	for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
	{
		if  (speed == name_arr[i])
		{
			tcflush(fd, TCIOFLUSH);
			cfsetispeed(&Opt, speed_arr[i]);
			cfsetospeed(&Opt, speed_arr[i]);
			status = tcsetattr(fd, TCSANOW, &Opt);
			if (status != 0)
			{
				perror("tcsetattr fd1");
				return;
			}
		}
		tcflush(fd,TCIOFLUSH);
	}
}

int set_Parity(int fd,int databits,int stopbits,int parity)
{
	struct termios options;
	if (tcgetattr( fd,&options)  !=  0)
	{
		perror("SetupSerial 1");
		return(FALSE);
	}
	options.c_cflag &= ~CSIZE;
	switch (databits) /*设置数据位数*/
	{
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:
			options.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr,"Unsupported data size\n");
			return (FALSE);
	}
	switch (parity)
	{
		case 'n':
		case 'N':
			options.c_cflag &= ~PARENB;   /* Clear parity enable */
			options.c_iflag &= ~INPCK;     /* Enable parity checking */
			break;
		case 'o':
		case 'O':
			options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
			options.c_iflag |= INPCK;             /* Disnable parity checking */
			break;
		case 'e':
		case 'E':
			options.c_cflag |= PARENB;     /* Enable parity */
			options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
			options.c_iflag |= INPCK;       /* Disnable parity checking */
			break;
		case 'S':
		case 's':  /*as no parity*/
			options.c_cflag &= ~PARENB;
			options.c_cflag &= ~CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported parity\n");
			return (FALSE);
	}
	/* 设置停止位*/   
	switch (stopbits)
	{
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported stop bits\n");
			return (FALSE);
	}
	/* Set input parity option */
	if (parity != 'n')
		options.c_iflag |= INPCK;
	options.c_cc[VTIME] = 150; // 15 seconds
	options.c_cc[VMIN] = 0;

	tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
	if (tcsetattr(fd,TCSANOW,&options) != 0)
	{
		perror("SetupSerial 3");
		return (FALSE);
	}
	return (TRUE);
 }

read.c


#include   <stdlib.h>           
#include   <stdio.h>
#include   <unistd.h>   
#include  <string.h>      
#include   <sys/types.h> 
#include   <sys/stat.h> 
#include   <fcntl.h>             
#include   <termios.h>         
#include   <errno.h>             
#include   <sys/time.h> 
#include   <time.h> 

#define BUFFER_SIZE 32
#define MAX 1024
#define FALSE -1
#define TRUE 1

int speed_arr[] = { B38400, B19200, B9600, B4800, B2400, B1200, B300,
	    B38400, B19200, B9600, B4800, B2400, B1200, B300, };
int name_arr[] = {38400,  19200,  9600,  4800,  2400,  1200,  300,
	    38400,  19200,  9600, 4800, 2400, 1200,  300, };

void set_speed(int fd, int speed);
int set_Parity(int fd,int databits,int stopbits,int parity);

int main(int argc,char *argv[])
{
	char *interface = NULL,*filename = NULL;

	if (argc < 2)
	{
		interface = "/dev/ttyACM0";
		filename = "hello.txt"	;
	}
	else 
	{
		interface = argv[1];
		filename = argv[2];	
	}
 
	int fd=open(interface,O_RDWR|O_NOCTTY|O_NDELAY);   
	if(fd == -1)   
	{   
	    printf("%s Open   Error!\n",interface);   
	    return -1;   
	} 
	printf("open %s successfully !",interface);
	
	set_speed(fd,19200);
	if (set_Parity(fd,8,1,'N')== FALSE)
	{
		printf("Set Parity Error\n");
		exit(1);
	}

	int fp = open(filename,O_RDWR|O_CREAT);
	if ( fp == -1 )
	{
		printf("open %s failured\n",filename);
		return -1;
	}

	int nread = 1,nwrite = 0;
	char buffer[BUFFER_SIZE] = {'\0'};
	while(1)
	{
		nread = read(fd,buffer,BUFFER_SIZE);   
		if (nread > 0)
		{
//			printf("读取 %d bytes \n",nread);
			nwrite = write(fp,buffer,nread);	
//			printf("写入 %d bytes \n",nwrite);
			if (nwrite <= 0)
			{		
				printf("write into file finished n");
				break;
			}
		}
	}

	printf("sussfully get %s\n",filename);
	return 0; 
}  

void set_speed(int fd, int speed)
{
	int   i;
	int   status;
	struct termios   Opt;
	tcgetattr(fd, &Opt);
	for ( i= 0;  i < sizeof(speed_arr) / sizeof(int);  i++)
	{
		if  (speed == name_arr[i])
		{
			tcflush(fd, TCIOFLUSH);
			cfsetispeed(&Opt, speed_arr[i]);
			cfsetospeed(&Opt, speed_arr[i]);
			status = tcsetattr(fd, TCSANOW, &Opt);
			if (status != 0)
			{
				perror("tcsetattr fd1");
				return;
			}
		}
		tcflush(fd,TCIOFLUSH);
	}
}

int set_Parity(int fd,int databits,int stopbits,int parity)
{
	struct termios options;
	if (tcgetattr( fd,&options)  !=  0)
	{
		perror("SetupSerial 1");
		return(FALSE);
	}
	options.c_cflag &= ~CSIZE;
	switch (databits) /*设置数据位数*/
	{
		case 7:
			options.c_cflag |= CS7;
			break;
		case 8:
			options.c_cflag |= CS8;
			break;
		default:
			fprintf(stderr,"Unsupported data size\n");
			return (FALSE);
	}
	switch (parity)
	{
		case 'n':
		case 'N':
			options.c_cflag &= ~PARENB;   /* Clear parity enable */
			options.c_iflag &= ~INPCK;     /* Enable parity checking */
			break;
		case 'o':
		case 'O':
			options.c_cflag |= (PARODD | PARENB);  /* 设置为奇效验*/ 
			options.c_iflag |= INPCK;             /* Disnable parity checking */
			break;
		case 'e':
		case 'E':
			options.c_cflag |= PARENB;     /* Enable parity */
			options.c_cflag &= ~PARODD;   /* 转换为偶效验*/  
			options.c_iflag |= INPCK;       /* Disnable parity checking */
			break;
		case 'S':
		case 's':  /*as no parity*/
			options.c_cflag &= ~PARENB;
			options.c_cflag &= ~CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported parity\n");
			return (FALSE);
	}
	/* 设置停止位*/   
	switch (stopbits)
	{
		case 1:
			options.c_cflag &= ~CSTOPB;
			break;
		case 2:
			options.c_cflag |= CSTOPB;
			break;
		default:
			fprintf(stderr,"Unsupported stop bits\n");
			return (FALSE);
	}
	/* Set input parity option */
	if (parity != 'n')
		options.c_iflag |= INPCK;
	options.c_cc[VTIME] = 150; // 15 seconds
	options.c_cc[VMIN] = 0;

	tcflush(fd,TCIFLUSH); /* Update the options and do it NOW */
	if (tcsetattr(fd,TCSANOW,&options) != 0)
	{
		perror("SetupSerial 3");
		return (FALSE);
	}
	return (TRUE);
 }

嵌入式boa服务器搭建和移植

1.先下载源码 http://www.boa.org/ ==> boa-0.94.13.tar.g

2.tar -xvf boa-0.94.13.tar.g

3./configure

4.修改Makefile

CC =or32-linux-gcc

CPP = or32-linux-gcc–E

5.修改boa.c,注释下面几行

if (setuid(0) != -1) {
DIE("icky Linux kernel bug!");
}改为/*if (setuid(0) != -1) {
DIE("icky Linux kernel bug!");
}*/

6.修改文件compat.h

#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff
修改成
#define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff

7.make 编译,这时候就会生成一个boa的可执行文件

boa的配置文件是boa.conf,大概的配置是这样的:

Port 80   //服务访问端口

User 0
Group 0   

ErrorLog /var/log/boa/error_log //错误日志地址
AccessLog /var/log/boa/access_log  //访问日志文件

DocumentRoot /var/www  //HTML文档的主目录

UserDir public_html  //

DirectoryIndex index.html  //默认访问文件

DirectoryMaker /usr/lib/boa/boa_indexer

KeepAliveMax 1000   //一个连接所允许的HTTP持续作用请求最大数目

KeepAliveTimeout 10  //HTTP持续作用中服务器在两次请求之间等待的时间数,以秒为单位

MimeTypes /etc/mime.types  //指明mime.types文件位置

DefaultType text/plain  //文件扩展名没有或未知的话,使用的缺省MIME类型

CGIPath /bin:/usr/bin:/usr/local/bin   //提供CGI程序的PATH环境变量值

Alias /doc /usr/doc    //为路径加上别名

ScriptAlias /cgi-bin/ /var/www/cgi-bin/ //输入站点和CGI脚本位置

对于调试阶段,可以把主机的目录挂载到目标板上面,这样就方便调试。

假设主机的NFS共享目录是/NFS,在NFS下面建立一个www的文件夹,然后在www的文件夹下面建立images,cig-bin目录,还有一个index.html,可以随便找一个主页就可以了。在主机上

mkdir /NFS/www

mkdir /NFS/www/images

mkdir /NFS/www/cgi-bin

cp <dir>/index.html /NFS/www/

然后在板子上面建立/var/www的目录

mkdir /var/www

mount -t nfs -o nolock <主机IP>:/NFS/www /var/www //挂载目标www

板子上建立日志存放目录,在板子上:

mkdir /var/log

mkdir /var/log/boa

对于boa配置文件的移植,可以把主机上的boa.conf拷贝到主机的NFS共享目录/NFS/www下面。

对于mime.types,直接用主机上/etc/mime.types就行了,拷贝到/NFS/www下面。

对于boa可执行文件,拷贝到/NFS/www/下面。

在主机上:

cp /etc/mime.types /NFS/www/

cp <boa dir>/boa.conf /NFS/www/

cp <boa dir>/src/boa  /NFS/www/

然后再在板子上把上面共享的文件拷贝到自己系统上,注意此时只要对/var/www操作就行了,在主机上:

cp /var/www/mime.types /etc/mime.types

mkdir /etc/boa

cp /var/www/boa.conf  /etc/boa/boa.conf

cp /var/www/boa /sbin/boa

/sbin/boa 启动boa服务器

在浏览器里输入板子的IP就可以测试了。

自己动手学TCP/IP--tftp协议

TFTP(Trivial File Transfer Protocol,简单文件传输协议

1.tftp的服务端口号是69

2.tftp是基于udp协议的

3.tftp是明文传输的,是一种比较轻量型的协议,一般用于bootloader加载内核

TFTP工作流程

服务端开启tftp服务,tftp是一种stand_alone服务,不是常驻内存的,是在有需要的时候才去调用的。首先,客户端发送一个读(RRQ:2个字节)或者写(WRQ:2个字节)的请求,数据包的目标端口是69。对于读或者写的报文格式如下:

RRQ/WRQ(2个字节)+文件名(N字节)+0(1字节)+模式(N字节)+0(1字节)

目前模式字段主要有2种:netascii,这是8位的ASCII码形式;另一种是octet,这是8位源数据类型。对于netascii是把回车和换行(CR/LF)解释成两个字节的。可以查看http://www.firefoxbug.com/?p=1041

tftp-server接收到数据包:如果是发现是读(RRQ),就重新随机分配一个端口,直接发送数据(DATA:2个字节)+块编号(2个字节),然后是0~512字节数据包。客户端接收到数据包,发给服务端(ACK:2个字节)+块编号(2个字节)。如果是普通的数据包,那么数据段的大小一定是512字节,如果是最后一个数据包,肯定是小于512字节的。tftp就是通过发现了一个数据段小于512字节的数据包来声明结束文件的传输了。那么一个要传输的文件刚还是512字节的整数倍怎么办呢?tftp会在最后传输一个数据段大小是0包。

tftp-server接收到数据包:如果发现是写(WRQ),服务端就发回(ACK:2个字节)+(块编号0:2个字节)的包,接着客户端就发送(DATA:2个字节)+(块编号1:2个字节)+数据段给服务端,服务端发回(ACK:2个字节)+(块编号1:2个字节)。。。依次发送。

错误信息是系统自定义的,格式主要是error(2个字节)+错误码(2个字节)+错误信息(N个字节)

下面是tftp数据包的格式图

下面是C语言解析tftp包的一小段代码:

struct tftphdr {
short th_opcode; /* packet type */
union {
unsigned short tu_block; /* block # */
short tu_code; /* error code */
char tu_stuff[1]; /* request packet stuff */
} __attribute__ ((__packed__)) th_u;
char th_data[1]; /* data or error string */
} __attribute__ ((__packed__));

// 解析udp包,packet_buffer是用rawsocket抓出来的以太网包,
void ParseUDPPacket(unsigned char *packet_buffer)
{
struct ethhdr *eth_header;//以太网头
struct iphdr *ip_header; //ip头
struct udphdr *udp_header; //tcp头
eth_header = (struct ethhdr*)packet_buffer;
ip_header = (struct iphdr*)(packet_buffer + sizeof(struct ethhdr));
udp_header = (struct udphdr*)(packet_buffer + sizeof(struct ethhdr) + ip_header->ihl*4);
unsigned char *data = NULL;
data = (packet_buffer + sizeof(struct ethhdr) + ip_header->ihl*4 + 8);//8代表UDP包头
struct tftphdr *tp = (struct tftphdr *)data; // /usr/include/arpa/tftp.h
tftp_print(data,ntohs(udp_header->len)-8); //ntohs(udp_header->len)-8表示udp数据包长度
}

/*
* Print trivial file transfer program requests
*/
void tftp_print(register const u_char *bp, u_int length)
{
register const struct tftphdr *tp;
register const u_char *p;
register int opcode,i;
static char tstr[] = " [|tftp]";
char buffer[520] = {'\0'};
tp = (const struct tftphdr *)bp;

// printf(" %d", length);
// printf("length of tftp_data = %d\n",length);

/* Print tftp request type */

opcode = EXTRACT_16BITS(&tp->th_opcode);
printf(" %s",tok2str(op2str, "tftp-#%d", opcode));
/* Bail if bogus opcode */

switch (opcode) {

case RRQ:
break;

case WRQ:
break;

case ACK:
break;

case DATA:
break;

case ERROR:
break;

default:
/* We shouldn't get here */
printf("(unknown #%d)", opcode);
break;
}
return;
}

详细的可以查看tcpdump的源码。