IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 231-Linux网络编程TCP -> 正文阅读

[系统运维]231-Linux网络编程TCP

1.主机字节序列和网络字节序列

主机字节序列分为大端字节序和小端字节序,不同的主机采用的字节序列可能不同。大端字节序是指一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。在两台使用不同字节序的主机之间传递数据时,可能会出现冲突。所以,在将数据发送到网络时规定整形数据使用大端字节序,所以也把大端字节序成为网络字节序列。对方接收到数据后,可以根据自己的字节序进行转换

Linux 系统提供如下 4 个函数来完成主机字节序和网络字节序之间的转换:

#include <netinet/in.h>

uint32_t htonl(uint32_t hostlong); // 长整型的主机字节序转网络字节序

uint32_t ntohl(uint32_t netlong); // 长整型的网络字节序转主机字节序

uint16_t htons(uint16_t hostshort); // 短整形的主机字节序转网络字节序

uint16_t ntohs(uint16_t netshort); // 短整型的网络字节序转主机字节序

2.双方要通信得有一个套接字,套接字中有ip地址(哪一台主机)和port(主机上的哪个进程)信息,两个进程就有两对ip地址和两对port,一组ip+port称之为套接字的地址

ipv4和ipv6的地址是不一样的

3.套接字地址结构

①通用 socket 地址结构
socket 网络编程接口中表示 socket 地址的是结构体 sockaddr,其定义如下:

#include <bits/socket.h>
struct sockaddr
{
	sa_family_t sa_family;
	char sa_data[14];
};

sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。常见的协议族和对应的地址族如下图所示:
在这里插入图片描述
②专用 socket 地址结构

TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用 socket 地址结构体,它们分别用于 IPV4 和 IPV6:

/*sin_family: 地址族 AF_INET
sin_port: 端口号,需要用网络字节序表示
sin_addr: IPV4 地址结构:s_addr 以网络字节序表示 IPV4 地址*/
struct in_addr
{
	u_int32_t s_addr;
};
struct sockaddr_in
{
	sa_family_t sin_family;
	u_int16_t sin_port;
	struct in_addr sin_addr;
};
struct in6_addr
{
	unsigned char sa_addr[16]; // IPV6 地址,要用网络字节序表示
};
struct sockaddr_in6
{
	sa_family_t sin6_family; // 地址族:AF_INET6
	u_inet16_t sin6_port; // 端口号:用网络字节序表示
	u_int32_t sin6_flowinfo; // 流信息,应设置为 0
	struct in6_addr sin6_addr; // IPV6 地址结构体
	u_int32_t sin6_scope_id; // scope ID,尚处于试验阶段
};

③IP 地址转换函数

通常,人们习惯用点分十进制字符串表示 IPV4 地址,但编程中我们需要先把它们转化为整数方能使用,下面函数可用于点分十进制字符串表示的 IPV4 地址和网络字节序整数表示的 IPV4 地址之间的转换:

#include <arpa/inet.h>
in_addr_t inet_addr( const char *cp); //字符串表示的 IPV4 地址转化为网络字节序
char* inet_ntoa( struct in_addr in); // IPV4 地址的网络字节序转化为字符串表示

4.网络编程接口

#include <sys/types.h>
#include <sys/socket.h>

/* socket()创建套接字,成功返回套接字的文件描述符,失败返回-1
domain: 设置套接字的协议簇, AF_UNIX AF_INET AF_INET6
type: 设置套接字的服务类型 SOCK_STREAM SOCK_DGRAM
protocol: 一般设置为 0,表示使用默认协议 */
int socket( int domain, int type, int protocol);

/* bind()将 sockfd 与一个 socket 地址绑定,成功返回 0,失败返回-1
sockfd 是网络套接字描述符
addr 是地址结构
addrlen 是 socket 地址的长度 */
int bind( int sockfd, const struct sockaddr *addr, socklen_t addrlen);

/* listen()创建一个监听队列以存储待处理的客户连接,成功返回 0,失败返回-1
sockfd 是被监听的 socket 套接字
backlog 表示处于完全连接状态的 socket 的上限 */
int listen( int sockfd, int backlog);

