当前位置: 首页 > article >正文

计算机网络开发(2)TCP\UDP区别、TCP通信框架、服务端客户端通信实例

TCP与UDP区别

  • UDP:用户数据报协议,面向无连接,可以单播,多播,广播, 面向数据报,不可靠
  • TCP:传输控制协议,面向连接的,可靠的,基于字节流,仅支持单播传输

在这里插入图片描述

socket通信

是对网络中不同主机上的应用程序进程之间进行双向通信端点的抽象

字节序

**小端字节序:**低位存在内存的低位
大端字节序:低位存在内存的高位
eg:小端
0x01 02 03 04
01是高位有效字节,04是低位,那么在内存中是这么存的:
0x04 0x03 0x02 0x01

字节序转化内函数

h  - host 主机,主机字节序 
to - 转换成什么 
n  - network 网络字节序 
s  - short unsigned short 
l  - long unsigned int

#include <arpa/inet.h> 
// 转换端口 short型,2个字节
uint16_t htons(uint16_t hostshort); // 主机字节序 => 网络字节序 
uint16_t ntohs(uint16_t netshort); // 主机字节序 => 网络字节序 
// 转IP int型,4个字节
uint32_t htonl(uint32_t hostlong); // 主机字节序 => 网络字节序 
uint32_t ntohl(uint32_t netlong); // 主机字节序 => 网络字节序

ip地址转换------点分十进制<–>网络通信2进制

通常,人们习惯用可读性好的字符串来表示 IP 地址,比如用点分十进制字符串表示 IPv4 地址,以及用十六进制字符串表示 IPv6> 地址。但编程中我们需要先把它们转化为整数(二进制数)方能使用。而记录日志时则相反,我们要把整数表示的 IP 地址转化为可读的字符串

新版:同时适用于IPV4和IPV6

* 字母含义
  * `p`:点分十进制的IP字符串
  * `n`:表示network,网络字节序的整数
* `int inet_pton(int af, const char *src, void *dst); `
  * 使用`man inet_pton`查看帮助
  * 功能:将点分十进制的IP地址字符串,转换成网络字节序的整数
  * 参数
    * `af`:地址族
      * IPV4:`AF_INET`
      * IPV6:`AF_INET6(IPV6)`
    * `src`:需要转换的点分十进制的IP字符串
    * `dst`:转换后的结果保存在这个里面
  * 返回值
    * 1:成功
    * 0:源IP地址有误
    * -1:地址族包含不可用的地址协议
* `const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);`
  * 使用`man inet_ntop`查看帮助
  * 功能:将网络字节序的整数,转换成点分十进制的IP地址字符串
  * 参数
    * `af`:地址族
      * IPV4:`AF_INET`
      * IPV6:`AF_INET6(IPV6)`
    * `src`:要转换的ip的整数的地址
    * `dst`:转换成IP地址字符串保存的地方
    * `size`:第三个参数的大小(数组的大小)
  * 返回值:返回转换后的数据的地址(字符串),和 dst 是一样的

把IP和port打包为一个socket地址

协议族地址族描述
PF_UNIXAF_UNIXUNIX本地域协议族
PF_INETAF_INETTCP/IPV4z协议族
PF_INET6AF_INET6tcp/IPV6协议族
//socket网络通信接口中表示socket地址的是一个结构体,他的地址作为通信用,但一般用下面的改进版本,再转换成这个
#include <bits/socket.h> 
struct sockaddr { 
    sa_family_t sa_family; 
    char sa_data[14]; 
};

typedef unsigned short int sa_family_t;

为了兼容IPV6,新的socketaddr_in

// IPV4
#include <netinet/in.h> 
-----------------------------------------------------------------------
struct sockaddr_in { 
    sa_family_t sin_family; /* __SOCKADDR_COMMON(sin_) */ 
    in_port_t sin_port; /* Port number. */ 
    struct in_addr sin_addr; /* Internet address. */ 
    /* Pad to size of `struct sockaddr'. */ 
    unsigned char sin_zero[sizeof (struct sockaddr) - __SOCKADDR_COMMON_SIZE - sizeof (in_port_t) - sizeof (struct in_addr)]; 
};
-----------------------------------------------------------------------
struct in_addr { 
    in_addr_t s_addr; 
};

// IPV6
struct sockaddr_in6 { 
    sa_family_t sin6_family; 
    in_port_t sin6_port; /* Transport layer port # */ 
    uint32_t sin6_flowinfo; /* IPv6 flow information */ 
    struct in6_addr sin6_addr; /* IPv6 address */ 
    uint32_t sin6_scope_id; /* IPv6 scope-id */ 
};

// 相关定义
typedef unsigned short uint16_t; 
typedef unsigned int uint32_t; 
typedef uint16_t in_port_t; 
typedef uint32_t in_addr_t; 
#define __SOCKADDR_COMMON_SIZE (sizeof (unsigned short int))

TCP通信框架

