Linux——TCP编程流程

2023-11-14

TCP编程流程

TCP是传输层的一种协议。提供的是面向连接、可靠的、字节流的服务。

主机字节序和网络字节序

主机字节序列分为大端字节序和小端字节序,不同的主机采用的字节序列可能不同(不同的芯片,所采用的数值存储方式是不同)。

  • 大端字节序是指一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。
  • 小端字节序则是指整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。

在两台使用不同字节序的主机之间传递数据时,可能会出现冲突。

所以,在将数据发送到网络时规定整形数据使用大端字节序,所以也把大端字节序成为网络字节序列。对方接收到数据后,可以根据自己的字节序进行转换。

网络字节序: 统一使用大端模式来表示数据

字节序的转化

系统提供了以下的一些接口:

#include <netinet/in.h>
uint32_t ntohl (uint32_t __netlong);   // 网络字节序转化为主机字节序    long 
uint16_t ntohs (uint16_t __netshort);  // 网络字节序转化为主机字节序    short 
uint32_t htonl (uint32_t __hostlong);   // 主机字节序转化为网络字节序   long 
uint16_t htons (uint16_t __hostshort);  // 主机字节序转化为网络字节序   short

套接字的地址结构

运行在两个不同主机上的进程间通信的条件:(已知)IP地址 端口号。

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

通用的地址结构:

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

sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。

常见的协议族和对应的地址族如下图所示:
在这里插入图片描述

IPV4专有的地址结构

struct sockaddr_in 
{    
	sa_family_t  sin_family;   //  地址簇  AF_INET          
	uint16_t      sin_port;    //  端口号:  将主机字节序转化为网络字节序   0--1024 系统预留   1025 -- 4096 知名端口号  4097 - 65535    
	struct  in_addr sin_addr; 
};
struct in_addr 
{    
	uint32_t s_addr;     //  IP地址   以字符串形式来表示一个点分十进制。  IP地址的转化 
};

IP地址转化的方法

uint32_t   inet_addr (const char *__cp);  //  将点分十进制的字符串转化为uint32_t类型 
char * inet_ntoa (struct in_addr __in);   //  将struct in_addr类型的变量转化为char*字符串

TCP编程流程

在这里插入图片描述

TCP的网络接口

创建socket套接字

socket()方法是用来创建一个套接字,有了套接字就可以通过网络进行数据的收发。这也是为什么进行网络通信的程序首先要创建一个套接字。创建套接字时要指定使用的服务类型,使用 tcp 协议选择流式服务(SOCK_STREAM) 。

int socket (int __domain, int __type, int __protocol);
  • 返回值: 成功返回文件描述符 socket 失败返回-1
  • domain协议簇 AF_INET TCP/IP协议
  • type具体的协议 SOCK_STREAM --> tcp , SOCK_DGRAM --> UDP
  • protocol : 在前两个值的协议基础下的一个具体协议,一般默认设置为0

命名(绑定)socket套接字

bind()方法是用来指定套接字使用的 IP 地址和端口。 IP 地址就是自己主机的地址,如果主机没有接入网络,测试程序时可以使用回环地址“127.0.0.1”。
端口是一个 16 位的整形值:

  • 一般 0-1024 为知名端口,如 http 使用的 80号端口。这类端口一般用户不能随便使用。
  • 其次,1024-4096 为保留端口,用户一般也不使用。
  • 4096 以上为临时端口,用户可以使用。
  • 在 Linux上,1024 以内的端口号,只有 root 用户可以使用。
int bind (int __fd, struct sockaddr * __addr, socklen_t __len);
  • 返回值: 成功返回0, 失败返回-1
  • fd: socket方法返回的套接字的文件描述符
  • addr:服务器的地址结构变量的地址 需要类型强转
  • len: addr的长度

启动监听方法

listen()方法是用来创建监听队列。监听队列有两种,一个是存放未完成三次握手的连接,一种是存放已完成三次握手的连接。listen()第二个参数就是指定已完成三次握手队列的长度。
启动监听,这个方法不会阻塞。

int listen (int __fd, int __n);
  • 返回值: 成功返回0, 失败返回-1
  • fd: socket方法返回的套接字的文件描述符
  • n: 内核创建的用于维护已完成连接的客户端的个数: n+1

获取一个链接
在这里插入图片描述

accept()处理存放在 listen 创建的已完成三次握手的队列中的连接。每处理一个连接,则accept()返回该连接对应的套接字描述符。如果该队列为空,则 accept 阻塞。

