Linux网络编程之Tcp(内含服务器、客户端代码实例)

2023-11-19

前言:

本节讲解需要相关的网络基础知识,如果缺少相关知识,请先学习:Linux网络编程之网络基础Linux网络编程之网络基础2

Tcp模型:

先打开我们之前讲过的Tcp模型作为参考:

img点击并拖拽以移动

服务器端:

创建socket:int socket(int domain, int type, int protocal);

参数1:域 AF_INET(ipv4) AF_INET6 (ipv6)

参数2:类型 SOCK_DGRAM(udp协议) SOCK_STREAM (tcp协议)

参数3:默认0即可

绑定地址:int bind(int sockfd, const struct sockaddr* addr, socklen_t len);

参数1:socket描述符

参数2:地址结构体

​ //通用的地址【此处所用的结构体】
struct sockaddr{
sa_family_t sa_family; //协议族
char sa_data[14]; //地址
}
//ipv4地址【ipv4专用,我们平常所用的最多】
struct sockaddr_in{
short int sin_family; //协议族
unsigned short int sin_port; //端口号
struct in_addr sin_addr; // IP地址【结构体原型在下面】
unsigned char sin_zero[8]; //填充
}
struct in_addr{
unsigned long s_addr; // IP地址 4字节整数
}

补充1:字符串形式的IP地址与整数形式的IP地址转化

从上面可以看到,我们用的地址是整数型的;而我们平常在现实生活中所看到的地址:例如:192.168.1.1是字符串型的;

地址转化函数:

in_addr_t inet_addr(const char* cp); //将192.168.1.1转化为整数型的IP地址(网络字节序)

举例:in_addr.saddr = inet_addr(“192.168.1.1” );

char *inet_ntoa (struct in_addr) //将整数形式的IP地址转化为字符串形式的IP地址

补充2:网络字节序

补充2.1:大端和小端模式

要想知道网络字节序是什么?首先需要知道大端和小端模式;简而言之:大端模式,是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中 ;小端模式,是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中 ;

具体知识请参考https://blog.csdn.net/litter_driver777/article/details/51636311。

我们自己电脑可能是大端,也可能是小端的;同样,别人的电脑也有可能是大端或者小端的,所以在网络上传输数据时,我们就需要统一他们的大小端模式。

规定网络中传输的是大端模式,所以网络字节序是大端模式的;

主机字节序和网络字节序转化函数:

uint32_t htonl(uint32_t hostlong); //将32位的数据从主机字节序转换为网络字节序

​ //举例: in_addr.saddr = htonl(INADDR_ANY)

uint16_t htons(uint16_t hostshort); //将16位的数据从主机字节序转换为网络字节序

uint32_t ntohl(uint32_t netlong); //将32位的数据从网络字节序转换为主机字节序

uint16_t ntohs(uint16_t netshort); //将16位的数据从网络字节序转换为主机字节序

监听:int listen(int sockfd, int backlog);

参数1:socket描述符

参数2:backlog 允许连接的客户机的数目

等待连接:int accept(int sockfd, struct sockaddr* reStrict addr, socklen_t* restrict len);

参数1:socket描述符

参数2:客户机的地址(接收到的)

参数3:地址长度的指针(注意:是指针哦,不是整形)

返回值:新的套接字的fd(记住了:以后收发用这个新的sockfd哦!)

发送:ssize_t send(int sockfd, const void* buf, size_t nbytes, int flags);

参数1:新的套接字的fd(accept的返回值)

参数2:要发送的数据

参数3:数据的长度

参数4:默认为0即可

接收:ssize_t recv(int sockfd, void* buf, size_t nbytes, int flags);

关闭连接close():

客户机端:

连接服务器:connect(int sockfd, const struct sockaddr* addr, socklen_t len);

参数1:socket描述符

参数2:要连接的服务器的地址

参数3:地址长度—整形

其余函数参考服务器端的讲解。

代码:

服务器code:

#include <stdio.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>

#define portnum 3333