在这里插入图片描述

实例,客户端和服务端通信

服务端:
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string>

using namespace std;
#define SERVERIP "192.168.226.129"
#define PORT 9999

//服务器端
int main() {
    //创建socket套接字,形成监听fd
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1) {
        perror("socket");
        return -1;
    }
    //形成一个端,把ip和port打包好在新版的sockaddr_in结构体中
    struct sockaddr_in mServ;
    mServ.sin_family = AF_INET;
    mServ.sin_port = htons(PORT);
    //将上面字符串形式的点分十进制IP转化为网络字节序的IP
    inet_pton(AF_INET, SERVERIP, &mServ.sin_addr.s_addr);
    //将上面的打包的端和监听fd绑定
    int ret = bind(lfd, (struct sockaddr*)&mServ, sizeof(mServ));
    if (ret == -1) {
        perror("bind");
        return -1;
    }
    //监听fd开始监听,后面的值代表未连接和连接的数
    ret = listen(lfd, 128);
    if (ret == -1) {
        perror("listen");
        return -1;
    }
    cout << "服务器启动成功,等待连接...." << endl;

    //接受连接,返回一个新的真正用于交换数据的fd
    struct sockaddr_in client;
    socklen_t client_addr_len = sizeof(client);
    int cfd = accept(lfd, (struct sockaddr*)&client, &client_addr_len);
    if (cfd == -1) {
        perror("accept");
        return -1;
    }
    //输出客户端的ip和端口
    char clinet_ip[16];
    inet_ntop(AF_INET, &client.sin_addr.s_addr, clinet_ip, sizeof(clinet_ip));//将n->p的ip写到clinet_ip中
    int clinet_port = ntohs(client.sin_port);
    cout << "连接成功!客户端IP:" << clinet_ip << " 端口:" << clinet_port << endl;

    //读取客户端的数据
    char buf[1024];
    char writer[1024];
    while(1) {
        int read_len = read(cfd, buf, sizeof(buf));
        if (read_len == -1) {
            perror("read");
            return -1;
        }else if (read_len == 0) {
            cout << "客户端断开连接!" << endl;
            break;
        }else if (read_len >0) cout << "客户端发来的数据:" << buf << endl;

        //向客户端发送数据
        cout << "请输入要发送的数据:";
        cin >> writer;
        write(cfd, writer, sizeof(writer));
    }   
    close(cfd);
    close(lfd);
    return 0;
}
客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <iostream>
#include <string>

using namespace std;
#define SERVERIP "192.168.226.129"
#define PORT 9999

//客户端
int main() {
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("client_socket");
        return -1;
    }
    //打包client的IP和PORT信息
    struct sockaddr_in clit;
    clit.sin_family = AF_INET;
    clit.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVERIP, &clit.sin_addr.s_addr);

    //客户端开始连接服务器
    int ret = connect(fd, (struct sockaddr*)&clit, sizeof(clit));
    if (ret == -1) {
        perror("client connect");
        return -1;
    }
    cout << "客户端开始连接...." << endl;

    //开始通信
    char writer[1024];
    char buf[1024];
    while(1) {
        cout << "请输入发给服务器的内容:";
        cin >> writer;
        ret = write(fd, writer, sizeof(writer));
        if (ret == -1) {
            perror("client write");
            return -1;
        }
        ret = read(fd, buf, sizeof(buf));
        if (ret == -1) {
            perror("client read");
            return -1;
        } else if (ret == 0) {
            cout << "服务器断开连接" << endl;
            break;
        } else  cout << "受到客户端返回信息:" << buf << endl;
    }
    close(fd);
    return 0;
}

http://www.kler.cn/a/573914.html

相关文章:

  • 每天一个Flutter开发小项目 (13) : 本地数据随心存取 - 构建SQLite ToDo 应用,掌握Flutter本地数据库操作
  • 算法·搜索
  • 《张一鸣,创业心路与算法思维》
  • 汽车AI识别及处理解决方案,引领未来驾驶新风尚
  • MybatisPlus从入门到精通
  • Linux基础 IO 和文件
  • Git安装与配置
  • 游戏树搜索与优化策略:Alpha-Beta剪枝及其实例分析
  • AI自动化应用的影响
  • IDC权威认证!永洪科技入选 IDC「GBI图谱」,点亮生成式 BI 价值灯塔
  • Redis的CPU高达90%时如何处理
  • STM32G030F6P6详细解读
  • upload-labs靶场 1-21通关
  • Go 语言中 panic 和 recover 的代价:性能与设计的权衡
  • css的毛玻璃效果backdrop-filter: blur()
  • 力扣hot100刷题——栈
  • 【C++设计模式】第七篇:桥接模式(Bridge)
  • Windows 10/11 系统下 Git 的详细安装步骤和基础设置指南
  • Power Settings Explorer官网下载地址: Windows电源选项设置管理工具
  • OSI七层网络结构和TCP/IP四层结构