深入了解LinuxTCP的例子(linuxtcp例子)

TCP(Tranission Control Protocol)是一种网络传输层协议,主要用于保证数据在网络中可靠传输。在 Linux 操作系统中,TCP 协议是非常重要且广泛使用的协议,因此深入了解 Linux TCP 将有助于我们更好地理解 Linux 网络编程和网络优化。

我们提供的服务有:成都网站建设、网站制作、微信公众号开发、网站优化、网站认证、汉阳ssl等。为近1000家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的汉阳网站制作公司

本文将介绍一些常用的 Linux TCP 相关命令和工具,并通过一个 TCP 客户端和服务器的例子,深入了解 TCP 的基本流程和一些常见的 TCP 问题。

1. 常用 Linux TCP 命令和工具

1.1 netstat

netstat 命令是一个非常常用的网络命令,可以用于显示各种网络信息。在查看 TCP 相关信息时,我们可以使用以下命令来查看当前连接的状态信息:

“`bash

netstat -ant | grep ESTABLISHED

“`

该命令将显示当前所有已建立的连接信息,包括本地 IP 和端口号、远程 IP 和端口号、TCP 的状态等信息。

1.2 tcpdump

tcpdump 是一个用于捕获网络数据包的命令行工具。通过 tcpdump,我们可以详细查看网络通信中的各种细节信息,包括TCP包的头部信息、数据长度、序列号、确认号等等。例如,我们可以使用以下命令来捕获指定端口的数据包:

“`bash

sudo tcpdump -n -i eth0 port 8080

“`

该命令将捕获所有进入或离开 eth0 接口上的 8080 端口的 TCP 数据包,并输出详细信息。

1.3 tcptrace

tcptrace 是一个用于分析和生成 TCP 连接数据包的工具。通过 tcptrace,我们可以详细了解 TCP 连接的生命周期,了解各个阶段的数据传输情况、时延、丢包等信息。例如,我们可以使用以下命令来生成 TCP 连接的日志文件:

“`bash

sudo tcpdump -n -s0 -w tcpdump.log port 8080

sudo tcptrace -xS tcpdump.log

“`

该命令将捕获本地 8080 端口的所有 TCP 数据包,并将其转储到 tcpdump.log 文件中。然后我们可以使用 tcptrace 命令来分析生成数据文件。

2. TCP 客户端和服务器的例子

在 Linux 中,我们可以使用各种编程语言来编写 TCP 客户端和服务器程序。下面,我们将使用 C 语言来编写一个简单的 TCP 客户端和服务器程序,并通过这个例子来深入了解 TCP 的基本流程和一些常见的 TCP 问题。

2.1 TCP 服务器

下面是一个简单的 TCP 服务器程序的示例代码:

“`c

#include

#include

#include

#include

#include

#include

#include

#define PORT 8080

#define BACKLOG 10

int mn()

{

int sockfd, new_fd;

struct sockaddr_in my_addr;

struct sockaddr_in their_addr;

size_t sin_size;

char buf[1024];

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror(“socket”);

exit(1);

}

my_addr.sin_family = AF_INET;

my_addr.sin_port = htons(PORT);

my_addr.sin_addr.s_addr = INADDR_ANY;

bzero(&(my_addr.sin_zero), 8);

if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1)

{

perror(“bind”);

exit(1);

}

if (listen(sockfd, BACKLOG) == -1)

{

perror(“listen”);

exit(1);

}

printf(“server started listening on %d…\n”, PORT);

while (1)

{

if ((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, (socklen_t *)&sin_size)) == -1)

{

perror(“accept”);

continue;

}

printf(“server got connection from %s\n”, inet_ntoa(their_addr.sin_addr));

send(new_fd, “Hello, world!”, 13, 0);

close(new_fd);

}

close(sockfd);

return 0;

}

“`

上述代码中,我们首先通过 socket 函数创建了一个套接字,然后通过 bind 函数将该套接字与本地的 IP 地址和端口号绑定。接着,我们调用 listen 函数开启服务,准备接受来自 TCP 客户端的连接请求。在主循环中,我们通过 accept 函数等待客户端的连接请求,一旦接收到连接请求,就使用 send 函数向客户端发送欢迎信息,并使用 close 函数关闭连接。

2.2 TCP 客户端

下面是一个简单的 TCP 客户端程序的示例代码:

“`c

#include

#include

#include

#include

#include

#include

#include

#define PORT 8080

#define SERVER_ADDR “127.0.0.1”

int mn()

{

int sockfd;

struct sockaddr_in server_addr;

char buf[1024];

if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror(“socket”);

exit(1);

}

server_addr.sin_family = AF_INET;

server_addr.sin_port = htons(PORT);

server_addr.sin_addr.s_addr = inet_addr(SERVER_ADDR);

bzero(&(server_addr.sin_zero), 8);

if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)

{

perror(“connect”);

exit(1);

}

recv(sockfd, buf, 1024, 0);

printf(“client received: %s\n”, buf);

close(sockfd);

return 0;

}

“`