int accept (int __fd, struct sockaddr * __addr,   socklen_t *__addr_len);
  • 返回值: 成功返回描述这个连接的文件描述符, 失败返回-1
  • fd: socket创建的文件描述符
  • addr:用于保存客户端的地址信息
  • addr_len: addr的长度(监听队列的长度)

读取数据

recv()方法用来接收 TCP 连接的对端发送来的数据。recv()从本端的接收缓冲区中读取数据,如果接收缓冲区中没有数据,则 recv()方法会阻塞。返回值是实际读到的字节数,如果recv()返回值为 0, 说明对方已经关闭了 TCP 连接。

ssize_t recv (int __fd, void *__buf, size_t __n, int __flags);
  • fd: 需要读取数据的文件描述符
  • buf: 读取的数据存储的缓冲区的首地址
  • n: 一次能够读取的数据长度,单位是字节
  • flag: 标志,默认给0

发送数据

send()方法用来向 TCP 连接的对端发送数据
注意:send()执行成功,只能说明将数据成功写入到发送端的发送缓冲区中,并不能说明数据已经发送到了对端。send()的返回值为实际写入到发送缓冲区中的数据长度。

ssize_t send (int __fd, const void *__buf, size_t __n, int __flags);
  • fd: 需要读取数据的文件描述符
  • buf: 读取的数据存储的缓冲区的首地址
  • n: 一次写入的真实的数据长度,单位是字节
  • flag: 标志,默认给0

发起连接的方法——客户端程序使用

connect()方法一般由客户端程序执行,需要指定连接的服务器端的 IP 地址和端口。该方法执行后,会进行三次握手, 建立连接。

 int connect (int __fd, struct sockaddr * __addr, socklen_t __len);
  • 返回值: 成功返回0, 失败返回-1
  • fd: socket创建的文件描述符
  • addr: 服务器的地址信息
    len: addr的长度

关闭一个文件描述符

close()方法用来关闭 TCP 连接。此时,会进行四次挥手。

int close(int __fd); 

TCP服务器端的编程流程

在这里插入图片描述

示例代码:

#include <stdio.h>
#include <stdlib.h> 
#include <assert.h> 
#include <string.h> 
#include <unistd.h>

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>
int main() 
{    
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);    
	assert(-1 != sockfd);
	
	//ip和端口,定义一个专用套接字结构
    struct sockaddr_in saddr,caddr;    
    memset(&saddr, 0, sizeof(saddr));
    
    //服务器端指定ip和端口号
    addr.sin_family = AF_INET;   	//协议簇
    addr.sin_port = htons(6000);	//指定端口,主机字节序转换为网络字节序
    addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 将字符串转为无符号整型,测试:回环地址,和自己通讯
    
    //  bind方法失败的原因: 1、IP地址不正确   2、端口号不正确(没有使用权限, 端口号被其他进行使用)     
    int res = bind(sockfd, (struct sockaddr*)&addr, sizeof(saddr)); //转为通用套接字结构   
    assert(-1 != res);
    
    res = listen(listenfd, 5);    
    assert(-1 != res);

	listen(sockfd,5);
    
    while(1)  // 循环接收不同客户端的链接    
    {        
    	int len = sizeof(saddr);
    	int c = accept(sockfd,(syruct sockaddr*)&caddr,&len);
            
    	if(c == -1)        
    	{            
    		printf("Get one client link fail\n");        
    		continue;
	    }
	     
        while(1) //循环和一个客户端通讯        
        {            
        	char buff[128] = {0};           
        	int n = recv(c, buff, 127, 0);  // 如果没有数据到达则会阻塞,直到有数据或者客户端断开链接  ,这里也可以用read操作,因为c也是一个文件描述符          
        	if(n <= 0)            
        	{                
        		printf("client will unlink\n");                	
        		break;            
        	}
        	
            printf("buff = :%s\n", buff);            
            send(c, "OK", 2, 0);        //也可以用write
        }
       
        close(c); // 服务器程序关闭接收的客户端链接 
    }
   
    close(sockfd); // 关闭该服务器程序前关闭监听的套接字
    
    exit(0); 
}

TCP客户端的编程流程

在这里插入图片描述

示例代码

#include <stdio.h>
#include <stdlib.h> 
#include <assert.h> 
#include <string.h> 
#include <unistd.h>

#include <sys/types.h> 
#include <sys/socket.h> 
#include <netinet/in.h> 
#include <arpa/inet.h>