/* accept()从 listen 监听队列中接收一个连接,成功返回一个新的连接 socket,
该 socket 唯一地标识了被接收的这个连接,失败返回-1
sockfd 是执行过 listen 系统调用的监听 socket
addr 参数用来获取被接受连接的远端 socket 地址
addrlen 指定该 socket 地址的长度 */
int accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen);

/* connect()客户端需要通过此系统调用来主动与服务器建立连接,成功返回 0,失败返回-1
sockfd 参数是由 socket()返回的一个 socket。
serv_addr 是服务器监听的 socket 地址
addrlen 则指定这个地址的长度 */
int connect( int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

/* close()关闭一个连接,实际上就是关闭该连接对应的 socket */
int close( int sockfd);

/* TCP 数据读写:
recv()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大小
send()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长度
flags 参数为数据收发提供了额外的控制 */
ssize_t recv( int sockfd, void *buff, size_t len, int flags);
ssize_t send( int sockfd, const void *buff, size_t len, int flags);

/* UDP 数据读写:
recvfrom()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大小
src_addr 记录发送端的 socket 地址
addrlen 指定该地址的长度
sendto()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长度
dest_addr 指定接收数据端的 socket 地址
addrlen 指定该地址的长度 */
ssize_t recvfrom( int sockfd, void *buff, size_t len, int flags,
struct sockaddr* src_addr, socklen_t *addrlen);
ssize_t sendto( int sockfd, void *buff, size_t len, int flags,
struct sockaddr* dest_addr, socklen_t addrlen);

5.TCP 编程流程

TCP 提供的是面向连接的、可靠的、字节流服务。TCP 的服务器端和客户端编程流程如下:
在这里插入图片描述
服务器端:
socket();//创建套接字
bind();//指定ip,端口号
listen();//创建监听队列
accept();//接受连接
recv();//接受客户端发来的数据
send();//向客户端发送的数据
close();//断开连接
客户端:
socket();//创建套接字
connect();//向服务器端发起连接,有一个三次握手的问题
send();//向服务器端发送的数据
recv();//接受服务器端发来的数据
close();//断开连接,有一个四次挥手的问题

6.Linux可以用ifconfig来查看ipv4,ipv6地址,Windows可以用ifconfig来查看ipv4,ipv6地址

7.TCP服务器端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
    assert(sockfd != -1);//断言创建套接字成功

    struct sockaddr_in saddr,caddr;//ipv4专用的地址结构,通用的是sockaddr
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;//ipv4地址族
    saddr.sin_port = htons(6000);
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");//该地址是本地回环地址,只能用于测试,不能给其他主机发信息
    int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//把指针强转成通用套接字地址类型
    assert(res != -1);//断言绑定成功

    res = listen(sockfd,5);//5是队列的大小
    assert(res != -1);//断言监听成功

    while(1)
    {
        int len = sizeof(caddr);
        int c = accept(sockfd,(struct sockaddr*)&caddr,&len);//c仍然是一个套接字,c相当于客户端,在这里会发生阻塞,等待客户端连接
        if(c<0)
        {
            continue;
        }
        printf("c=%d\n",c);

        char buff[128] = {0};

        int num = recv(c,buff,127,0);//返回的是接收到的字节数
        printf("buff=%s\n",buff);
        send(c,"ok",2,0);//向客户端发送数据
        
        close(c);//关闭套接字
    }
}

8.TCP客户端代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);//创建套接字
    assert(sockfd != -1);//断言创建套接字是否成功

    struct sockaddr_in saddr;
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(6000);//必须和服务器的一样,去链接服务器的6000号端口,如果和服务器的不一样,就连接不到服务器端
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//连接服务器的6000号端口
    assert(res != -1);//断言连接是否成功
    
    printf("input\n");//提示用户输入数据
    char buff[128] = {0};
    fgets(buff,128,stdin);

    send(sockfd,buff,strlen(buff)-1,0);//-1是因为减掉了\n,而不是'\0'
    memset(buff,0,128);
    recv(sockfd,buff,127,0);
    printf("buff = %s\n",buff);
    close(sockfd);
}
  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:35:20  更:2022-03-06 13:36:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/16 3:51:41-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码