int main()
{
	int sockfd;
	int new_fd;
	struct sockaddr_in server_addr;
	struct sockaddr_in client_addr;
	char buffer[128];
	int nByte;
	int sin_size;
	
	//1.创建套接字
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		printf("create socket error!\n");
		exit(1);
	}
	
	//2.1设置要绑定的地址
	bzero(&server_addr, sizeof(struct sockaddr_in));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(portnum); //字节序(大小端)
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
		
	//2.绑定地址
	bind(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr));
	
	//3.监听端口
	listen(sockfd,5);
		
	while(1)
	{
		//4.等待连接
		sin_size = sizeof(struct sockaddr);
		new_fd = accept(sockfd,(struct sockaddr*)(&client_addr),&sin_size);//注意:第3个参数为socklen_t*(整形的指针)
		printf("server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); //将整数型的IP地址转化为字符型(192.168.1.1)
		
		//5.接收数据
		nByte = recv(new_fd, buffer, 128, 0);
		buffer[nByte] = '\0';
		printf("server reciivd : %s\n", buffer);
		
		//6.结束连接
		close(new_fd);
	}	
	
	close(sockfd);
	
	return 0;
	
}	

客户端code:

#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>

#define portnum 3333

int main()
{
	struct sockaddr_in server_addr;
	char buffer[128];
	
	//1.创建套接字
	int sockfd;
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		printf("create socket error!\n");
		exit(1);
	}
	
	//2.1设置要连接的服务器的地址
	bzero(&server_addr, sizeof(struct sockaddr_in));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(portnum);
	server_addr.sin_addr.s_addr = inet_addr("192.168.103.100");
	
	//2.连接服务器
	if(connect(sockfd,(struct sockaddr*)(&server_addr),sizeof(struct sockaddr)) == -1)
	{
		printf("connect error\n");
		exit(1);
	}
	
	//3.发送数据到服务器
	printf("please input char:\n");
	fgets(buffer,128,stdin);
	send(sockfd, buffer, strlen(buffer), 0);
	
	//4.关闭连接
	close(sockfd);
	
	return 0;
	
}

运行结果:

操作流程:先开服务器,再开客户端,客户端提示输入please input char:后,自行输入任意字符串,点enter后发送,可以看到服务器收到了数据。

服务器端:

在这里插入图片描述

客户机端:

在这里插入图片描述

结束

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

Linux网络编程之Tcp(内含服务器、客户端代码实例) 的相关文章

