TCP/IP五层模型消息解/封装仿真

来源:互联网 发布:matlab cell数组 拼接 编辑:程序博客网 时间:2024/06/04 18:44

来自我的博客Minecode.link

消息封/解装仿真

功能要求

按照TCP五层模型仿真消息在两台主机之间的通信过程。
1. 在发送端模拟数据从高层到低层的封装过程,在接收端模拟数据从低层到高层的解封装过程。
2. 按照每层的功能对数据填加报头,并显示每一层得到的封/解装格式。
3. 传输层和网络层的封装格式参考TCP/IP的相应各层协议格式。
4. 网络层的IP报文需要模拟报文分段和重组的过程。
5. 数据链路层帧格式参考局域网的MAC帧格式。
6. 物理层显示为0或1比特串。

五层模型


Socket编程简介

Socket是网络文件描述符。在基于Socket的编程技术中,用户不直接访问发送和接收包的网络接口设备,而是建立一个中间文件描述符来处理编程接口到网络的操作。简单来说,Socket就是我们常说的“套接字”
本段只介绍了本实验需要设计的知识,更多Socket用法可Google一下。

Socket包含的内容

  • 一个特殊的通信域,比如一个网络连接
  • 一个特殊的通信类型,比如流或者数据报
  • 一个特殊的协议,比如TCP或者UDP

其中,可以实现面向连接和无连接的Socket

面向连接的Socket

面向连接的Socket

面向无连接的Socket

面向无连接的Socket

模拟TCP

语言 Objective-C + C 平台 Mac OSX 工具 XCode (LLVM)

最终效果

Showcase

运行逻辑

本实验模拟了TCP五层模型中的消息解/封装仿真,建立在现有网络的基础上,使用Socket进行通信,使用了面向连接的Socket。除了两台机器相互通信之外,我们也可以将服务端绑定到网卡端口,使用客户端与服务端在本机相互通信。

由服务端绑定端口并侦听客户端消息。而后客户端连接服务端,并相互发送消息。

服务端(Server)

首先,服务器绑定端口,并侦听客户端连接请求,当客户端连接后进行消息侦听和发送。

