深入学习Linux C高级编程——网络编程之以太网(2)
我们将更深入地学习以太网的协议、帧格式和数据传输方式,即发送方将数据发送给所有连接在同一网络上的设备,并通过sendto()和recvfrom()函数分别发送和接收UDP数据包。
在上一篇文章中,我们已经初步了解了以太网的基本概念和工作原理。接下来,我们将更深入地学习以太网的协议、帧格式和数据传输方式,并通过实例演示如何使用Linux C语言进行以太网编程。
1. 以太网协议
在计算机网络中,协议是指通信双方遵循的规则集合。对于以太网而言,其协议分为物理层、数据链路层和网络层三个部分。
物理层:主要负责电气特性、物理连接等底层细节。
数据链路层:主要负责将数据分成帧,并加上头部和尾部信息进行传输。
网络层:主要负责地址分配、路由选择等功能。
其中,在数据链路层中,最重要的就是MAC(Media Access Control)子层。MAC子层控制着各个节点对共享介质的访问方式,保证了多个节点同时访问同一个介质时不会发生冲突或丢失信息等问题。而MAC子层所采用的协议便是CSMA/CD(Carrier Sense Multiple Access with Collision Detection)协议。
CSMA/CD协议采用了一种“先听再说”的方式,即在发送数据前先监听信道,如果信道上有其他节点正在传输,则等待;如果信道上没有其他节点在传输,则开始发送数据。如果发现冲突(两个或多个节点同时开始传输),则立即停止传输并等待一段随机时间后重新发送。
2. 以太网帧格式
以太网帧由以下几部分组成:
Preamble:7字节的前导码,用于同步时钟和告知接收方有数据到来。
Destination MAC Address:目标MAC地址,6字节长度。
Source MAC Address:源MAC地址,6字节长度。
Ether Type:表示上层协议类型的字段(如IP、ARP等),2字节长度。
Data:实际要传输的数据部分,46-1500字节不等。如果该字段小于46字节,则需要填充使其达到最小值。
CRC Checksum:4字节的循环冗余校验码,在接收端用于检查是否存在错误或丢失信息等问题。
3. 以太网数据传输方式
以太网采用的是广播方式进行数据传输,即发送方将数据发送给所有连接在同一网络上的设备,接收方通过MAC地址识别并抓取自己需要的信息。因此,在一个以太网中,所有设备都可以互相通信。
对于局域网而言,其具有以下特点:
1)覆盖面积比较小,一般不超过10公里。
2)传输速度较快,可达到1Gbps或更高。
3)连接数量有限制(多数为1000台左右),因为节点数量增加会导致冲突概率增大、吞吐量降低等问题。
4. 以太网编程实例
下面我们将通过一个简单的实例来演示如何使用Linux C语言进行以太网编程。在该实例中,我们将使用socket库函数创建UDP协议套接字,并通过sendto()和recvfrom()函数分别发送和接收UDP数据包。具体代码如下:
“`
#include
#include
#include
#include
#include
#include
#define DEST_PORT 8080
#define DEST_IP_ADDR “192.168.1.100”
#define LOCAL_PORT 9000
int main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in dest_addr, local_addr;
if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
perror(“socket”);
exit(1);
}
memset(&dest_addr, 0, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(DEST_PORT);
dest_addr.sin_addr.s_addr = inet_addr(DEST_IP_ADDR);
memset(&local_addr, 0, sizeof(local_addr));
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(LOCAL_PORT);
local_addr.sin_adr.s_adr = htonl(INADDR_ANY);
if (bind(sockfd, (struct sockaddr *)&local_address,
sizeof(local_address)) == -1) {
perror(“bind”);
}
char buffer[1024];
int len;
while (fgets(buffer, 1024, stdin)) {
len = sendto(sockfd, buffer, strlen(buffer), 0,
(struct sockaddr *)&dest_address,
sizeof(dest_address));
if (len != strlen(buffer)) {
fprintf(stderr,”sendto error: %sn”, strerror(errno));
break;
}
memset(buffer,’′,sizeof(buffer));
len=recvfrom(sockfd,buff,sizeof(buff),0,NULL,NULL);
if(len==-1){
fprintf(stderr,”recvfrom error: %sn”,strerror(errno));
break;
}
printf(“%s”,buffer);
}
close(sockfd);
return 0;
}
在该代码中,我们首先创建了一个UDP协议套接字,并分别设置了目标IP地址和端口号以及本地IP地址和端口号。然后通过bind()函数将套接字与本地的IP地址和端口号绑定。接着进入循环,使用fgets()函数从标准输入读入数据,并通过sendto()函数发送UDP数据包。最后,通过recvfrom()函数接收远程主机返回的UDP数据包,并将其打印到屏幕上。
5. 总结
本文介绍了以太网的协议、帧格式和数据传输方式,并通过实例演示了如何使用Linux C语言进行以太网编程。相信读者已经对以太网有了更深入的理解和掌握。在今后的网络编程中,我们可以根据需要选择不同的网络协议和套接字类型,从而实现更加灵活、高效地通信。