Linux下套接字TCP实现网络通信

Linux下套接字TCP实现网络通信

文章目录

  • Linux下套接字TCP实现网络通信
  • 1.引言
  • 2.具体实现
    • 2.1接口介绍
      • 1.socket()
      • 2.bind()
      • 3.listen()
      • 4.accept()
      • 5.connect()
    • 2.2 服务器端server.hpp
    • 2.3服务端server.cc
    • 2.4客户端client.cc

1.引言

套接字(Socket)是计算机网络中实现网络通信的一种编程接口。它提供了应用程序与网络通信之间的一座桥梁,因为它允许应用程序通过网络发送和接收相应的数据以实现不同主机之间的通信。
在这里插入图片描述

通常套接字由以下两部分组成:

1.网络IP和端口号:IP用来标识主机,而端口号可以标识到单台主机的唯一进程。

2.通信协议:套接字通过规定通信协议来制定数据传输和发送的规则。常见的有TCP和UDP等协议。

TCP是一种面向连接的协议,提供可靠的、有序的、基于字节流的数据传输。

UDP是一种无连接的协议,提供不可靠的、无序的、基于数据报的数据传输。

​ 今天我们来学习TCP实现网络通信。TCP由于能提供可靠、基于字节流的数据传输,使用率与使用场景也比UDP多很多。

我们来看看能实现出什么样的结果(聊天室模拟两个用户随机通信):

若不开启服务端就只开启客户端的话,那么就会像打游戏的某些情况连不上:
在这里插入图片描述

当服务端和客户端都开启后就可以正常通信了:

在这里插入图片描述

这里我们还是通过客户端给服务器端发送消息,通过TCP链接实现通信。

那么事不宜迟,我们马上开始分享实现过程吧!

2.具体实现

2.1接口介绍

1.socket()

​ socket函数是用于创建套接字的函数,创建成功返回文件描述符fd,失败返回-1

int socket(int domain, int type, int protocol);

​ 参数说明:

  • domain

    :指定套接字的地址族(Address Family)

    今天我们选择:

    • AF_INET:IPv4 地址族
  • type

    :指定套接字的类型(Socket Type)

    今天我们选择:

    • SOCK_STREAM:有连接的字节流套接字,用于TCP协议
  • protocol

    :可选参数,指定具体的传输协议。常用的有:

    ​ 今天我们选择:

    • 0:自动选择合适的协议

2.bind()

​ 在Linux下,bind() 函数用于将一个套接字(socket)与特定的IP地址和端口号进行绑定

*int bind(int sockfd, const struct sockaddr addr,socklen_t addrlen);

参数说明:

  • sockfd:要进行绑定的套接字的文件描述符。
  • addr:指向一个 struct sockaddr 结构体的指针,其中包含要绑定的IP地址和端口号信息。
  • addrlenaddr 结构体的长度。

在绑定bind的第二个参数中,我们也需要用到库中定义好的sockaddr_in结构体来初始化!

具体结构体struct sockaddr_in说明:

结构体中有三个值也需要初始化指定一下:

  1. sin_family:表示地址族(Address Family),一般为 AF_INET

  2. sin_port:表示端口号。它是一个 16 位的整数,使用网络字节序(大端字节序)表示。在使用时,通常需要使用 htons() 函数将主机字节序转换为网络字节序。

  3. sin_addr:表示 IPv4 地址。它是一个 struct in_addr 类型的结构体,用于存储 32 位的 IPv4 地址。

    一般服务端用INADDR_ANY,让udp_server在启动时候可以绑定任何ip.

    ​ 客户端用inet_addr函数将字符串转化成32位无符号整数


3.listen()

listen()函数:将套接字设置为监听状态,等待连接请求。

参数:

  • sockfd:套接字的文件描述符。这里我们选择前面socket创建好的返回值.
  • backlog:指定等待连接队列的最大长度。一般不会太大,我们这里写32即可。

4.accept()

accept() 函数:接受客户端的连接请求,创建一个新的套接字用于与客户端进行通信。

参数:

  • sockfd:套接字的文件描述符。这里我们选择前面socket创建好的返回值.
  • addr:指向客户端地址的结构体指针。创建一个sockaddr的结构体强转一下(struct sockaddr*)即可。
  • addrlen:客户端地址结构体的字节大小。创建一个socklen_t类型的值用来计算结构体大小。

5.connect()

connect() 函数:发起与远程主机建立TCP连接的请求。

参数:

  • sockfd:套接字的文件描述符。这里我们选择前面socket创建好的返回值.
  • addr:指向远程主机地址的结构体指针。创建一个sockaddr的结构体强转一下(struct sockaddr*)即可。
  • addrlen:远程主机地址结构体的字节大小。创建一个socklen_t类型的值用来计算结构体大小。

