unix socket接口
socket
创建套接字文件:
#include <sys/socket.h>
// 成功返回非负套接字描述符,失败返回-1
int socket(int domain, int type, int protocol);
domain值:
domain | 描述 |
---|---|
AF_INET | IPv4 Internet protocols |
AF_INET6 | IPv6 Internet protocols |
type值:
type | 描述 |
---|---|
SOCK_STREAM | Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported. |
SOCK_DGRAM | Supports datagrams (connectionless, unreliable messages of a fixed maximum length). |
protocol值:
protocol | 描述 |
---|---|
IPPROTO_TCP | TCP协议 |
IPPROTO_UDP | UDP协议 |
socket里面并没有定义protocal的宏,需要额外引入<netinet/in.h>
sockaddr
sockaddr用来记录ip和端口信息,sockaddr是通用的结构体,没有具体划分ip和端口的信息字段,对于ipv4地址需要用sockaddr_in结构体,对于ipv6需要用sockaddr_in6结构体:
<sys/socket.h>
// 通用地址信息结构体
struct sockaddr {
sa_family_t sa_family; // 地址簇
char sa_data[14]; // 填充字符
};
<netinet/in.h>
// 存储ipv4地址
struct in_addr {
in_addr_t s_addr; // ipv4地址(网络序4字节)
};
// ipv4地址和端口信息
struct sockaddr_in {
sa_family_t sin_family; // AF_INET
in_port_t sin_port; // 端口号(网络序2字节)
struct in_addr sin_addr; // ipv4地址
char sin_zero[8]; // 填充字符
};
// 存储ipv6地址
struct in6_addr {
uint8_t s6_addr[16]; // ipv6地址(网络序16字节)
};
// ipv6地址和端口信息
struct sockaddr_in6 {
sa_family_t sin6_family; // AF_INET6
in_port_t sin6_port; // 端口号(网络序2字节)
uint32_t sin6_flowinfo; // 流信息
struct in6_addr sin6_addr; // ipv6地址
uint32_t sin6_scope_id; // 作用域的接口集合
};
在用sockaddr_in结构体之前,需要清零整个结构体。
将字符串转换为ip地址类型可以用inet_pton,转换成功返回1:
<netinet/in.h>
// 转换成功返回1,ip字符串不合法返回0,地址簇不支持返回-1
int inet_pton(int af, const char *src, void *dst);
除了通过字符串转换ip地址之外,<netinet/in.h>
头文件定义了INADDR_ANY
来表示0.0.0.0
这个通配地址。
bind
将socket套接字绑定到指定ip地址和端口上:
#include <sys/socket.h>
// 失败返回-1,成功返回0
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
对于服务器来说,如果要同时监听多张网卡,那么就需要绑定到INADDR_ANY
上,如果只接受本机访问,那么需要绑定到127.0.0.1
上。
listen
将服务器的套接字描述符状态从CLOSED
转移到LISTEN
:
#include <sys/socket.h>
// 失败返回-1,成功返回0
int listen(int sockfd, int backlog);
backlog
表示连接队列的大小,虽然标准中并没有说明,但BSD4.2的实现中,连接队列里面包括两种状态的连接:
- 一部分连接刚接收到SYN包,处于
SYN_RCVD
状态。 - 一部分连接已经完成了三次握手过程,处于
ESTABLISHED
状态
不能将backlog设置为0,因为这一行为标准未定义,如果不想客户端可以连接自己,那么需要关闭该监听套接字。并且实际的连接队列的大小不一定等于backlog
,比如有的实现会乘以一个系数1.5。
accept
用于接受连接队列中处于ESTABLISHED
状态的连接:
#include <sys/socket.h>
// 成功返回非负的连接套接字描述符,失败返回-1
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
cliaddr可以用来获取客户端的地址和端口信息。
#include <arpa/inet.h>
// 转换成功返回dst指针,转换失败返回NULL
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
<netinet/in.h>
头文件定义了保存ipv4地址和ipv6字符串所需的字符数组长度:
#define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */
#define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */
connect
客户端用来连接服务端:
#include <sys/socket.h>
// 失败返回-1,成功返回0
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
当客户端调用该接口的时候,就开始TCP三次握手,首先发送SYN包,若:
- 一直没有收到回复,那么会触发超时机制(75秒),在此期间会不断重发SYNC包,直到返回ETIMEOUT错误
- 如果服务主机返回RST(reset),那么表示服务主机上该端口没有服务在监听,那么客户端返回ECONNREFUSED错误
- 如果返回ICMP包,表示网络或主机不可达,那么还是会触发超时机制,在此期间会不断重发SYNC包,直到返回EHOSTUNREACH错误
客户端代码
#include <iostream>
#include <string>
#include <cstring> // memset strlen
#include <cstdint> // uint16_t
#include <cstdio> // snprintf
#include <sys/socket.h> // socket
#include <netinet/in.h> // IPPROTO_TCP htons
#include <arpa/inet.h> // inet_pton
#include <unistd.h> // write read
int main(int argc, char const *argv[])
{
// 创建连接套接字
int connectfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == connectfd) {
std::cout << "create connectfd failed" << std::endl;
return -1;
}
std::cout << "connectfd: " << connectfd << std::endl;
// 设置服务器地址(ipv4)
sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
std::string servIP = "127.0.0.1";
uint16_t servPort = 23333;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(servPort);
int res = inet_pton(AF_INET, servIP.c_str(), &servAddr.sin_addr);
if (1 != res) {
std::cout << "presentation(" << servIP << ") to numeric failed" << std::endl;
return -1;
}
std::cout << "set server Address success" << std::endl;
// 连接服务器
res = connect(connectfd, reinterpret_cast<sockaddr *>(&servAddr), sizeof(servAddr));
if (-1 == res) {
std::cout << "connect server failed" << std::endl;
return -1;
}
std::cout << "connect server success" << std::endl;
// 体验echo服务
while (true) {
// 获取一行输入
std::string line;
getline(std::cin, line);
if ("quit" == line) {
break;
}
// 发送消息
ssize_t nLeft = line.size();
const char *curStr = line.c_str();
while (0 != nLeft) {
ssize_t nWrite = write(connectfd, curStr, nLeft);
if (nWrite < 0) {
std::cout << "write connectfd failed" << std::endl;
break;
}
nLeft -= nWrite;
curStr += nWrite;
}
if (0 != nLeft) {
break;
}
// 输出回复
constexpr int MAX = 1024;
char buf[MAX + 1];
ssize_t nRead = read(connectfd, buf, sizeof(buf));
if (nRead < 0) {
std::cout << "read connectfd failed" << std::endl;
break;
}
buf[nRead] = '\0';
std::cout << buf << std::endl;
}
// 关闭套接字
close(connectfd);
return 0;
}
服务端代码:
#include <iostream>
#include <string>
#include <cstring> // memset
#include <cstdint> // uint16_t
#include <sys/socket.h> // socket
#include <netinet/in.h> // IPPROTO_TCP htons ntohs INET_ADDRSTRLEN
#include <arpa/inet.h> // inet_pton inet_ntop
#include <unistd.h> // write read
int main(int argc, char const *argv[])
{
// 创建监听socket
int listenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == listenfd) {
std::cout << "create listenfd failed" << std::endl;
return -1;
}
std::cout << "listenfd: " << listenfd << std::endl;
// 设置服务器地址(ipv4)
sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
std::string servIP = "0.0.0.0";
uint16_t servPort = 23333;
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(servPort);
int res = inet_pton(AF_INET, servIP.c_str(), &servAddr.sin_addr);
if (1 != res) {
std::cout << "presentation(" << servIP << ") to numeric failed" << std::endl;
return -1;
}
std::cout << "set server Address success" << std::endl;
// 绑定服务器地址
res = bind(listenfd, reinterpret_cast<sockaddr*>(&servAddr), sizeof(servAddr));
if (0 != res) {
std::cout << "bind listenfd to server address failed" << std::endl;
return -1;
}
std::cout << "bind listenfd to server address success" << std::endl;
// 套接字开启监听
res = listen(listenfd, 5);
if (0 != res) {
std::cout << "listen listenfd failed" << std::endl;
return -1;
}
std::cout << "listen listenfd success" << std::endl;
// 服务循环
while (true) {
// 接受远端连接
sockaddr_in clientAddr;
socklen_t len = sizeof(clientAddr);
int connectfd = accept(listenfd, reinterpret_cast<sockaddr*>(&clientAddr), &len);
if (-1 == connectfd) {
std::cout << "accept connectfd failed" << std::endl;
return -1;
}
char clientIP[INET_ADDRSTRLEN];
uint16_t clientPort = ntohs(clientAddr.sin_port);
inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, sizeof(clientIP));
std::cout << "accept connectfd(" << clientIP << ":" << clientPort << ") success" << std::endl;
// echo服务
while (true) {
// 接受消息
constexpr int MAX = 1024;
char buf[MAX + 1];
ssize_t nRead = read(connectfd, buf, sizeof(buf));
if (nRead < 0) {
std::cout << "read connectfd failed" << std::endl;
break;
} else if (0 == nRead) {
std::cout << "read EOF" << std::endl;
break;
}
buf[nRead] = '\0';
std::cout << buf << std::endl;
// echo消息
ssize_t nLeft = strlen(buf);
const char *curStr = buf;
while (0 != nLeft) {
ssize_t nWrite = write(connectfd, curStr, nLeft);
if (nWrite < 0) {
std::cout << "write connectfd failed" << std::endl;
break;
}
nLeft -= nWrite;
curStr += nWrite;
}
if (0 != nLeft) {
break;
}
}
// 结束本轮服务
close(connectfd);
}
return 0;
}
unix socket接口的更多相关文章
- Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差
Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别 Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...
- socket接口详解
1. socket概述 socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket起源于UNIX,在Unix一切 ...
- 【转】nginx 和 php-fpm 通信使用unix socket还是TCP,及其配置
原文: http://blog.csdn.net/pcyph/article/details/46513521 -------------------------------------------- ...
- UNIX SOCKET编程简介
1 . Layered Model of Networking Socket 编程的层次模型如下图所示, 最上面是应用层,应用层下面的是 SOCKET API 层,再下面是传输层和网络层 ...
- 基于Unix Socket的可靠Node.js HTTP代理实现(支持WebSocket协议)
实现代理服务,最常见的便是代理服务器代理相应的协议体请求源站,并将响应从源站转发给客户端.而在本文的场景中,代理服务及源服务采用相同技术栈(Node.js),源服务是由代理服务fork出的业务服务(如 ...
- Windows Socket 接口简介
Windows Socket接口是Windows下网络编程的接口,在介绍Windows Socket接口之前,首先要简单介绍一下TCP/IP协议和描述网络系统架构的 OSI模型,以及TCP/IP模型 ...
- Another MySQL daemon already running with the same unix socket的解决
问题出现: 每周一需要备份一次数据库,即从服务器MySQL导出sql文件,再导入到我机器上虚拟机的MySQL里.但是今天早上连不上,我进入控制台用#service mysqld start强行启动,报 ...
- Unable to start MySQL service. Another MySQL daemon is already running with the same UNIX socket
Unable to start MySQL service. Another MySQL daemon is already running with the same UNIX socket 特征 ...
- UNIX系统接口
UNIX系统接口 8.1 文件描述符 UNIX操作系统中,所有的外围设备(包括键盘和显示器)都被看作是文件系统中的文件.系统通过文件描述符来标识文件:标准输入为0,标准输出为1,标准错误为2. 当程序 ...
随机推荐
- vue父子组件状态同步的最佳方式
哈喽!大家好!我是木瓜太香,一位老牌儿前端工程师,平时我们在使用 vue 开发的时候,可能会遇到需要父组件与子组件某个状态需要同步的情况,通常这个是因为我们封装组件的时候有一个相同的状态外面要用,里面 ...
- Kubernetes 服务部署最佳实践(二) ——如何提高服务可用性
引言 上一篇文章我们围绕如何合理利用资源的主题做了一些最佳实践的分享,这一次我们就如何提高服务可用性的主题来展开探讨. 怎样提高我们部署服务的可用性呢?K8S 设计本身就考虑到了各种故障的可能性,并提 ...
- 快速生成网络mp4视频缩略图技术
背景 由于网络原因,在下载视频之前我们往往会希望能够先生成一些视频的缩略图,大致浏览视频内容,再确定是否应花时间下载.如何能够快速得到视频多个帧的缩略图的同时尽量少的下载视频的内容,是一个值得研究的问 ...
- ctfhub sql注入 整数型注入
整数型注入 手工注入 1.查看是否存在sql注入,及sql注入类型 2.确定列数 3.确定注入点,以及数据库版本,数据库名称 4.查表名 5.查字段名以及flag值 获得flag值 sqlmap做法 ...
- PHP学习路线图(转)
PHP学习路线图 在网上很多人公布了太多的PHP学习路线图,本人在互联网公司工作十余年,也带了很多PHP入门的新手,将他们的一些问题和学习路线图为大家整理出来,希望很多小白少走弯路. 一. 网上某些错 ...
- Anaconda是个什么东东?
2020/5/18 一.Anaconda 有什么用? 是一个安装.管理python相关包的软件,还自带python.Jupyter Notebook.Spyder,有管理包的conda工具,非常有用 ...
- T和Class以及Class的理解
转载自https://blog.csdn.net/witewater/article/details/53462385 首先看下Class类 ,普通的非泛型类Class. 注意:class是java的 ...
- 科普-- 白话HTTPS
HTTPS是传输协议吗? HTTPS与HTTP有什么关系? HTTPS为什么会安全? 闲扯一下 Mac笔记本.Windows台式机.Linux主机.像这三种类型,它们硬件不同,系统不同,服务端处理的编 ...
- Magicodes.IE之导入导出筛选器
总体设计 Magicodes.IE是一个导入导出通用库,支持Dto导入导出以及动态导出,支持Excel.Word.Pdf.Csv和Html.在本篇教程,笔者将讲述如何使用Magicodes.IE的 ...
- 渗透测试方法论(qf总结)
渗透测试(penetration testing , pentest)是实施安全评估(即审计)的具体手段.方法论是在指定.实施信息安全审计方案时,需要遵循的规则.惯例和过程.人们在评估网路.应用.系统 ...