随机推荐

  • OpenFace库(Tadas Baltrusaitis)中基于HOG进行正脸人脸检测的测试代码

    Tadas Baltrusaitis的OpenFace是一个开源的面部行为分析工具 它的源码可以从https github com TadasBaltrusaitis OpenFace下载 OpenFace主要包括面部关键点检测 facia
  • 计算机毕业设计项目大全

    文章目录 0 前言 1 java web 管理系统 毕设选题 2 java web 平台 业务系统 毕设选题 3 游戏设计 动画设计类 毕设选题 适合数媒的同学 4 算法开发 5 数据挖掘 毕设选题 6 大数据处理 云计算 区块链 毕设选题
  • 美化github首页

    创建仓库 注 新建仓库的名字要和自己的名字完全一致 提交的源码必须在主分支上 master或者main 配置完成 回到你的主页 你就能看到在置顶项目的上方多了一个README md 编辑改文件即可 美化首页源码 1 github概览数据 G
  • 若依框架中@DataScope数据权限注解的使用与自定义sql语句。

    介绍 若依框架本身具有角色 部门 用户业务 在业务中 用户必须绑定一个角色 而角色又必须将自身绑定到部门 角色绑定了哪些部门 就决定着隶属于该角色的用户能对哪些部门数据进行增删改 那么 怎么实现让用户只能遵循其绑定角色所指定的部门 来进行数
  • 分布式CAP定理,为什么不能同时满足三个特性?

    在弄清楚这个问题之前 我们先了解一下什么是分布式的CAP定理 根据百度百科的定义 CAP定理又称CAP原则 指的是在一个分布式系统中 Consistency 一致性 Availability 可用性 Partition tolerance
  • linux rhel6 nvidia 卡 在启动界面,RHEL6上安装Nvidia显卡驱动

    二 在字符界面下 运行级3 运行驱动安装程序 在RHEL6上因为Nouveau的关系会出现下面的错误 因此需要将该模块禁用掉 root rhel6 sh NVIDIA Linux x86 64 275 09 07 run Verifying
  • 如何编译SQLite-How To Compile SQLite

    SQLite是ANSI C的源代码 在使用之前必须要编译成机器码 这篇文章是用于各种编译SQLite方法的指南 这篇文章不包含编译SQLite的每个步骤的反馈 那样可能会困难因为每种开发场景都不同 所以这篇文章描述和阐述了编译Sqlite的
  • 区块链应用开发(智能合约的开发和WeBASE合约IDE的使用)

    文章目录 四 智能合约的开发和WeBASE合约IDE的使用 一 实验概述 二 实验目标 三 实验环境及建议 四 实验步骤 4 1 启动Webase 4 2 智能合约开发 4 2 1 合约功能设计 4 2 2 存证合约开发 4 2 3 工厂合
  • 浅析赋值、浅拷贝、深拷贝的区别

    1 原理 赋值 将某一对象赋给某个变量的过程 称为赋值 浅拷贝 浅拷贝是创建一个对象 这个对象有着原始对象属性值的一份精准拷贝 如果属性是基本类型 拷贝的就是基本类型的值 如果属性是引用类型 拷贝的就是内存地址 如果其中一个对象改变了这个地
  • 寒假训练 第二节 数据结构基础 总结

    数组 定义 语言支持数组数据结构 它可以存储一个固定大小的相同类型元素的顺序集合 数组是用来存储一系列数据 但它往往被认为是一系列相同类型的变量 所有的数组都是由连续的内存位置组成 最低的地址对应第一个元素 最高的地址对应最后一个元素 数组
  • python 爬虫 GET(带参) 请求 配置params参数

    import requests 发送网络请求 response requests get http httpbin org get name Jack age 30 print response text 输出结果 args age 30
  • 2019北邮网安院机试真题(回忆版)@lantin

    2019北邮网安院机试真题 回忆版 细不谈 前两题真的都是签到题 会简单排序和if else都可以写的出来的题目 网安院的机试基本上是两道签到题 C题是数据结构题 D题是算法 做到保2争3 保3争4面试不出原则问题基本上复试稳了 Probl
  • Qt系列文章之 mousePressEvent

    上一篇文章介绍QTabWidget的使用技巧 本文开始介绍Qt最常用的事件之一QMouseEvent 鼠标事件 mousePressEvent 鼠标事件是Qt中十分重要的人机交互形式 鼠标事件包括很多内容 此处先介绍最基本的mousePre
  • MySQL 表分区 A PRIMARY KEY must include all columns in the table's partitioning function

    当我们在对MySQL的数据表进行分区的时候 很容易碰到如下错误 A PRIMARY KEY must include all columns in the table s partitioning function 意思是说分区的字段必须是
  • Qt 不规则窗口,不规则按钮,不规划控件 不规则界面

    有一些特殊情况 需要使用不规则窗口或按钮 看起来非常炫酷 类似 Qt 实现 不规则样式设置 不是视觉欺骗 是真正的不规则 点击外面不会触发按钮的点击信号 Part1 不规则窗口 效果 显示成一张图片中的图形 使用带有透明区域的png图片 i
  • 设计模式--提供者模式provider

    设计模式 C 提供者模式 Provider Pattern 介绍 为一个API进行定义和实现的分离 示例 有一个Message实体类 对它的操作有Insert 和Get 方法 持久化数据在SqlServer数据库中或Xml文件里 根据配置文
  • sql 求日期均值

    备忘一下 在sql中求日期均值的语句 首先 AVG 只能作用与数值型字段 不能作用于日期字段 所以不能像求数值均值一样求日期均值 要求日期均值 需要先把 日期转为Long 然后对转后的Long求均值 对Long求完均值 在把Long转为 日
  • git 将本仓库的分支代码提交到另外一个仓库

    因为新项目开发的项目代码是建立在另外一个项目的代码框架上进行样式与逻辑功能修改 所以为了省事 想着把旧仓库的temp分支代码直接提交到新仓库master分支底下进行项目开发 所以需要在git上进行一些操作 将本仓库的分支代码提交到另外一个仓
  • 【Linux扩容】Vmware虚拟机内的Linux扩容

    1 虚拟机设置 Vmware 虚拟机 设置 硬盘 扩展 40 64G 2 Linux磁盘扩展 有图形化桌面 GParted 分区上右键 扩展 保存 无图形化桌面 找一个有图形化桌面的Linux live镜像 CD载入 或者LinuxToGo
  • Linux网络编程之Tcp(内含服务器、客户端代码实例)

    文章目录 前言 Tcp模型 服务器端 客户机端 代码 运行结果 结束 前言 本节讲解需要相关的网络基础知识 如果缺少相关知识 请先学习 Linux网络编程之网络基础 和 Linux网络编程之网络基础2 Tcp模型 先打开我们之前讲过的Tcp