2.2 服务器端server.hpp

在整个服务器端server.hpp中,我们需要创建tcpServer类,并在类中建立这些成员:监听套接字、端口号等.

在类中我们还需要初始化服务器InitServer()和启动服务器Start()两个接口。

服务器端具体实现思路是:我们创建套接字socket()后开始绑定bind(),之后监听listen(),监听成功后我们获取链接accept()即可。

思路简单,但是实现还需要很多事完成:

static const uint16_t defaultport = 8081;
static const int backlog = 32;
using func_t = std::function<std::string(const std::string&)>;class tcpServer
{public:tcpServer(func_t func,uint16_t port = defaultport):_func(func),_port(port),_quit(true){}~tcpServer() {}void InitServer(){//1.创建套接字_listensock = socket(AF_INET,SOCK_STREAM,0);if(_listensock < 0){std::cerr << "create socket error" << std::endl;exit(-1);}//2.绑定struct sockaddr_in local;memset(&local,0,sizeof(local)); //清空结构体local.sin_family = AF_INET;local.sin_port = htons(_port);    //主机转网络local.sin_addr.s_addr = htonl(INADDR_ANY);if(bind(_listensock,(struct sockaddr*)&local,sizeof(local)) < 0){std::cerr << "bind error" << std::endl;exit(-2);}//3.监听(tcp)if(listen(_listensock,backlog) < 0){std::cerr <<" listen error" << std::endl;exit(-3);}}void Start(){_quit = false; //运行时设置位运行状态,即不退出的状态while(!_quit) //服务器死循环{struct sockaddr_in client;socklen_t len = sizeof(client);//4.获取链接acceptint sock = accept(_listensock,(struct sockaddr*)&client,&len);if(sock < 0) {std::cerr <<"accept error " <<std::endl;continue;} //揽客的sock失败后继续即可//5.获取链接成功std::cout<< "获取链接成功" << sock << " from " << _listensock <<  std::endl;service(sock);}}void service(int sock) //服务{char buffer[1024];while(true){ssize_t s = read(sock,buffer,sizeof(buffer)-1);if(s > 0) //代表成功读取{buffer[s] = 0;std::string res = _func(buffer); //回调显示std::cout<< res <<std::endl;write(sock,res.c_str(),res.size());}else if(s == 0) //代表读到文件结尾 在网络中就相当于对方关闭链接{close(sock);std::cout << "quit" <<std::endl;break;}else //文件读取失败{close(sock);std::cerr << " read error" <<std::endl;break;}}}private:uint16_t _port; //端口号int _listensock; //监听套接字bool _quit; //代表服务器没有运行的状态func_t _func; //回调包装器,为了后面输出后回显
};

2.3服务端server.cc

在服务端的主文件中,我们直接包含上面的头文件。

我们期望的用法是:./tcp_server port,代表运行可执行文件后面需要带一个参数:端口号

所以我们能够从用户中输入的port,通过main函数中的**char* argv[]**参数列表中获取到。并传给tcpSercer类中初始化与启动服务器即可。

#include "server.hpp"
#include<memory>
using namespace std;static void usage(string proc) //使用手册,代表运行可执行文件后面需要带一个参数:端口号
{std::cout << "Usage:\n\t" << proc << "port\n" <<std::endl;}std::string echo(const std::string& message)//输出回显
{return message;
}//期望用法:./tcp_server port
int main(int argc,char* argv[])
{if(argc != 2) //输入的不是两个参数,说明你不会用。输出使用手册{usage(argv[0]);exit(-1);}uint16_t port = atoi(argv[1]); //强转成能够使用的类型unique_ptr<tcpServer> ts(new tcpServer(echo,port));//采用智能指针创建释放资源ts->InitServer();ts->Start();return 0;
}

2.4客户端client.cc

在客户端中我们conncet尝试链接到服务器端,这里需要做一个重连反馈:正在尝试重连…

我们期望运行格式:./client serverip serverport,代表运行可执行文件后需要两个参数:IP和端口

我们从用户输入的两个参数中传给main,并通过main参数char* argv[]参数列表获取到,之后获取到直接转化即可。


static void usage(string proc)
{std::cout << "Usage:\n\t" << proc << "serverip serverport\n" <<std::endl;}
//期望使用:./client serverip serverport 
int main(int argc,char* argv[])
{if(argc != 3){usage(argv[0]);exit(-2);}string serverip = argv[1]; //获取到参数uint16_t port = atoi(argv[2]);//1.创捷套接字int sock = socket(AF_INET,SOCK_STREAM,0);if(sock < 0){std::cerr << " socket error" <<std::endl;exit(-1);}//2.客户端需要链接服务器 --connectstruct sockaddr_in server; //memset(&server,0,sizeof(server)); //清空结构体server.sin_family = AF_INET;//初始化结构体server.sin_port = htons(port);//server.sin_addr.s_addr = inet_addr(serverip.c_str()); //客户端inet_aton(serverip.c_str(),&(server.sin_addr));int cnt = 5;while(connect(sock,(struct sockaddr*)&server,sizeof(server)) != 0) //如果绑定失败{sleep(1);std::cout<<"正在尝试重连... 重连次数:" <<cnt-- <<std::endl;if(cnt <= 0) break;}    if(cnt <= 0){cerr<< "服务器连接失败"<<endl;exit(-1);}//3.连接成功while(true) //连接成功后从客户端直接输入发送数据{string line;char buffer[1024];cout<<"Enter>> "; getline(cin,line);write(sock,line.c_str(),line.size()); //给缓冲区写数据ssize_t s = read(sock,buffer,sizeof(buffer) -1);if(s > 0)//正常写{buffer[s] = 0;cout<< " server rcho >>>" <<buffer <<endl;}else if(s == 0) //写结束{cerr << "server quit" <<endl;break;}else{ //异常cerr<< " read error " <<endl;break;}}close(sock);//关闭套接字,管不管都可以return 0;
}

最后运行之后就能获得我们之前通信的结果了:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/51679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux笔记--Ubuntu设置sftp服务

目录 1--修改配置文件 2--注销代码 3--更改代码 4--重启服务 1--修改配置文件 vim /etc/ssh/sshd_config 2--注销代码 ① 注销&#xff1a; Subsystem sftp /usr/libexec/openssh/sftp-server ② 更改为&#xff1a; Subsystem sftp internal-sftp 3--更改代码 Per…

浙大陈越何钦铭数据结构07-图6 旅游规划

题目: 有了一张自驾旅游路线图&#xff0c;你会知道城市间的高速公路长度、以及该公路要收取的过路费。现在需要你写一个程序&#xff0c;帮助前来咨询的游客找一条出发地和目的地之间的最短路径。如果有若干条路径都是最短的&#xff0c;那么需要输出最便宜的一条路径。 输入…

Java——面向对象编程思想

前言 如果有人问你&#xff0c;C和Java的区别是什么呢&#xff1f; 我想大部分人肯定脱口而出的是&#xff1a;C是面向过程的&#xff0c;Java是面向对象的。 那如何理解面向过程和面向对象呢&#xff1f; 首先&#xff0c;面向过程和面向对象都是编程思想。 ①面向过程&#…

【3Ds Max】可编辑多边形“边界”层级的简单使用

目录 示例 &#xff08;1&#xff09;挤出 &#xff08;2&#xff09;插入顶点 &#xff08;3&#xff09;切角 &#xff08;4&#xff09;利用所选内容创建图形 &#xff08;5&#xff09;封口 &#xff08;6&#xff09;桥 示例 这里我们首先创建一个长方体&#xff…

【Java面试】指令重排引发问题及解决方案

一 指令重排引发的问题 什么是指令重排&#xff1f; 指令重排是指在程序执行过程中&#xff0c;为了优化性能&#xff0c;编译器或处理器可能会重新安排代码指令的执行顺序&#xff0c;但要求不改变程序的最终结果。 在多线程环境中&#xff0c;指令重排可能会引发一些问题&a…

C#使用xamarin进行跨平台开发

使用 Xamarin 进行跨平台开发可以使用 C# 和 .NET 平台来开发移动应用程序&#xff0c;同时将代码在多个主要移动操作系统上运行&#xff0c;包括 Android 和 iOS。以下是在 C# 中使用 Xamarin 进行跨平台开发的一般步骤&#xff1a; 安装 Xamarin&#xff1a; 在开始之前&…

Java基础之IO流File类创建及删除

1.File类概述及构造方法 2.File类创建功能 文件创建成功&#xff01; 如果文件不存在&#xff0c;就创建文件&#xff0c;并返回true 如果文件存在&#xff0c;就不创建文件&#xff0c;并返回false 如果文件夹不存在&#xff0c;就创建文件夹&#xff0c;并返回true 如果文件…

html学习第2篇---标签(1)

html学习第2篇---标签 1、标题标签h1---h62、段落标签p3、换行标签br4、文本格式化标签5、div标签和span标签6、图像标签img6.1、图像属性6.2、相对路径、绝对路径 7、超链接标签a7.1、属性7.2、分类 8、注释标签和特殊字符8.1、注释8.2、特殊字符 1、标题标签h1—h6 为了使网…

【Unity学习笔记】DOTween(2)官方案例

本文中大部分内容学习来自DOTween官方文档 此处无法展示动图&#xff08;懒得录GIF&#xff09;&#xff0c;请下载官方案例场景自行学习 文章目录 场景1 基本补间场景2 动态补间场景3 Shader修改场景4 路径拟合运动场景5 序列播放场景6 UGUI 场景1 基本补间 案例一展示了最基…

Python操作sql,备份数据库

1、批量执行sql import pymysql# 执行批量的 SQL 语句 def executeBatchSql(cursor, sqlStatements):for sql in sqlStatements:try:cursor.execute(sql)print(Executed SQL statement:, sql)except Exception as e:print(Error executing SQL statement:, e)# 创建数据库连接…

AMBA总线协议(0)——目录与传送门

一、AMBA总线协议 Arm高级微控制器总线架构&#xff08;Advanced Microcontroller Bus Architecture&#xff0c;AMBA&#xff09;是一种开放式标准片上互联规范&#xff0c;用于连接和管理片上系统&#xff08;System on Chip,Soc&#xff09;中的功能块。 AMBA是一种广泛用于…

设计模式-代理模式

代理模式 ● 为对象提供一个代理类&#xff0c;增强该对象的方法&#xff0c;控制对这个对象的访问 ● 静态代理和动态代理&#xff1a;静态代理就是编译的时候就已经确定&#xff0c;而动态代理就是运行时才会生成 静态代理的使用场景 缓存代理 ● 提供数据的缓存功能&…

python字符串处理15题-刷题

题目一&#xff1a;最长无重复子串长度 给定一个字符串&#xff0c;找出其中不含有重复字符的最长子串的长度。 示例&#xff1a; 输入: "abcabcbb" 输出: 3 解释: 最长无重复子串是 "abc"&#xff0c;长度为 3。 解答&#xff1a; def length_of_longest…

Ubuntu vi 左下角没有提示

1 打开终端&#xff0c;输入以下命令 sudo gedit /etc/vim/vimrc.tiny 2 进入文件后&#xff0c;修改 set compatible 为set nocompatible&#xff0c;如下所示&#xff1a; " Vim configuration file, in effect when invoked as "vi". The aim of this "…

全球公链进展| Shibarium已上线;opBNB测试网PreContract硬分叉;Sui 主网 V1.7.1 版本

01 ETH 以太坊最新一次核心开发者执行会议&#xff1a;讨论 Devnet 8 更新、ElP-4788、Holesky 测试网等 以太坊核心开发者 Tim Beiko 总结最新一次以太坊核心开发者执行会议&#xff08;ACDE&#xff09;&#xff0c;讨论内容包括 Devnet 8 更新、ElP-4788、Holesky 测试网、…

【DevOps视频笔记】4.Build 阶段 - Maven安装配置

一、Build 阶段工具 二、Operate阶段工具 三、服务器中安装 四、修改网卡信息 五、安装 jdk 和 maven Stage1 : 安装 JDK Stage 2 : 安装 Maven 2-1 : 更换文件夹名称 2-2 : 替换配置文件 settings.xml- 2-3 : 修改settings.xml详情 A. 修改maven仓库地址 - 阿里云 B…

CentOS系统环境搭建(十七)——elasticsearch设置密码

centos系统环境搭建专栏&#x1f517;点击跳转 elasticsearch设置密码 没有密码是很不安全的一件事&#x1f62d; 文章目录 elasticsearch设置密码1.设置密码2.登录elasticsearch3.登录kibana4.登录elasticsearch-head 1.设置密码 关于Elasticsearch的安装请看CentOS系统环境搭…

适配小程序隐私保护指引设置

由于小程序发布了一个公告&#xff0c;那么接下来就是怎么改简单的问题了。毕竟不太想大的改动历史上的代码。尽量简单的适配隐私策略就可以了。 整体思路也是参考现在App普遍的启动就让用户同意隐私策略&#xff0c;不同意不让用&#xff0c;同意了之后才能够继续使用。 公告…

ubuntu下mysql

安装&#xff1a; sudo apt update sudo apt install my_sql 安装客户端&#xff1a; sudo apt-get install mysql-client sudo apt-get install libmysqlclient-dev 启动服务 启动方式之一&#xff1a; sudo service mysql start 检查服务器状态方式之一&#xff1a;sudo …

计算机网络 QA

DNS 的解析过程 浏览器缓存。当用户通过浏览器访问某域名时&#xff0c;浏览器首先会在自己的缓存中查找是否有该域名对应的 IP 地址&#xff08;曾经访问过该域名并且没有清空缓存&#xff09;系统缓存。当浏览器缓存中无域名对应的 IP 地址时&#xff0c;会自动检测用户计算机…