手把手写c++服务器(21):linuxsocket网络编程入门基础(代码片段)

沉迷单车的追风少年 沉迷单车的追风少年     2022-12-18     669

关键词:

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】

前言:刚开始写C++服务器的时候,我们进行网络编程肯定是使用socket API,等熟练之后,会根据我们自己的需要,封装这些API组成自己的网络编程库。如何优雅地封装?这是一个哲学问题,非常能体现C++程序员的功底。但是首先要熟悉socket的常见用法,这一篇博客带你入门,并用手把手写C++服务器(18):TCP紧急传输的方法——带外数据 (原理与代码示例)这篇文章作为综合应用实例。

目录

什么是socket?

socket建立连接的过程

常用socket函数一览

不清楚的地方记得查询linux socket手册!

通用socket地址:sockaddr和sockaddr_storage

Unix专用socket地址:sockaddr_un

TCP/IP专用socket地址:sockaddr_in和sockaddr_in6

IP地址转换函数:inet_addr、 inet_aton、inet_ntoa

创建socket:socket()

参数释义:

函数返回:

绑定socket地址:bind()

函数返回:

监听socket地址:listen()

接受socket连接:accept()

发起socket连接:connect()

函数返回:

关闭socket连接:close()

立即终止socket连接:shutdown()

TCP数据读写:recv()、send()

参考:


什么是socket?

socket(套接字)用来描述IP地址和端口,是通信链的句柄,应用程序可以通过 Socke向网络发送请求或者应答网络请求。socket是支持TCP/P协议的网络通信的基本操作单元是对网络通信过程中端点的抽象表示,包含了进行网络通信所必须的五种信息连接所使用的协议;本地主机的P地址;本地远程的协议端口;远地主机的IP地址以及远地进程的协议端口。

socket建立连接的过程

常用socket函数一览

不清楚的地方记得查询linux socket手册!

https://man7.org/linux/man-pages/man2/socket.2.html

通用socket地址:sockaddr和sockaddr_storage

sockaddr定义如下:

#include <bits/socket.h>
struct sockaddr 
    sa_family_t sa_family;    //地址族类型 
    char sa_data[14];    //socket地址值
;

由于sockaddr里面只能存放14个字节,明显不够用!因此sockaddtr_stroage就用了作用:

#include<bits/socket.h>
struct sockaddr_storage

    sa_family_t sa_family;
    unsigned long int__ss_align;
    char__ss_padding[128-sizeof(__ss_align)];

Unix专用socket地址:sockaddr_un

#include <sys/un.h>
strcut sockaddr_un 
    sa_family_t sin_family;    //地址族
    char sun_path[108];    // 文件路径名
;

TCP/IP专用socket地址:sockaddr_in和sockaddr_in6

sockaddr_in对应IPv4,sockaddr_in6对应IPv6

struct sockaddr_in

    sa_family_t sin_family;/*地址族: AF_INET*/
    u_int16_t sin_port;/*端口号, 要用网络字节序表示*/
    struct in_addr sin_addr;/*IPv4地址结构体, 见下面*/
;
struct sockaddr_in6

    sa_family_t sin6_family;/*地址族: AF_INET6*/
    u_int16_t sin6_port;/*端口号, 要用网络字节序表示*/
    u_int32_t sin6_flowinfo;/*流信息, 应设置为0*/
    struct in6_addr sin6_addr;/*IPv6地址结构体, 见下面*/
    u_int32_t sin6_scope_id;/*scope ID*/
;

注意:实际使用的过程中,所有专用socket地址类型都需要强制转换成sockaddr,统一类型。

IP地址转换函数:inet_addr、 inet_aton、inet_ntoa

 inet_addr函数将用点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址:

#include<arpa/inet.h>
in_addr_t inet_addr(const char*strptr);

inet_aton函数完成和inet_addr同样的功能, 但是将转化结果存储于参数inp指向的地址结构中。 它成功时返回1, 失败则返回0。

#include<arpa/inet.h>
int inet_aton(const char*cp,struct in_addr*inp);

inet_ntoa函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址。

#include<arpa/inet.h>
char*inet_ntoa(struct in_addr in);

创建socket:socket()

#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain,int type,int protocol);

参数释义:

1、domain:告诉系统使用哪个底层协议族。 对TCP/IP协议族而言, 该参数应该设置为PF_INET(Protocol Family of Internet, 用于IPv4) 或PF_INET6(用于IPv6) ; 对于UNIX本地域协议族而言, 该参数应该设置为PF_UNIX。
2、type:指定服务类型。 服务类型主要有SOCK_STREAM服务(流服务) 和SOCK_UGRAM(数据报) 服务。 对TCP/IP协议族而言, 值取SOCK_STREAM表示传输层使用TCP协议, 取SOCK_DGRAM表示传输层使用UDP协议。
3、protocol:是在前两个参数构成的协议集合下, 再选择一个具体的协议。 不过这个值通常都是唯一的(前两个参数已经完全决定了它的值) 。 一般设置为0, 表示使用默认协议。

