`
simohayha
  • 浏览: 1386313 次
  • 性别: Icon_minigender_1
  • 来自: 火星
社区版块
存档分类
最新评论

tcp协议栈处理各种事件的分析

阅读更多
首先我们来看socket如何将一些状态的变化通知给对应的进程,比如可读,可写,出错等等。

先来看sock结构中这几个相关域:

struct sock {
..........................
wait_queue_head_t	*sk_sleep;
.....................................
void		(*sk_state_change)(struct sock *sk);
void		(*sk_data_ready)(struct sock *sk, int bytes);
void		(*sk_write_space)(struct sock *sk);
void		(*sk_error_report)(struct sock *sk);
int	(*sk_backlog_rcv)(struct sock *sk,struct sk_buff *skb);  
};


这里我们一个个来说。

sk_sleep是一个等待队列,也就是所有阻塞在这个sock上的进程,我们通知用户进程就是通过这个等待队列来做的。

sk_state_change是一个sock状态改变的回调函数,也就是当sock的状态变迁了(比如从established到clos_wait状态),那么就会调用这个函数。

sk_data_ready 这个函数是当当前sock有可读数据的时候,就会被调用。

sk_write_space 这个函数是当当前的sock有可写的空间的时候,就会被调用。

sk_error_report 这个函数是当当前的sock出错(比如收到一个rst)后就会被调
用.

接下来我们就来看这几个函数的具体实现。

在看之前我们先来看一个sk_has_sleeper的实现,它的作用就是用来判断是否有进程阻塞在当前的sock,也就是sk_slleep这个等待队列上是否有元素。

我们每次唤醒用户进程之前都会调用这个函数进行判断,如果没有那就不用唤醒进程了。

static inline int sk_has_sleeper(struct sock *sk)
{
	smp_mb__after_lock();
///判断等待队列是否有元素。
return sk->sk_sleep && waitqueue_active(sk->sk_sleep);
}


先来看sock_def_wakeup,这个函数用来唤醒所有阻塞在当前的sock的进程。

static void sock_def_wakeup(struct sock *sk)
{
	read_lock(&sk->sk_callback_lock);
///先检测是否有进程阻塞在当前的sock上,如果有才会去唤醒等待队列。
	if (sk_has_sleeper(sk))
		wake_up_interruptible_all(sk->sk_sleep);
	read_unlock(&sk->sk_callback_lock);
}


然后是sk_data_ready,这个是用来发起可读事件的。


static void sock_def_readable(struct sock *sk, int len)
{
	read_lock(&sk->sk_callback_lock);
///首先判断是否有进程休眠在sock上。如果有则同步唤醒所有的阻塞的进程,这里注意传递的参数是POLLIN,这样我们就能通过epoll这类来捕捉事件了。
	if (sk_has_sleeper(sk))
	       wake_up_interruptible_sync_poll(sk->sk_sleep, POLLIN | POLLRDNORM | POLLRDBAND);
///这里主要是处理异步的唤醒事件。
	sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
	read_unlock(&sk->sk_callback_lock);
}


接下来是sock_def_write_space,它用来发起可写事件。

可以看到这个实现和前面的有所不同,它会先判断已提交的内存大小sk_wmem_alloc的2倍和sk的发送缓冲区(sk_sndbuf),如果大于sndbuf,则不要唤醒。这是因为为了防止太小的缓冲区导致每次写只能写一部分,从而效率太低。


static void sock_def_write_space(struct sock *sk)
{
	read_lock(&sk->sk_callback_lock);

	/* Do not wake up a writer until he can make "significant"
	 * progress.  --DaveM
	 */
///先判断内存使用。
	if ((atomic_read(&sk->sk_wmem_alloc) << 1) <= sk->sk_sndbuf) {
///判断是否有需要被唤醒的进程。
	if (sk_has_sleeper(sk))
///同步的唤醒进程,可以看到事件是POLLOUT.
	       wake_up_interruptible_sync_poll(sk->sk_sleep, POLLOUT |
						POLLWRNORM | POLLWRBAND);
///这里也是处理一些异步唤醒。
		if (sock_writeable(sk))
			sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
	}

	read_unlock(&sk->sk_callback_lock);
}


最后是sock_def_error_report,他是当sock有错误时会被调用来唤醒相关的进程。

static void sock_def_error_report(struct sock *sk)
{
	read_lock(&sk->sk_callback_lock);

///这里只需要注意上报的事件是POLL_ERR。
	if (sk_has_sleeper(sk))
		wake_up_interruptible_poll(sk->sk_sleep, POLLERR);
///处理异步的唤醒。
	sk_wake_async(sk, SOCK_WAKE_IO, POLL_ERR);
	read_unlock(&sk->sk_callback_lock);
}


这里我们看到所有的处理最终都会调用sk_wake_async来处理异步的事件。这个函数其实比较简单,主要是处理如果我们在用户空间设置了相关的句柄的O_ASYNC属性时,也就是信号io,我们需要单独的处理信号io。


int sock_wake_async(struct socket *sock, int how, int band)
{
	if (!sock || !sock->fasync_list)
		return -1;
///通过how的不同来进行不同的处理,这里每次都要先测试flags的状态。
	switch (how) {
	case SOCK_WAKE_WAITD:
		if (test_bit(SOCK_ASYNC_WAITDATA, &sock->flags))
			break;
		goto call_kill;
	case SOCK_WAKE_SPACE:
		if (!test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags))
			break;
		/* fall through */
	case SOCK_WAKE_IO:
call_kill:
///发送信号给应用进程。(SIGIO)
		__kill_fasync(sock->fasync_list, SIGIO, band);
		break;
	case SOCK_WAKE_URG:
///这里是发送urgent信号(SIGURG)给应用进程。
		__kill_fasync(sock->fasync_list, SIGURG, band);
	}
	return 0;
}


看完这些函数,我们再来看这些函数什么时候会被调用并且调用后如何被select,epoll这类的框架所捕捉。

先来看sk_state_change(也就是sock_def_wakeup),直接搜索内核代码。可以看到有6个地方调用了sk_state_change函数,分别是inet_shutdown,tcp_done,tcp_fin,tcp_rcv_synsent_state_process以及tcp_rcv_state_process函数。可以看到这些基本都是tcp状态机的的状态变迁的地方,也就是每次状态的变迁都会调用state_change函数。

我们一个个来看,有些可能前面已经分析过了,这里就简要介绍下。先是inet_shutdown.

这个函数我们知道系统调用shutdown最终会调用到这个函数:


int inet_shutdown(struct socket *sock, int how)
{
	struct sock *sk = sock->sk;
.......................................
///唤醒等待的进程。
	sk->sk_state_change(sk);
	release_sock(sk);
	return err;
}


然后是tcp_done,这个函数也就是在整个tcp连接完全断开或者说当前的sock没有和任何进程相关联(SOCK_DEAD)的状态,或者收到reset时,所做的工作。要注意这个函数不会free buffer的,它只是处理状态机的一些东西:


void tcp_done(struct sock *sk)
{
	if (sk->sk_state == TCP_SYN_SENT || sk->sk_state == TCP_SYN_RECV)
		TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_ATTEMPTFAILS);

///最终状态机回到初始状态(也就是接到最后一个ack).
	tcp_set_state(sk, TCP_CLOSE);
///清理定时器
	tcp_clear_xmit_timers(sk);
///设置shutdown的状态。
	sk->sk_shutdown = SHUTDOWN_MASK;

///如果还有进程和它相关联则调用state_change来唤醒相关进程。
	if (!sock_flag(sk, SOCK_DEAD))
		sk->sk_state_change(sk);
	else
///否则destroy这个sock
		inet_csk_destroy_sock(sk);
}


然后是tcp_fin函数,我们要重点来看这个函数,这个函数代表我们接受到了一个fin分节。

1 我们知道当服务器端收到一个fin后会,它会发送一个ack,然后通知应用程序,紧接着进入close_wait状态,等待应用程序的关闭。

2 当我们处于TCP_FIN_WAIT1状态(客户端)时,如果收到一个fin,会进入tcp_closing状态。

3 当我们处于TCP_FIN_WAIT2状态(客户端)时,如果收到fin,则会进入time_wait状态。

static void tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
{
	struct tcp_sock *tp = tcp_sk(sk);


	inet_csk_schedule_ack(sk);

	sk->sk_shutdown |= RCV_SHUTDOWN;
///设置sock的状态。
	sock_set_flag(sk, SOCK_DONE);

///不同的状态进入不同的处理
	switch (sk->sk_state) {
///syn_recv状态和establish状态的处理是相同的。
	case TCP_SYN_RECV:
	case TCP_ESTABLISHED:
		/* Move to CLOSE_WAIT */
///直接进入close_wait状态
		tcp_set_state(sk, TCP_CLOSE_WAIT);
///设置pingpong,也就是这个时候所有数据包都是立即ack
		inet_csk(sk)->icsk_ack.pingpong = 1;
		break;
///处于这两个状态,说明这个fin只不过是个重传数据包。
	case TCP_CLOSE_WAIT:
	case TCP_CLOSING:

		break;
	case TCP_LAST_ACK:
		/* RFC793: Remain in the LAST-ACK state. */
		break;

///我们在等待fin,
	case TCP_FIN_WAIT1:
///因此我们先发送ack,给对端,然后设置状态为closing状态。
		tcp_send_ack(sk);
		tcp_set_state(sk, TCP_CLOSING);
		break;

///这个状态说明我们已经接到ack,在等待最后的fin。
	case TCP_FIN_WAIT2:
///发送ack给对方,
		tcp_send_ack(sk);
//然后进入time-wait状态,并启动定时器。
		tcp_time_wait(sk, TCP_TIME_WAIT, 0);
		break;
	default:
///其他的状态收到都是错误的。
		printk(KERN_ERR "%s: Impossible, sk->sk_state=%d\n",
		       __func__, sk->sk_state);
		break;
	}

///可以看到一接到fin,就会马上清理调ofo队列。
	__skb_queue_purge(&tp->out_of_order_queue);
	if (tcp_is_sack(tp))
		tcp_sack_reset(&tp->rx_opt);
	sk_mem_reclaim(sk);

///这里判断是否有进程和sock关联。
	if (!sock_flag(sk, SOCK_DEAD)) {
///ok,现在通知进程状态的改变。
		sk->sk_state_change(sk);
///这里注意如果是两端只有一端关闭,则是不会发送poll_hup而是发送poll_in也就是可读事件(这里也只是处理信号io的东西)
		if (sk->sk_shutdown == SHUTDOWN_MASK ||
		    sk->sk_state == TCP_CLOSE)
			sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_HUP);
		else
			sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
	}
}


然后就是tcp_rcv_synsent_state_process函数,这个函数主要用来处理syn_sent状态的数据。这个函数前面的blog已经介绍过了,这里就不介绍了,这里它会在接受到ack之后调用state_change函数,也就是要进入established状态。

接下来就是tcp_rcv_state_process函数中的两个调用的地方,第一个地方是当处于TCP_SYN_RECV状态然后接受到一个ack,此时需要进入established状态,则会调用state_change函数.

这里要注意的是第二个地方,那就是当处于TCP_FIN_WAIT1,然后接受到了一个ack,此时由于应用程序调用close时,设置了linger套接口选项,因此我们这里有可能需要唤醒休眠等待的进程。

接下来的可读和可写的回调函数我就简单的介绍下,先来看可读函数sk_data_ready。它会在收到urgent数据后马上调用(tcp_urg中).还有就是dma中,再就是tcp_rcv_established中,我们主要来看这个,因为这个是我们最主要的接收函数。

这里要知道我们是处于软中断上下文中,然后调用data_ready来通知应用程序的。
来看代码片断:

if (eaten)
				__kfree_skb(skb);
			else
				sk->sk_data_ready(sk, 0);


可以看到只有当eaten为0时才会唤醒等待队列。并且这段代码是处于fast path中的。而eaten为0说明拷贝给用户空间失败。

然后是sk_write_space函数,这个函数主要是在tcp_new_space中以及sock_wfree中被调用。

这两个函数第一个是当接收到一个ack之后,我们能从write_queue中删除这条报文的时候被调用。

第二个函数是当free写buff的时候被调用。


接下来来详细看sk_error_report 回调函数,这个函数,我们主要来看两个调用它的地方,一个是tcp_disconnect中,一个是tcp_reset中。

第一个函数是用来断开和对端的连接。第二个函数是处理rst分节的。

第一个函数就不介绍了,我前面的blog已经分析过了,这个函数被调用,是当我们自己调用close的时候或者说当前的sock要被关闭的时候会调用这个函数。

来看第二个函数,也就是处理rst分节的部分。

这里可以看到是先设置错误号,也就是防止应用程序读写错误的sock,然后发送给应用程序不同的信号。
static void tcp_reset(struct sock *sk)
{
///不同的状态设置不同的错误号
	switch (sk->sk_state) {
	case TCP_SYN_SENT:
		sk->sk_err = ECONNREFUSED;
		break;
	case TCP_CLOSE_WAIT:
		sk->sk_err = EPIPE;
		break;
	case TCP_CLOSE:
		return;
	default:
///其他状态都是ECONNRESET。
		sk->sk_err = ECONNRESET;
	}

///传递错误给应用程序。
	if (!sock_flag(sk, SOCK_DEAD))
		sk->sk_error_report(sk);

	tcp_done(sk);
}


这里我们来看这几个错误号,可以看到

1 当TCP_SYN_SENT状态时如果收到rst,则会设置错误号为ECONNREFUSED,并唤醒进程。

2 当为TCP_CLOSE_WAIT状态时,则设置错误号为EPIPE。

3 剩余的状态的话错误号都为 ECONNRESET。

接下来我们就来看当读或者写已经收到rst的sock会出现什么情况。

先是写函数,前面的blog我们知道写函数是tcp_sendmsg。我们来看代码片断:

///如果是established或者close_wait状态则进入wait_connect处理,我们上面可以看到(tcp_reset)接收到rst后,我们会通过tcp_done设置状态为tcp_close,所以我们一定会进入这里。
if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))
///这里不等于0也就是表示sk_err有值。接下来会详细分析这个函数。
	if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
			goto out_err;

..................................
...............................................
out_err:
///调用这个函数进行处理
	err = sk_stream_error(sk, flags, err);



在看这个函数之前我们先来看sock_error函数,这个用来返回sk->sk_err,然后清除这个error。


static inline int sock_error(struct sock *sk)
{
	int err;
///如果为空则直接返回。
	if (likely(!sk->sk_err))
		return 0;
///这个是用汇编实现的。返回sk_err然后设置sk_err为0.也就是清空。
	err = xchg(&sk->sk_err, 0);
//返回错误号,这里主要加了个负号。
	return -err;
}


然后我们来看sk_stream_wait_connect的实现。我们这里只看他的错误处理部分。

int sk_stream_wait_connect(struct sock *sk, long *timeo_p)
{
	struct task_struct *tsk = current;
	DEFINE_WAIT(wait);
	int done;

	do {
///首先取得sk_err,然后清空sk_err
		int err = sock_error(sk);
///如果err存在则直接返回。
		if (err)
			return err;
//到达这里说明err是0,如果状态不是sent或者recv则返回-EPIPE.
		if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV))
			return -EPIPE;
.......................................

	} while (!done);
	return 0;
}

这里为什么要判断完err之后还要再次判断状态呢,等下面我们分析sk_stream_error函数之后一起来看。

然后来看sk_stream_error函数,要知道err这里传递进来的是wait_connect返回的值.

int sk_stream_error(struct sock *sk, int flags, int err)
{
///如果传递进来的err是-EPIPE,进入第一个处理。
	if (err == -EPIPE)
//如果sk_err没有被清掉,则返回sk_err否则返回-EPIPE.
		err = sock_error(sk) ? : -EPIPE;
//如果是EPIPE的话,会直接发送信号给应用程序
	if (err == -EPIPE && !(flags & MSG_NOSIGNAL))
		send_sig(SIGPIPE, current, 0);
///返回错误号
	return err;
}


接下来我们把两个函数一起来看。

当我们收到一个rst之后我们设置状态为tcp_close.然后设置相应的错误号。

假设现在我们在收到rst的sock上调用send方法,此时我们会进入sk_stream_wait_connect,然后直接返回err。现在就有3种情况:

1 我们在TCP_SYN_SENT状态收到的rst,此时的错误号为ECONNREFUSED,因此我们会返回ECONNREFUSED,设置err为ECONNREFUSED,然后进入sk_stream_error处理。由于我们的err并不等于EPIPE,因此我们会直接返回错误号。也就是应用进程回收到错误号。不过此时sk_err已经清0了。

2 当我们在TCP_CLOSE_WAIT收到rst,此时的错误号为EPIPE,因此我们进入sk_stream_error的时候,err就是EPIPE,此时我们就会发送EPIPE信号给进程,并返回EPIPE的错误号。

3 我们在其他状态收到rst,此时错误号为ECONNRESET,可以看到这个的处理和上面TCP_SYN_SENT状态收到的rst的处理一致,只不过返回的错误号不一样罢了。

紧接着,我们来看当我们第一次调用完毕之后,第二次调用会发生什么。这里可以看到只有上面的1,3两种情况第二次调用才会有效果。

当我们再次进入sk_stream_wait_connect,此时由于sk_err已经被清0,因此我们会进入第二个处理也就是状态判断,由于我们是tcp_close状态,因此我们会直接返回-EPIPE.此时我们出来之后,会再次进入sk_stream_error,而之后调用就和上面的2 一样了。会直接发送epipe信号,然后返回epipe.


然后是tcp_recvmsg,也就是接收函数。

下面是代码片断。这里要知道,就算收到rst,如果sock没有完全关闭,我们还是可以从缓冲区读取数据的。

这个函数我前面的blog已经介绍过了。详细的介绍可以看我前面的blog。

//copied为已经复制的数据。这里可以看到如果已经复制了一些数据,并且sk_err有值,则直接跳出循环。
		if (copied) {
			if (sk->sk_err ||
			    sk->sk_state == TCP_CLOSE ||
			    (sk->sk_shutdown & RCV_SHUTDOWN) ||!timeo ||signal_pending(current))
				break;
		} else {
			if (sock_flag(sk, SOCK_DONE))
				break;
///如果没有值,可以看到cpoied直接被设置为错误号。
			if (sk->sk_err) {
				copied = sock_error(sk);
				break;
			}
///到这里copied为0
			if (sk->sk_shutdown & RCV_SHUTDOWN)
				break;


这个函数最终的返回值是copied,也就是说当缓冲区有数据,我们的返回值为拷贝完的数据,而没有数据的话,直接会返回错误号。也就是说是ECONNREFUSED,EPIPE或者ECONNRESET。而当再次调用,还是没有数据的话,会直接返回0.

最后我们来看tcp_poll的实现,这个函数也就是当sock的等待队列有事件触发式会被调用的。有关slect和epoll的源码分析可以看这个:

http://docs.google.com/Doc?docid=0AZr7tK22PNlAZGRqdHZ4NHFfMTQ0Zmh0OTIzZzQ&hl=en

http://docs.google.com/Doc?docid=0AZr7tK22PNlAZGRqdHZ4NHFfMTQ2ZnIzMmtwaHM&hl=en

ok,我们来看tcp_poll.它的功能很简单,就是得到触发的事件掩码,然后返回给
select,poll或者epoll。

我们来看代码。这里要知道shutdown域,这个域主要是通过shutdown来设置。不过其他地方偶尔也会设置,比如收到fin(见上面的tcp_fin).

unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
{
	unsigned int mask;
	struct sock *sk = sock->sk;
	struct tcp_sock *tp = tcp_sk(sk);

	sock_poll_wait(file, sk->sk_sleep, wait);
///如果是tcp_listen的话,单独处理。
	if (sk->sk_state == TCP_LISTEN)
		return inet_csk_listen_poll(sk);

	mask = 0;
//如果sk_err有设置则添加POLLERR事件。
	if (sk->sk_err)
		mask = POLLERR;


//如果shutdown被设置,或者tcp状态为close,则添加POLLHUP
	if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE)
		mask |= POLLHUP;
///如果shutdown被设置为RCV_SHUTDOWN则添加 POLLIN | POLLRDNORM | POLLRDHUP状态(我们通过上面知道,当接收到fin后就会设置为RCV_SHUTDOWN)。
	if (sk->sk_shutdown & RCV_SHUTDOWN)
		mask |= POLLIN | POLLRDNORM | POLLRDHUP;

///如果不是TCPF_SYN_SENT以及TCPF_SYN_RECV状态。
	if ((1 << sk->sk_state) & ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) {
		int target = sock_rcvlowat(sk, 0, INT_MAX);

		if (tp->urg_seq == tp->copied_seq &&
		    !sock_flag(sk, SOCK_URGINLINE) &&
		    tp->urg_data)
			target--;


		if (tp->rcv_nxt - tp->copied_seq >= target)
			mask |= POLLIN | POLLRDNORM;
///如果没有设置send_shutdown.则进入下面的处理。
		if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {

///如果可用空间大于最小的buf,则添加POLLOUT | POLLWRNORM事件。
			if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk)) {
				mask |= POLLOUT | POLLWRNORM;
			} else {  /* send SIGIO later */

//设置flag的标记位
		set_bit(SOCK_ASYNC_NOSPACE,		&sk->sk_socket->flags);
		set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

//再次判断,因为有可能此时又有空间了。
	if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
					mask |= POLLOUT | POLLWRNORM;
			}
		}
//如果有紧急(urgent)数据,添加POLLPRI事件。
		if (tp->urg_data & TCP_URG_VALID)
			mask |= POLLPRI;
	}
	return mask;
}















0
1
分享到:
评论

相关推荐

    ZLIP TCP/IP协议栈在嵌入式系统中的应用

    ZLIP协议栈是一种极小的TCP/IP协议栈,该协议栈是免费的开源协议栈,可以适用于8位或16位的微处理器搭建的嵌入式系统。本文介绍了将ZLIP结合嵌入式平台,实现将嵌入式系统作为客户端接入网络中的应用,对ZLIP协议栈...

    Linux 蓝牙协议栈的USB+设备驱动

    摘 要:基于对Linux 下蓝牙协议栈BlueZ 源代码的分析,给出BlueZ的组织结构和特点。分析蓝牙USB 传输驱动机制和数据处理过程, 给出实现蓝牙设备驱动的重要数据结构和流程,并总结Linux 下开发蓝牙USB 设备驱动的...

    通信与网络中的嵌入式系统TCP/IP 协议栈的定制研究

    本文在对标准TCP/IP 协议栈的组成结构分析基础上,针对简单文件传输的需要,选择ARP、IP、UDP、TFTP 四个基本协议进行简化定制,提出了一种新的嵌入式TCP/IP协议栈。文中阐述了对各协议进行优化精简的原理和方法,给出了...

    轻量级协议栈LWIP的分析与改进

    为了适应嵌入式应用的要求,提高轻量级协议栈(light weight internet protocol,LWIP) 的响应速度和实时性,提出了两 种优化方法。第一种优化方法是采用混合 TCP-UDP 协议,对少量数据的传输采用 UDP 协议,而对大量...

    论文研究-嵌入式精简TCP/IPV6协议栈的设计与实现 .pdf

    嵌入式精简TCP/IPV6协议栈的设计与实现,成继中,赵欢,本文通过对常用IPV6技术的研究和分析,针对嵌入设备处理速度慢,存储容量小等特点,对常用TCP/IPV6协议栈进行了裁减和简化,裁减掉一

    嵌入式系统/ARM技术中的基于TMS320DM642的嵌入式TCP/I P协议栈的实现

    嵌入式视频通信系统由视频数据...1 TCP/IP协议栈分析嵌入式TCP/IP协议栈具有TCP/IP协议栈的基本功能,它运行于以太网环境下,其软件模型如图1所示。 最底层是物理层,定义了以太网控制器的工作方式,实现以

    uIP TCP/IP协议栈在51系列单片机上的应用

    摘要:uIP协议栈是一种免费的可实现的极小的TCP/IP协议栈,可以使用于由8位或16位微处理器构建的嵌入式系统。本文分析了uIP协议栈的结构和应用接口,并讨论了如何将其应用到51系列单片机上。关键词:TCP/IP单片机...

    基于精简TCP/IP协议栈的信息家电网络服务器

    摘要:通过分析和实验,研究嵌入式TCP/IP协议栈S1C33-Stack的结构及运行原理,给出以S1C33-Stack构造单芯片嵌入式网络服务器,将信息家电组网及接入Internet的实现方案。研究单芯片嵌入式网络服务器中S1C33-Stack与...

    通信与网络中的由网络协议栈芯片W3100A构成的TCP/IP处理平台

    关键词:TCP/IP AT91M40800 网络协议栈芯片 W3100A引言越来越多的人已经认知到后PC时代的到来,越来越多的嵌入式产品走到了现实应用的前台。嵌入式产品从出生到繁荣一直秉承着与应用紧耦合这个宗旨。过去它们基本上...

    消费电子中的基于精简TCP/IP协议栈的信息家电网络服务器

    摘要:通过分析和实验,研究嵌入式TCP/IP协议栈S1C33-Stack的结构及运行原理,给出以S1C33-Stack构造单芯片嵌入式网络服务器,将信息家电组网及接入Internet的实现方案。研究单芯片嵌入式网络服务器中S1C33-Stack与...

    LwIP源代码详解

    lwip是瑞典计算机科学院的一个开源的TCP/IP协议栈实现.lwIP是TCP/IP协议栈的一个实现。lwIP协议栈主要关注的是怎么样减少内存的使用和代码的大小,这样就可以让lwIP适用于资源有限的小型平台例如嵌入式系统。为了...

    基于ARM核的嵌入式TCP/IP协议栈简化实现 (2006年)

    结合具体的工程实例,通过分析嵌入式TCP/IP协议栈的设计原理,研究并简化了嵌入式TCP/IP协议栈的通信模型、体系结构和实现方案。开发出基于SAMSUNG S3C44B0X微处理器和RTL8019AS以太网物理层接口的低功耗网络控制器...

    Dos+ddos+网络攻击原理及其实现

    有的协议栈基于这样的假设,没有针对不设置任何标志的TCP报文的处理过程,因此,这样的协议栈如果收到了这样的报文,可能会崩溃。攻击者利用了这个特点,对目标计算机进行攻击。 1.8 设置了FIN标志却没有设置ACK...

    TCP.IP路由技术[第二卷](CCIE职业发展系列).pdf

    8.6.1 双协议栈 527 8.6.2 DNS 527 8.6.3 IPv4中的IPv6隧道 528 8.6.4 网络地址翻译-协议翻译 530 8.7 尾注 530 8.8 展望 530 8.9 推荐书目 531 8.10 复习问题 531 8.11 参考文献 533 第9章 路由器管理 535 9.1 ...

    单片机与DSP中的基于DSP的嵌入式TCP/IP协议的研究和实现

    本文结合电力自动化系统实际详细分析了嵌入式TCP/IP协议的选取原则,采用DSP芯片和网卡接口控制芯片设计了以太网接口,介绍了DSP芯片对网卡接口控制芯片的控制过程和TCP/IP协议栈处理数据包的流程,完成了嵌入式TCP/...

    嵌入式系统/ARM技术中的基于DSP的嵌入式TCP/IP协议的研究与实现

    本文结合电力自动化系统实际详细分析了嵌入式TCP/IP协议的选取原则,采用DSP芯片和网卡接口控制芯片设计了以太网接口,介绍了DSP芯片对网卡接口控制芯片的控制过程和TCP/IP协议栈处理数据包的流程,完成了嵌入式TCP/...

    ZigBee CC2530、CC2531实现木材监测系统(基于ZStack)【支持CC253X系列】

    本系统基于CC2530开发板,以Z-stack作为协议栈,利用IAR开发环境,将采集到的温度、湿度、烟雾、火灾等信息通过Zigbee协议栈,结合TCP/IP协议传送到上位机,该系统采用数字化数据采集,模块化处理,便于系统维护以及...

    网络故障排查指南以及部分工具

    3、协议栈 (1)、邻居子系统,arp (2)、ip层,地址,路由,防火墙,分片,mtu (3)、tcp:time外套,队列,syn攻击,超时,乱序,内存不足 (4)、UDP (5)、socket设置 分析工具: (1)、排障工具:tcpdump,...

    STM32嵌入式系统开发实战指南 FreeRTOS与LwIP联合移植Part1[机械工业出版社]

    9.6 TCP协议 9.7 UDP协议 9.8 FTP协议 第10章 LwIP轻量级TCP/IP协议栈 10.1 LwIP进程模型 10.2 LwIP缓冲与内存管理 10.2.1 LwIP动态内存管理机制 10.2.2 LwIP的缓冲管理机制 10.3 LwIP网络接口 10.4 LwIP的ARP处理 ...

Global site tag (gtag.js) - Google Analytics