手把手写C++服务器(22):Linux socket网络编程进阶第一弹
Posted 沉迷单车的追风少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手把手写C++服务器(22):Linux socket网络编程进阶第一弹相关的知识,希望对你有一定的参考价值。
前言:前面一篇文章手把手写C++服务器(21):Linux socket网络编程入门基础,讲解了如何建立socket连接、如何转换/使用socket地址、如何绑定/监听/发起/接受/断开/终止/关闭连接。socket博大精深,进阶会多写几弹,这一讲主要熟悉如何TCP、UDP读写以及通用数据读写,如何操作网络地址,socket选项设置等,进一步熟悉linux网络编程。
目录
地址信息函数:getsockname()、getpeername()
获取服务完整信息: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; // 服务类型
};
参考
- https://wizardforcel.gitbooks.io/linux-c-api-ref/content/282.html
- http://www.steves-internet-guide.com/tcpip-ports-sockets/
- 《linux高性能服务器编程》
- https://www.cnblogs.com/wanpengcoder/p/11749287.html
- https://blog.csdn.net/u010020404/article/details/78918937
- https://mp.weixin.qq.com/s/EyQ_RVJcDXgGkCBC-eEkQQ
- https://pubs.opengroup.org/onlinepubs/7908799/xns/getsockopt.html
以上是关于手把手写C++服务器(22):Linux socket网络编程进阶第一弹的主要内容,如果未能解决你的问题,请参考以下文章
手把手写C++服务器(21):Linux socket网络编程入门基础