函数返回:

调用成功时返回一个socket文件描述符, 失败则返回-1并设置errno。

绑定socket地址:bind()

 只有服务端需要绑定地址,客户端使用系统自动分配的匿名地址。

#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr*my_addr,socklen_t addrlen);

bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符, addrlen参数指出该socket地址的长度。

函数返回:

bind成功时返回0, 失败则返回-1并设置errno。 其中两种常见的errno是EACCES和EADDRINUSE。

  • EACCES, 被绑定的地址是受保护的地址, 仅超级用户能够访问。 比如普通用户将socket绑定到知名服务端口(端口号为0~1023) 上时, bind将返回EACCES错误。
     
  • EADDRINUSE, 被绑定的地址正在使用中。 比如将socket绑定到一个处于TIME_WAIT状态的socket地址。

监听socket地址:listen()

创建一个监听队列以存放待处理的客户连接:

#include<sys/socket.h>
int listen(int sockfd,int backlog);

sockfd参数指定被监听的socket。 backlog参数提示内核监听队列的最大长度。 监听队列的长度如果超过backlog, 服务器将不受理新的客户连接, 客户端也将收到ECONNREFUSED错误信息。listen成功时返回0, 失败则返回-1并设置errno。

接受socket连接:accept()

从listen监听队列中接受一个连接:

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr*addr,socklen_t*addrlen);

sockfd参数是执行过listen系统调用的监听socket。 addr参数用来获取被接受连接的远端socket地址, 该socket地址的长度由addrlen参数指出。 accept成功时返回一个新的连接socket, 该socket唯一地标识了被接受的这个连接, 服务器可通过读写该socket来与被接受连接对应的客户端通信。 accept失败时返回-1并设置errno。

注意:accept只是从监听队列中取出连接, 而不论连接处于何种状态(如上面的ESTABLISHED状态和CLOSE_WAIT状态) , 更不关心任何网络状况的变化。

发起socket连接:connect()

客户端使用connect()与服务端建立连接:

#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr*serv_addr,socklen_t addrlen);

sockfd参数由socket系统调用返回一个socket。 serv_addr参数是服务器监听的socket地址, addrlen参数则指定这个地址的长度。

函数返回:

connect成功时返回0。 一旦成功建立连接, sockfd就唯一地标识了这个连接, 客户端就可以通过读写sockfd来与服务器通信。 connect失败则返回-1并设置errno。 其中两种常见的errno是ECONNREFUSED和ETIMEDOUT, 它们的含义如下:

  • ECONNREFUSED, 目标端口不存在, 连接被拒绝。
  • ETIMEDOUT, 连接超时。

关闭socket连接:close()

关闭普通文件描述符的系统调用:

#include<unistd.h>
int close(int fd);

fd参数是待关闭的socket。 不过,close系统调用并非总是立即关闭一个连接, 而是将fd的引用计数减1。只有当fd的引用计数为0时, 才真正关闭连接。 多进程程序中, 一次fork系统调用默认将使父进程中打开的socket的引用计数加1, 因此我们必须在父进程和子进程中都对该socket执行close调用才能将连接关闭。

立即终止socket连接:shutdown()

#include<sys/socket.h>
int shutdown(int sockfd,int howto);

howto参数决定了shutdown的行为,具体参看手册。

TCP数据读写:recv()、send()

TCP是流协议,recv()和send()用于读写缓冲区:

#include<sys/types.h>
#include<sys/socket.h>
ssize_t recv(int sockfd,void*buf,size_t len,int flags);
ssize_t send(int sockfd,const void*buf,size_t len,int flags);

recv读取sockfd上的数据, buf和len参数分别指定读缓冲区的位置和大小, flags参数通常设置为0即可。

recv成功时返回实际读取到的数据的长度, 它可能小于我们期望的长度len。 因此我们可能要多次调用recv, 才能读取到完整的数据

send往sockfd上写入数据, buf和len参数分别指定写缓冲区的位置和大小。 send成功时返回实际写入的数据的长度, 失败则返回-1并设置errno。

综合应用实例:带外数据

https://xduwq.blog.csdn.net/article/details/119182975

参考:

手把手写c++服务器:专栏文章-汇总导航更新中