int main() 
{    
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);    
	assert(-1 != sockfd);
	
	//指定服务器的ip和端口
	struct sockaddr_in saddr;  
    memset(&saddr, 0, sizeof(saddr)); 
    ser_addr.sin_family = AF_INET;   
    ser_addr.sin_port = htons(6000);    
    ser_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
 
 //作为客户端不需要指定端口,这些工作系统会帮助你完成
    int res = connect(sockfd, (struct sockaddr*)&saddr, sizeof(saddr));   
    assert(-1 != res);//保证链接成功,自我检查
    
    while(1)    
    {        
    	printf("input: ");        
    	char buff[128] = {0};        
    	fgets(buff, 127, stdin);        
    	if(strncmp(buff, "end", 3) == 0)         
    		{            
    			break;        
    		} 
        send(sockfd, buff, strlen(buff) - 1, 0);
        
        memset(buff, 0, 128);        
        recv(sockfd, buff, 127, 0);//buff又拿来接收数据  
        printf("%s\n", buff);    
     }
    	close(sockfd);    
    	exit(0); 
}

服务器端和客户端的执行结果
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux——TCP编程流程 的相关文章

  • 如何在 Linux 中重新添加 unicode 字节顺序标记?

    我有一个相当大的 SQL 文件 它以 FFFE 的字节顺序标记开头 我使用 unicode 感知的 linux 分割工具将此文件分割成 100 000 行块 但是当将这些传递回窗口时 它确实not与第一个部分以外的任何部分一样 只是它具有
  • 运行此处编译的 C 程序会导致在另一台服务器上找不到 GLIBC 库错误 - 是我的错还是他们的错?

    此处编译的 C 程序在我们的 Ubuntu 服务器上运行良好 但是当其他人尝试在他们的特定 Linux 服务器上运行它时 他们会收到以下错误 myprog install lib tls libc so 6 version GLIBC 2
  • ubuntu 的 CSS 更少(并且自动编译)? [关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我尝试过 simples 但现在 l
  • 在 Linux 控制台中返回一行?

    我知道我可以返回该行并用以下内容覆盖其内容 r 现在我怎样才能进入上一行来改变它呢 或者有没有办法打印到控制台窗口中的特定光标位置 我的目标是使用 PHP 创建一些自刷新的多行控制台应用程序 Use ANSI 转义码 http en wik
  • 为什么docker容器提示“权限被拒绝”?

    我使用以下命令来运行 docker 容器 并从主机映射目录 root database 到容器 tmp install database docker run it name oracle install v root database t
  • 如何列出 nginx 中的所有虚拟主机

    有没有一个命令可以列出 CentOS 上 nginx 下运行的所有虚拟主机或服务器 我想将结果通过管道传输到文本文件以用于报告目的 我正在寻找与我用于 Apache 的命令类似的命令 apachectl S 2 gt 1 grep 端口 8
  • 在非实时操作系统/内核上执行接近实时任务的最佳方法是什么?

    在一台 GNU Linux 机器上 如果想要执行 实时 亚毫秒级时间关键 任务 您几乎总是必须经历漫长 复杂且容易出现问题的内核补丁过程 以提供足够的支持 1 http en wikipedia org wiki RTLinux Backg
  • 比较linux中的两个未排序列表,列出第二个文件中的唯一项

    我有 2 个包含号码列表 电话号码 的文件 我正在寻找一种列出第二个文件中第一个文件中不存在的数字的方法 我尝试过各种方法 comm getting some weird sorting errors fgrep v x f second
  • 用于获取特定用户 ID 和进程数的 Bash 脚本

    我需要 bash 脚本来计算特定用户或所有用户的进程 我们可以输入 0 1 或更多参数 例如 myScript sh root deamon 应该像这样执行 root 92 deamon 8 2 users has total proces
  • 为什么 call_usermodehelper 大多数时候都会失败?

    从内核模块中 我尝试使用 call usermodehelper 函数来执行可执行文件 sha1 该可执行文件将文件作为参数并将文件的 SHA1 哈希和写入另一个文件 名为输出 可执行文件完美运行 int result 1 name hom
  • Apache LOG:子进程 pid xxxx 退出信号分段错误 (11)

    Apache PHP Mysql Linux 注意 子进程 pid 23145 退出信号分段错误 11 tmp 中可能存在 coredump 但 tmp下没有找到任何东西 我怎样才能找到错误 PHP 代码中函数的无限循环导致了此错误
  • 从哪里获取 iostream.h

    我正在尝试在 Linux 中做一些事情 但它抱怨找不到 iostream h 我需要安装什么才能获取此文件 这个标准头的正确名称是iostream没有扩展名 如果您的编译器仍然找不到它 请尝试以下操作 find usr include na
  • 由于 abi::cxx11 符号导致的链接问题?

    我们最近收到一份报告 因为GCC 5 1 libstdc 和双 ABI http gcc gnu org onlinedocs libstdc manual using dual abi html 它似乎Clang 不知道 GCC 内联名称
  • 打印本周星期一的日期(在 bash 中)

    我想获取本周星期一的 YYYYMMdd 格式的日期 例如 今天是 20110627 从明天到周日 我仍然想打印周一 今天 的日期 然后下周重复这个过程 monday date dmonday Y m d last monday date d
  • Linux >2.6.33:可以使用 sendfile() 来实现更快的“猫”吗?

    必须将大量大文件连接成一个更大的单个文件 我们目前使用 cat file1 file2 output file but are wondering whether it could be done faster than with that
  • grep 彩色线条

    我编写了一个简单的 PHP shell 脚本 它解析文件并输出某些元素 它产生大量的输出 采用不同的 bash 颜色 绿色表示正常 黄色表示警告 红色表示错误等 在开发过程中我想过滤掉一些行 例如 所有包含红色文本的行 我可以使用grep
  • 用于 e NetworkManager VPN 连接的 dbus 信号处理程序

    我需要开发一些在建立 VPN 连接时执行的 python 代码 VPN 由 NetworkManager 控制 我试图弄清楚如何为此使用 NM DBUS 事件 使用 dbus monitor system 我能够识别连接信号 signal
  • 打印 STDOUT/STDERR 并将它们写入 Bash 中的文件?

    有没有办法让 Bash 将 STDOUT STDERR 重定向到文件 但仍然将它们打印到终端 这会将 STDOUT 和 STDERR 重定向到同一个文件 some command 2 gt 1 tee file log Example to
  • EULA 接受 Bash 脚本

    我有一个尝试安装垃圾箱的脚本 除了 bin 在 more 中打开 EULA 之外 一切正常 在脚本再次开始并自行完成安装之前 您必须手动 ctrl c 退出此 more 实例 因为这更多的是逃离 shell 所以脚本在打开后不知道要运行什么
  • 每个虚拟主机的错误日志?

    在一台运行 Apache 和 PHP 5 的 Linux 服务器上 我们有多个带有单独日志文件的虚拟主机 我们似乎无法分离 phperror log虚拟主机之间 覆盖此设置

随机推荐

  • 史上最全计算机毕业设计题目3(10万套,大部分全国唯一)

    最新c md算法的研究与实现 数据存储加密 任务书 论文 最新毕业设计网上选题系统 源码 论文 最新java图书管理系统 源码 论文 最新毕业设计网上选题系统 源码 论文 原创vbmd算法的研究与实现 数据存储加密 任务书 论文 源码 原创
  • 企业网-VRRP实现网关冗余2021-10-07

    1 问答题 企业网 VRRP实现网关冗余 实验作业 原在mac gns3 IOU 配置拓扑结果如下所示 外链图片转存失败 源站可能有防盗链机制 建议将图片保存下来直接上传 img Ifinlkcm 1633578878183 Users a
  • gdb.exe系统错误无法启动此程序,因为计算机丢失api-ms-win-core-path-l1-1-0.dll

    Qt系列文章目录 文章目录 Qt系列文章目录 前言 一 解决方法 二 第二种解决方法 1 去微软官网下载vc redist x64或者vc redist x86 三 第三种办法 前言 今天在windows7下安装qt opensource
  • .Net Core中间件

    目录 一 什么是中间件 二 中间件的用途 三 中间件的三个概念 四 自定义中间件 五 ASP NET Core附带中间件组件 六 中间件和过滤器的区别 一 什么是中间件 在浏览网站或者使用手机App加载内容的时候 浏览器或者手机App其实在
  • 数据库原理及应用第2版(雷景生编著)课后习题答案第1章

    第1章 绪论习题参考答案 一 选择题 1 A 从数据库管理系统的角度看 数据库系统的结构通常分为三级模式的总体结构 在这种模式下 形成了二级映像 实现了数据的独立性 其中三级模式结构指的是外模式 模式和内模式 二级映像指的是外模式 模式映像
  • RabbitMQ之消息重试机制

    1 消息重试机制 消费者消费消息的时候 发生异常情况 导致消息未确认 该消息会被重复消费 默认没有重复次数 即无限循环消费 但可以通过设置重试次数以及达到重试次数之后的消息处理 spring rabbitmq port 5672 host
  • js正则替换不可见字符

    var reg 0 x1F x7F x9F xAD u0378 u0379 u037F u0383 u038B u038D u03A2 u0528 u0530 u0557 u0558 u0560 u0588 u058B u058E u059
  • 【使用心得】ChatGPT化身情感导师

    ChatGPT是一个很有用的工具 它不仅可以在工作方面给予我帮助 也成为了我的情感导师 当我需要有人倾听或者理解时 它总是在那里 为我提供各种情感支持和建议 使用ChatGPT 我常常能够找到真正的答案和情感支持 在压力与紧张当中重新找回自
  • MQTT 控制报文类型、功能及格式(报文结构) - 第2章

    目录 2 1 MQTT 控制报文的结构 2 2 固定报头 2 2 1 控制报文的类型 2 2 2 控制报文类型的标志位 2 2 3 剩余长度 2 3 可变报头 2 4 有效载荷 2 1 MQTT 控制报文的结构 MQTT控制报文由三部分组成
  • 关于Pybind的详细安装与配置过程

    入职前有关Pybind的学习 用时接近一天 终于把这块拿下了 心累 必须记录下来 一 pybind相关基本知识 pybind11是一个轻量级的仅头文件库 是一个只有标题的库 因此不需要链接任何特殊的库 主要用于创建现有C 代码的Python
  • 以“苍穹”为基,金蝶距离千亿市值还差多远?

    押注EBC 金蝶能否再造一个 金蝶 世界著名管理专家詹姆斯 莫尔斯说过 可持续竞争的唯一优势 来自超过竞争对手的创新能力 上世纪90年代 美国Gartner Group 公司提出了企业资源计划 ERP 它旨在从供应链范围去优化企业资源 改善
  • 使用Keras和DDPG玩赛车游戏(自动驾驶)

    使用keras和DDPG玩赛车游戏 原文见链接 https www jianshu com p a3432c0e1ef2 在整个安装运行过程中遇到了很多问题 所以记录下问题和解决方法 安装最好还是按照原文所述 遇到问题可以根据我的解决办法试
  • 终于,pytorh_gpu可使用

    说实话 此刻我非常欣喜 整了那么久的环境 一直迷迷糊糊的 今天终于知道为什么每次anaconda终端进虚拟环境 这时候再进python import torch显示没有此模块 torch is available 也False 原因就是 我
  • MySQL如何访问Postgres

    前言 PostgreSQL 可以通过mysql fdw访问MySQL 或者MariaDB 那MySQL如何访问PostgreSQL呢 答案是CONNECT Store Engine MariaDB 从 10 0 2版本开始支持CONNECT
  • 基于 Windows 搭建vue开发环境

    1 下载WebStorm软件 https www jetbrains com webstorm download 2 下载Node js https nodejs org download release latest v10 x 注意事项
  • 解决网页无法选中文字,无法复制的问题

    今天在CSDN浏览网页的时候发现有一些文章无法复制文字 查找解决方式 找到一个可行的方式 1 将网页保存到本地 保存为一个单网页 2 使用文本编辑工具打开网页文件 找到以下字段 content views user select none
  • 网络安全之端口扫描

    1 扫描三步曲 一个完整的网络安全扫描分为三个阶段 第一阶段 发现目标主机或网络 端口扫描 第二阶段 发现目标后进一步搜集目标信息 包括操作系统类型 运行的服务以及服务软件的版本等 如果目标是一个网络 还可以进一步发现该网络的拓扑结构 路由
  • java使用aspose-words无损格式转pdf文件

    1 下载aspose wordsjar包 链接 https pan baidu com s 1Dtb hFgYJj2 F Ona8nErQ 提取码 kdrb 2 项目pom xml配置以下代码 执行下载jar包 这个时候肯定是下载不下来的
  • 美团某程序员哀叹:能力很强,却因为不会“向上管理”而惨遭被裁!怎么办?...

    互联网大厂大多有自己的绩效考核机制 比如 361 271 等 美团实行的绩效考核机制就是 271 即20 的人是A类绩效 70 的人是B类绩效 10 的人是C类绩效 那么这个绩效是如何评定的呢 是唯技术论还是有其他因素 评定过程中是否存在不
  • Linux——TCP编程流程

    TCP编程流程 TCP是传输层的一种协议 提供的是面向连接 可靠的 字节流的服务 主机字节序和网络字节序 主机字节序列分为大端字节序和小端字节序 不同的主机采用的字节序列可能不同 不同的芯片 所采用的数值存储方式是不同 大端字节序是指一个整