- (void)bindSocketWithPort:(NSInteger)port {    // 创建Socket地址    struct sockaddr_in server_addr;                             // socket地址    server_addr.sin_len = sizeof(struct sockaddr_in);           // 设置地址结构体大小    server_addr.sin_family = AF_INET;                           // AF_INET地址簇    server_addr.sin_port = htons((short)port);                  // 设置端口    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);            // 服务器地址    // 创建Socket    int server_socket = socket(AF_INET, SOCK_STREAM, 0);        // 创建Socket    if (server_socket == -1) {        [self showMessageWithMsg:@"创建Socket失败"];        return;    }    int reuse = 1;    int sockOpt = setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));    if (sockOpt == -1) {        [self showMessageWithMsg:@"重设Socket失败"];        return;    }    // 绑定Socket    // 将创建的Socket绑定到本地IP和端口,用于侦听客户端请求    int bind_result = bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr));    if (bind_result == -1) {        [self showMessageWithMsg:@"绑定Socket失败"];        return;    }    // 侦听客户端消息    if (listen(server_socket, 5) == -1) {        [self showMessageWithMsg:@"开启侦听失败"];        return;    }    // 获取客户端端口信息    struct sockaddr_in client_address;    socklen_t address_len;    int client_socket = accept(server_socket, (struct sockaddr *)&client_address, &address_len);    if (client_socket == -1) {        [self showMessageWithMsg:@"客户端握手失败"];        return;    }    char recv_msg[RECV_BUFFER_SIZE];    char reply_msg[REPLY_BUFFER_SIZE];    while (YES) {        bzero(recv_msg, RECV_BUFFER_SIZE);        bzero(reply_msg, REPLY_BUFFER_SIZE);        long byteLen = recv(client_socket, recv_msg, RECV_BUFFER_SIZE, 0);        recv_msg[byteLen] = '\0';                   // 添加消息结尾        NSMutableString *msgStr = [NSMutableString stringWithFormat:@"%s", recv_msg];        [self clearCurrentMsg];        strcpy(recv_msg, [[self reciveFromClient:msgStr] UTF8String]);        if (strcmp(recv_msg, "") != 0) {            strcpy(reply_msg, "服务端消息:收到");            strcat(reply_msg, recv_msg);            send(client_socket, reply_msg, REPLY_BUFFER_SIZE, 0);        }    }}

客户端(Client)

首先,客户端要和服务端建立连接。调用socket的方法顺序为:
socket() -> connect()

- (void)bindSocketWithIP:(NSString *)ipStr andPort:(NSInteger)port {    // 创建Socket地址    struct sockaddr_in server_addr;                                      // 创建Socket地址    server_addr.sin_len = sizeof(struct sockaddr_in);                    // 设置结构体长度    server_addr.sin_family = AF_INET;                                    // AF_INET地址簇    server_addr.sin_port = htons((short)port);                           // 设置端口    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);                     // 服务器地址    // 创建Socket    int server_socket = socket(AF_INET, SOCK_STREAM, 0);    if (server_socket == -1) {        [self showMessageWithMsg:@"创建Socket失败"];        return;    }    else {        // 保存Socket        self.server_socket = server_socket;        // 保存port        self.port_num = (short)port;        [self showMessageWithMsg:@"创建Socket成功"];        [NSThread sleepForTimeInterval:0.3];    }    int connect_result = connect(server_socket, (struct sockaddr *)&server_addr, sizeof(struct sockaddr_in));    if (connect_result == -1) {        [self showMessageWithMsg:@"连接主机失败"];        return;    }    else {        [self showMessageWithMsg:@"连接主机成功,等待发送消息..."];        [NSThread sleepForTimeInterval:0.3];    }    // 连接成功后的操作    [self didConnected];}

连接成功后,双方互相发送和接收消息
send() -> recv()

- (void)sendMsgAction {    NSMutableString *msg = [NSMutableString stringWithFormat:@"%@",self.msgField.stringValue];    char recv_msg[RECV_BUFFER_SIZE];    char send_msg[REPLY_BUFFER_SIZE];    // 发送消息,并接收服务器回信    bzero(recv_msg, RECV_BUFFER_SIZE);    bzero(send_msg, REPLY_BUFFER_SIZE);    // 向服务端通过socket发消息    NSMutableString *resStr = [self appLayerWithString:msg];    strcpy(send_msg, resStr.UTF8String);    long send_result = send(self.server_socket, send_msg, REPLY_BUFFER_SIZE, 0);    if (send_result == -1) {        [self showMessageWithMsg:@"消息发送失败"];        return;    }    else {        [self showMessageWithMsg:@"消息发送成功"];        [NSThread sleepForTimeInterval:0.3];    }    // 接收服务端消息    long recv_result = recv(self.server_socket, recv_msg, RECV_BUFFER_SIZE, 0);    [self reciveFromServer:[NSString stringWithUTF8String:recv_msg]];}

报头编码

各层在传递给下一层之前,要对数据进行封装,增加对应的报头。具体代码如下:

#pragma mark - 五层传输协议// 模拟网络层对数据的包装- (NSMutableString *)appLayerWithString:(NSMutableString *)str {    NSMutableString *resStr = [NSMutableString stringWithFormat:@"AppHeader#%@", str];    dispatch_async(dispatch_get_main_queue(), ^{        self.appLayer.stringValue = [resStr copy];    });    [NSThread sleepForTimeInterval:0.3];    return [self transferLayerWithString:resStr];}// 模拟传输层对数据的包装- (NSMutableString *)transferLayerWithString:(NSMutableString *)str {    NSMutableString *resStr = [NSMutableString string];    // 添加源端口 16位(0-15)    [resStr appendFormat:@"0000000011111111"];    // 添加目的端口 16位(16-31)    [resStr appendFormat:@"%@", [self intToBinary:self.port_num]];    // 添加序列编号 32位    [resStr appendFormat:@"00000000000000000000000000001011"];    // 添加确认帧 32位    [resStr appendFormat:@"00000000000000000000000011111011"];    // 添加报头长度    [resStr appendFormat:@"0101"];    // 添加保留长度    [resStr appendFormat:@"000000"];    // 添加FLag    [resStr appendFormat:@"000000"];    // 添加窗口大小    [resStr appendFormat:@"0000000000000111"];    // 添加确认值    [resStr appendFormat:@"0101010101010010"];    // 添加UrgentPointer    [resStr appendFormat:@"0000000000001111"];    // 添加Header结尾    [resStr appendFormat:@"#%@", str];    dispatch_async(dispatch_get_main_queue(), ^{        self.transLayer.stringValue = [resStr copy];    });    [NSThread sleepForTimeInterval:0.3];    return [self networkLayerWith:resStr];}// 模拟网络层对数据的包装- (NSMutableString *)networkLayerWith:(NSMutableString *)str {    NSMutableString *resStr = [NSMutableString string];    // 添加VER    [resStr appendFormat:@"0100"];    // 添加HLEN    [resStr appendFormat:@"1111"];    // 添加Service    [resStr appendFormat:@"00000000"];    // 添加totalLength    [resStr appendFormat:@"0101010101010101"];    // 添加Identification    [resStr appendFormat:@"0000000000000000"];    // 添加Flag    [resStr appendFormat:@"000"];    // 添加FragmentationOffset    [resStr appendFormat:@"0000000000000"];    // 添加TTL    [resStr appendFormat:@"00000000"];    // 添加Protocol    [resStr appendFormat:@"00000000"];    // 添加HeaderChecksum    [resStr appendFormat:@"0000000000000000"];    // 添加SourIPAddress    [resStr appendFormat:@"00000000000000000000000000000000"];    // 添加DestinationIPAddress    [resStr appendFormat:@"00000000000000000000000000000000"];    // 添加Header结尾    [resStr appendFormat:@"#%@", str];    dispatch_async(dispatch_get_main_queue(), ^{        self.networkLayer.stringValue = [resStr copy];    });    [NSThread sleepForTimeInterval:0.3];    return [self dlinkLayerWithString:resStr];}// 模拟链路层对数据的包装- (NSMutableString *)dlinkLayerWithString:(NSMutableString *)str {    NSMutableString *resStr1 = [NSMutableString string];    // 添加FrameFlag1    [resStr1 appendFormat:@"00001111"];    // 添加FrameAdd    [resStr1 appendFormat:@"11101011"];    // 添加FrameControl    [resStr1 appendFormat:@"01111000"];    NSMutableString *resStr2 = [NSMutableString string];    // 添加FrameFCS    [resStr2 appendFormat:@"00001111"];    // 添加FrameFlag2    [resStr2 appendFormat:@"11101011"];    // 合成帧    NSMutableString *resStr = [NSMutableString stringWithFormat:@"%@#%@#%@", resStr1, str, resStr2];    dispatch_async(dispatch_get_main_queue(), ^{        self.dlinkLayer.stringValue = [resStr copy];    });    [NSThread sleepForTimeInterval:0.3];    return [self phyLayerWithString:resStr];}// 模拟物理层对数据的包装- (NSMutableString *)phyLayerWithString:(NSMutableString *)str {    NSMutableString *resStr = [NSMutableString stringWithFormat:@"PhysicsHeader#%@", str];    dispatch_async(dispatch_get_main_queue(), ^{        self.phyLayer.stringValue = [resStr copy];    });    [NSThread sleepForTimeInterval:0.3];    return resStr;}

其他细节

1、在定义Socket地址端口时,要注意端口值类型,若使用htons,则需要转换为short类型(16位)。
server_addr.sin_port = htons((short)port);

本文源代码:Github: https://github.com/Minecodecraft/TCP-IP-Model-Simulation
原文地址:Minecode’s Blog: TCP五层模型消息解/封装仿真