手把手写C++服务器(22):Linux socket网络编程进阶第一弹

Posted 沉迷单车的追风少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手写C++服务器(22):Linux socket网络编程进阶第一弹相关的知识,希望对你有一定的参考价值。

前言:前面一篇文章手把手写C++服务器(21):Linux socket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹,这一讲主要熟悉如何TCP、UDP读写以及通用数据读写,如何操作网络地址,socket选项设置等,进一步熟悉linux网络编程。

目录

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

UDP数据读写:recvfrom()、sendto()

通用数据读写:recvmsg()、sendmsg()

地址信息函数:getsockname()、getpeername()

socket选项

常用选项选讲:

获取服务完整信息:getserverbyname()、getserverbyport()

参考


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。

flags参数:一般设0,其他数值定义如下 MSG_OOB 传送的数据以out-of-band 送出。 MSG_DONTROUTE 取消路由表查询 MSG_DONTWAIT 设置为不可阻断运作 MSG_NOSIGNAL 此动作不愿被SIGPIPE 信号中断。

UDP数据读写:recvfrom()、sendto()

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, int void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);

recvfrom:用于读取sockfd上面的数据;

sendto:往sockfd上写入数据;

buf:缓冲区的位置

len:缓冲区大小

相比于tcp的两个API recv()和send(),参数多了地址src_addr和地址的长度addrlen,这是因为UDP没有连接的概念,每次读取数据都需要获取发送端的socket地址。

通用数据读写:recvmsg()、sendmsg()

通用的意思是TCP和UDP都能使用

#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags);
ssize_r sendmsg(int sockfd, struct msghdr* msg, int flags);

msghdr结构的定义:

#include<sys/socket.h>
struct msghdr  { 
    void  * msg_name ;   / *  消息的协议地址  * / 协议地址和套接口信息,在非连接的UDP中,发送者要指定对方地址端口,接受方用于的到数据来源,如果不需要的话可以设置为NULL(在TCP或者连接的UDP中,一般设置为NULL)
    socklen_t msg_namelen ;   / *  地址的长度  * / 
    struct iovec  * msg_iov ;   / *  多io缓冲区的地址  * / 
     int  msg_iovlen ;   / *  缓冲区的个数  * / 
    void  * msg_control ;   / *  辅助数据的地址  * / 
    socklen_t msg_controllen ;   / *  辅助数据的长度  * / 
     int  msg_flags ;   / *  接收消息的标识  * / 
} ;

地址信息函数:getsockname()、getpeername()

#include <sys/socket.h>
int getsockname(int sockfd, struct sockaddr* address, socklen_t* address_len);
int getpeername(int sockfd, struct sockaddr* address, socklen_t* address_len);

getsockname():获取sockfd对应的本端socket地址,并将其存储与address参数指定内存中。如果实际socket地址长度大于address指定内存中的大小,那么该socket地址将被截断 。

getpeername():获取sockfd对应的远端socket地址,用法和getsockname类似。

socket选项

读取和设置文件描述符的属性和方法。

#include <sys/socket.h>
int getsockopt(int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len);
int setsockopt(int sockfd, int level, int option_name, const void* option_value, socklen_t* restrict option_len);

level:指定属性,如IPv4、IPv6、TCP等

具体选项参数含义:https://pubs.opengroup.org/onlinepubs/7908799/xns/getsockopt.html

#define SOL_IP 0

#define SOL_IPX 256

#define SOL_AX25 257

#define SOL_ATALK 258

#define SOL_NETROM 259

#define SOL_TCP 6

#define SOL_UDP 17

#define SOL_SOCKET 0xffff

常用选项选讲:

1、SO_REUSEADDR

当TCP连接处于TIME_WAIT状态的时候,SO_REUSEADDR来强制使用被处于TIME_WAIT状态的连接占用的socket地址。使该地址能立即被重用。

2、SO_RCVBUF

TCP接收缓冲区大小

3、SO_SNDBUF

TCP发送缓冲区大小

4、SO_RCVLOWAT

TCP接收缓冲区低水位标记,被I/O复用系统调用用来判断socket是否可写。

5、SO_SNDLOWAT

TCP发送缓冲区低水位标记,被I/O复用系统调用用来判断socket是否可写。

6、SO_LINGER

控制close系统调用在关闭TCP连接时的行为

获取服务完整信息:getserverbyname()、getserverbyport()

#include <netdb.h>
struct servent* getserverbyname(const char* name, const char* proto);
struct servent* getserverbyport(int port, const char* proto);

getserverbyname:根据名称获取某个服务的完整信息。

getserverbyport:根据端口号获取某个服务的完整信息。

两个函数都是通过读取/etc/services文件来获取服务信息的。

参数name指定目标服务的名字;port指定端口号;proto指定服务类型,传递tcp表示获取流服务,传递udp表示获取数据报服务,传递NULL表示获取所有类型的服务。

返回servent结构体定义如下:

#include <netdb.h>
struct servent {
    char* s_name;    // 服务名称
    char** s_aliases;// 服务别名列表
    int s_port;      // 端口号
    char* s_proto;   // 服务类型
};

参考

以上是关于手把手写C++服务器(22):Linux socket网络编程进阶第一弹的主要内容,如果未能解决你的问题,请参考以下文章

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

手把手写C++服务器(21):Linux socket网络编程入门基础

手把手写C++服务器:Linux四大必备网络分析工具

手把手写C++服务器:Linux四大必备网络分析工具

手把手写C++服务器(38):面试必背!Linux网络socket编程必会十问!

手把手写C++服务器(26):常用I/O操作创建文件描述符