手把手写C++服务器(1):网络编程常见误区手把手写C++服务器(2):C/C++编译链接模型、函数重载隐患、头文件使用规范手把手写C++服务器(3):C++编译常见问题、编译优化方法、C++库发... 查看详情

手把手写c++服务器(22):linuxsocket网络编程进阶第一弹(代码片段)

前言:前面一篇文章手把手写C++服务器(21):Linuxsocket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹&... 查看详情

手把手写c++服务器(22):linuxsocket网络编程进阶第一弹(代码片段)

前言:前面一篇文章手把手写C++服务器(21):Linuxsocket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹&... 查看详情

手把手写c++服务器(26):常用i/o操作创建文件描述符

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:通过上一篇文章(手把手写C++服务器(25):万物皆可文件之socketfd),Linux万物皆文件的一定深入人心。如何操作这些文件?I/O函数将震撼登场!第一... 查看详情

手把手写c++服务器(33):linux常用命令合集

 本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:服务端编程的过程当中,各种常用的命令行也会大量使用;熟悉常用Linux命令不仅仅是运维的基本要求,也是一个主程的基本门槛。这里... 查看详情

手把手写c++服务器(27):五大文件描述符零拷贝控制总结(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》、《手把手写C++服务器(25):万物皆可文件之sock... 查看详情

手把手写c++服务器(28):手撕cgi通用网关接口服务器代码(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文《手把手写C++服务器(26):常用I/O操作、创建文件描述符》《手把手写C++服务器(27):五大文件描述符零拷... 查看详情

手把手写c++服务器(29):手撕echo回射服务器代码(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:上一讲《手把手写C++服务器(28):手撕CGI通用网关接口服务器代码》讲解了如何利用复制文件描述符dup重定位标准输出&#x... 查看详情

手把手写c++服务器(30):手撕代码——基于tcp/ip的抛弃服务discard(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前面两讲讲了echo服务器和CGI网关服务器《手把手写C++服务器(29):手撕echo回射服务器代码》《手把手写C++服务器(2... 查看详情

手把手写c++服务器(32):三大事件之信号详解(代码片段)

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:信号实际上是一种软中断,信号机制实际上是进程间通信的一种方式。状态改变、系统异常、系统状态的变化等等,这些是... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这几个一次只能监听... 查看详情

手把手写c++服务器(23):必知必会!操作系统面试十连问

前言:系列文章手把手写C++服务器(17):自测!TCP协议面试经典十连问受到了大家的收藏和点赞,谢谢读者的支持。操作系统是后端开发工程师的童子功,掌握常见的操作系统基础知识不仅有助于我们日... 查看详情

手把手写c++服务器(34):高并发高吞吐io秘密武器——epoll池化技术两万字长文(代码片段)

本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:前文手把手写C++服务器(31):服务器性能提升关键——IO复用技术【两万字长文】介绍了IO复用技术,其中重点比较了... 查看详情

手把手写c++服务器(17):自测!tcp协议面试经典十连问(代码片段)

前言:前面一篇文章《手把手写C++服务器(15):网络编程入门第一个TCP项目》介绍了一个简单入门级的TCP项目,这一篇文章重点讲一讲面试常见的TCP协议相关的十个问题,都是后端开发程序员必知必会的经典... 查看详情

手把手写c++服务器(17):自测!tcp协议面试经典十连问(代码片段)

前言:前面一篇文章《手把手写C++服务器(15):网络编程入门第一个TCP项目》介绍了一个简单入门级的TCP项目,这一篇文章重点讲一讲面试常见的TCP协议相关的十个问题,都是后端开发程序员必知必会的经典... 查看详情

手把手写c++服务器(31):服务器性能提升关键——io复用技术两万字长文(代码片段)

 本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:Linux中素有“万物皆文件,一切皆IO”的说法。前面几讲手撕了CGI网关服务器、echo回显服务器、discard服务的代码,但是这... 查看详情

手把手写c++服务器(24):socket响应一般框架tcp修改缓冲区内核监听listen最大长度(代码片段)

本系列文章导航:手把手写C++服务器(0):专栏文章-汇总导航【更新中】 前言:本系列文章手把手写C++服务器(15):网络编程入门第一个TCP项目以封装好的网络库为例,重点讲解了如何正确的建立TCP... 查看详情

手把手写c++服务器(38):面试必背!linux网络socket编程必会十问!(代码片段)

 本系列文章导航: 手把手写C++服务器(0):专栏文章-汇总导航【更新中】目录1、说一下客户端和服务端socket建立连接和关闭连接的过程2、如何将一个socket设置成非阻塞模式3、什么是socket三大属性?4、阻塞模... 查看详情