上述代码中,我们同样首先通过 socket 函数创建了一个套接字,然后通过 connect 函数连接到远程服务器的 IP 地址和端口号。在连接成功后,我们通过 recv 函数等待服务器的响应,并将其输出到控制台。我们通过 close 函数关闭连接。

3.

相关问题拓展阅读:

  • TCP那些事儿
  • Linux TCP/IP协议栈数据包处理流程及代码实现分析

TCP那些事儿

目录:

以前毕盯洞我也认为TCP是相当底层的东西,我永远不需要去了解它。虽然差不多是这样,但是实际生活中,你依然可能遇见和TCP算法相关的bug,这时候懂一些TCP的知识就至关重要了。(

本文也可以引申为,系统调用,操作系统这些都很重要,这个道理适用于很多东西

这里推荐一篇小短文, 人人都应该懂点TCP

使用TCP协议通信的双方必须先建立TCP连接,并在内核中为该连接维持一些必要的数据结构,比如连接的状态、读写缓冲区、定时器等。当通信结束时,双方必须关闭连接以释放这些内核数据。TCP服务基于流,源源不断从一端流向另一端,发送端可以逐字节写入,接收端可以逐字节读出,无需分段。

需要注意的几点:

TCP状态(11种):

eg.

以上为TCP三次握手的状态变迁

以下为TCP四次挥手的状态变迁

服务器通过 listen 系统调用进入

LISTEN

状态,被动等待客户端连接,也就是所谓的被动打开。一旦监听到SYN(同步报文段)请求,就将该连接放入内核的等待队列,并向客户端发送带SYN的ACK(确认报文段),此时该连接处于

SYN_RECVD

状态。如果服务器收到客户端返回的ACK,则转到

ESTABLISHED

状态。这个状态就是连接双方能进行全双工数据传输的状态。

而当客户端主动关闭连接时,服务器收到FIN报文,通过返回ACK使连接进入

CLOSE_WAIT

状态。此状态表示——等待服务器应用程序关闭连接。通常,服务器检测到客户端关闭连接之后,也会立即给客户端发送一个FIN来关闭连接,使连接转移到

LAST_ACK

状态,等待客户端对最后一个FIN结束报文段的最后一次确认,一旦确认完成,连接就彻底关闭了。

客户端通过 connect 系统调用主动与服务器建立连接。此系统调用会首先给服务器发一个SYN,使连接进入

SYN_SENT

状态。

connect 调用可能因为两种原因失败:1. 目标端口不存在(未被任何进程监听)护着该端口被

TIME_WAIT

状态的连接占用( 详见后文 )。2. 连接超时,在超时时间内未收到服务器的ACK。

如果 connect 调用失败,则连接返回初始的

CLOSED

状态,如果调用成功,则转到

ESTABLISHED

状态。

客户端执行主动关闭时,它会向服务器发送一个FIN,连接进入

TIME_WAIT_1

状态,如果收到服务器的ACK,进入

TIME_WAIT_2

状态。此时服务器处于

CLOSE_WAIT

状态,这一对状态是可能发生办关闭的状态(详见后文)。此时如果服务器发送FIN关闭连接,则客户端会发送ACK进行确认并进入

TIME_WAIT

状态。

流量控制是为了控制发送方发送速率,保证接收方来得及接收。

接收方发送的确认报文中的窗口字段可以用来控制发送方窗口大小,从而影响发送方的发送速率。将窗口字段设置为 0,则发送方不能发送数据。

如果网络出现拥塞,分组将会丢失,此时发送方会继续重传手枯,从而导致网络拥塞程度更高。因此当出现拥塞时,应当控制发送方的速率。这一点和流量控制很像,但是出发点不同。

流量控制是为了让接收方能来得及接收,而拥塞控制是为了降低整个网络的拥塞程度。

TCP 主要通过四种算法来进行拥塞控制:

慢开始、拥塞避免、快重传、快恢复。

在Linux下有多种则埋实现,比如reno算法,vegas算法和cubic算法等。

发送方需要维护一个叫做拥塞窗口(cwnd)的状态变量,注意拥塞窗口与发送方窗口的区别:拥塞窗口只是一个状态变量,实际决定发送方能发送多少数据的是发送方窗口。

为了便于讨论,做如下假设:

发送的最初执行慢开始,令 cwnd=1,发送方只能发送 1 个报文段;当收到确认后,将 cwnd 加倍,因此之后发送方能够发送的报文段数量为:2、4、8 …

注意到慢开始每个轮次都将 cwnd 加倍,这样会让 cwnd 增长速度非常快,从而使得发送方发送的速度增长速度过快,网络拥塞的可能也就更高。设置一个慢开始门限 ssthresh,当 cwnd >= ssthresh 时,进入拥塞避免,每个轮次只将 cwnd 加 1。

如果出现了超时,则令 ssthresh = cwnd/2,然后重新执行慢开始。

在接收方,要求每次接收到报文段都应该对最后一个已收到的有序报文段进行确认。例如已经接收到 M1 和 M2,此时收到 M4,应当发送对 M2 的确认。

在发送方,如果收到三个重复确认,那么可以知道下一个报文段丢失,此时执行快重传,立即重传下一个报文段。例如收到三个 M2,则 M3 丢失,立即重传 M3。

在这种情况下,只是丢失个别报文段,而不是网络拥塞。因此执行快恢复,令 ssthresh = cwnd/2 ,cwnd = ssthresh,注意到此时直接进入拥塞避免。

慢开始和快恢复的快慢指的是 cwnd 的设定值,而不是 cwnd 的增长速率。慢开始 cwnd 设定为 1,而快恢复 cwnd 设定为 ssthresh。

  发送端的每个TCP报文都必须得到接收方的应答,才算传输成功。

  TCP为每个TCP报文段都维护一个重传定时器。

  发送端在发出一个TCP报文段之后就启动定时器,如果在定时时间类未收到应答,它就将重发该报文段并重置定时器。

  因为TCP报文段最终在网络层是以IP数据报的形式发送,而IP数据报到达接收端可能是乱序或者重复的。TCP协议会对收到的TCP报文进行重排、整理,确保顺序正确。

TCP报文段所携带的应用程序数据按照长度分为两种:

交互数据

成块数据

对于什么是粘包、拆包问题,我想先举两个简单的应用场景:

对于之一种情况,服务端的处理流程可以是这样的:当客户端与服务端的连接建立成功之后,服务端不断读取客户端发送过来的数据,当客户端与服务端连接断开之后,服务端知道已经读完了一条消息,然后进行解码和后续处理…。对于第二种情况,如果按照上面相同的处理逻辑来处理,那就有问题了,我们来看看

第二种情况

下客户端发送的两条消息递交到服务端有可能出现的情况:

之一种情况:

服务端一共读到两个数据包,之一个包包含客户端发出的之一条消息的完整信息,第二个包包含客户端发出的第二条消息,那这种情况比较好处理,服务器只需要简单的从网络缓冲区去读就好了,之一次读到之一条消息的完整信息,消费完再从网络缓冲区将第二条完整消息读出来消费。

第二种情况:

服务端一共就读到一个数据包,这个数据包包含客户端发出的两条消息的完整信息,这个时候基于之前逻辑实现的服务端就蒙了,因为服务端不知道之一条消息从哪儿结束和第二条消息从哪儿开始,这种情况其实是发生了TCP粘包。

第三种情况:

服务端一共收到了两个数据包,之一个数据包只包含了之一条消息的一部分,之一条消息的后半部分和第二条消息都在第二个数据包中,或者是之一个数据包包含了之一条消息的完整信息和第二条消息的一部分信息,第二个数据包包含了第二条消息的剩下部分,这种情况其实是发送了TCP拆,因为发生了一条消息被拆分在两个包里面发送了,同样上面的服务器逻辑对于这种情况是不好处理的。

我们知道tcp是以流动的方式传输数据,传输的最小单位为一个报文段(segment)。tcp Header中有个Options标识位,常见的标识为mss(Maximum Segment Size)指的是,连接层每次传输的数据有个更大限制MTU(Maximum Tranission Unit),一般是1500比特,超过这个量要分成多个报文段,mss则是这个更大限制减去TCP的header,光是要传输的数据的大小,一般为1460比特。换算成字节,也就是180多字节。

tcp为提高性能,发送端会将需要发送的数据发送到缓冲区,等待缓冲区满了之后,再将缓冲中的数据发送到接收方。同理,接收方也有缓冲区这样的机制,来接收数据。

发生TCP粘包、拆包主要是由于下面一些原因:

既然知道了tcp是无界的数据流,且协议本身无法避免粘包,拆包的发生,那我们只能在应用层数据协议上,加以控制。通常在制定传输数据时,可以使用如下方法:

写了一个简单的 golang 版的tcp服务器实例,仅供参考:

例子

参考和推荐阅读书目:

注释:

eg.

Linux TCP/IP协议栈数据包处理流程及代码实现分析

好吧,我来回答吧,首先是网卡驱动程序捕银衡获到数据包,做检验无误后,和DMA以及CPU交互,然后由DMA和驱动程序创建BD表,然后分配skbuf(LINUX下)数据结构保存获得的数据帧,内核通过协议栈处理这个skbuf,通常是层层剥离每个层的首部,然后传到上一层,细节就是一个变量做偏移量,每次做一个首部偏移读取租搏键首部数据,识别本层协议类型以及下一层协议类型,具体过程就是这个网络原理的过程,请参考《TCP/IP详解卷一》《linux设备驱动程序》《understanding linux network internals》弊巧《Unix网络编程卷一》等。

太高声了

关于linux tcp例子的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站。

创新互联服务器托管拥有成都T3+级标准机房资源,具备完善的安防设施、三线及BGP网络接入带宽达10T,机柜接入千兆交换机,能够有效保证服务器托管业务安全、可靠、稳定、高效运行;创新互联专注于成都服务器托管租用十余年,得到成都等地区行业客户的一致认可。

网站栏目:深入了解LinuxTCP的例子(linuxtcp例子)
浏览地址:http://www.hantingmc.com/qtweb/news25/245225.html

网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联