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. 当程序 ...
随机推荐
- Jenkins下Vue自动部署(一)
1,服务器上安装docker http://www.runoob.com/docker/ubuntu-docker-install.html?tdsourcetag=s_pctim_aiomsg 2, ...
- 一文带你了解Sql优化
我们后台开发人员每天都难免与数据库打交道,那么你在写sql语句的时候有注重到自己sql的效率吗?当你sql查询速度很慢的时候你有想过是你的sql语句造成的吗?看完这篇文章,我相信你会对sql优化有了一 ...
- 关于LLVM源码在Linux下编译的过程
1. 最好在gitee上查找llvm的源码或者直接git clone https://gitee.com/uniquesquirrel/llvm-project.git,是和github上的llvm项 ...
- 一起来读官方文档-----SpringIOC(04)
1.4.2.依赖性和详细配置 如上一节所述,您可以将bean属性和构造函数参数定义为对其他托管bean(协作者)的引用或内联定义的值.Spring的基于XML的配置元数据为此目的在其和元素中支持子元素 ...
- 如何用canvas拍出 jDer's工作照
背景 在京东,就职满五年的老员工被称作"大佬",如果满了十年,那就要被称之为"超级大佬"了. 从 2016 年 5 月 19 日开始,每一年的这一天都被定为京东 ...
- Solon详解(七)- Solon Ioc 的注解对比Spring及JSR330
Solon详解系列文章: Solon详解(一)- 快速入门 Solon详解(二)- Solon的核心 Solon详解(三)- Solon的web开发 Solon详解(四)- Solon的事务传播机制 ...
- Centos7,PHP7安装swoole
Swoole详细介绍及如何使用,这里暂时不做说明,可以参考一下文档:https://wiki.swoole.com/ 源码编译安装 下载地址如下: https://github.com/swoole/ ...
- Windows下使用Nginx+Tomact做负载均衡
前言 今天,王子与大家闲谈一下如何在Windows下使用Nginx+Tomcat做负载均衡的完整步骤,小伙伴们可以试着自己动手实践一下哦. 另外说明一点,本篇文章是纯实操文章,不涉及太多原理的解读,后 ...
- Docker:二、开始部署第一个Asp.net应用
各位看官大家好,接着上一篇,我们构建自己的镜像文件成功之后,准备开始部署自己的第一个docker应用了... 接着上文,我们构建自己的镜像,如下是Dockerfile文件 #引入运行环 ...
- 定时任务与feign超时的纠葛,该咋优化?
1 背景 业务定时器应用半夜经常会触发熔断异常的告警邮件 根据邮件提示的类找到归纳以下表格 编号 报错方法 接口所属应用 所属定时任务类 A VipTradeReportFeignService#ge ...