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. 当程序 ...
随机推荐
- pythont多线程
import threading # 线程模块 def function(num): pass thread_1 = threading.Thread(target=function, args=(1 ...
- VS调试出现解决 尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题
- 学习 | jQuery全屏滚动插件FullPage.js
简介 fullPage.js是一个基于jQuery的全屏滚动插件,它能够很方便.很轻松的制作出全屏网站. 主要功能 支持鼠标滚动 支持前进后退和键盘控制 多个回调函数 支持手机.平板触摸事件 支持 C ...
- k8s通过service访问pod(五)
service 每个 Pod 都有自己的 IP 地址.当 controller 用新 Pod 替代发生故障的 Pod 时,新 Pod 会分配到新的 IP 地址.这样就产生了一个问题: 如果一组 Pod ...
- nioServerChannel的的状态
转载自https://blog.csdn.net/zxhoo/article/details/17964353 Channel继承层次图分析上面提到的三个状态的时候,会去看Channel继承层次里某些 ...
- 【转】Locust 性能测试-小案例(1)-环境搭建
说在前面的话:从这节课开始,将讲解Locust作为一款测试工具,要怎么去应用.首先是"小案例"的系列文章,主要是给大家讲解locustfile也就是场景模拟的一些模式和方法.等到& ...
- 深入研究RocketMQ生产者发送消息的底层原理
前言 hello,小伙伴们,王子又来和大家研究RocketMQ的原理了,之前的文章RocketMQ生产部署架构如何设计中,我们已经简单的聊过了生产者是如何发送消息给Broker的. 我们简单回顾一下这 ...
- 深入理解HBase
深入理解HBase: https://www.jianshu.com/p/b23800d9b227
- 刷题[BJDCTF2020]Mark loves cat
解题思路 打开网页,发现是一个博客,基本寻找博客挂载信息,源码等无果后,扫描后台.发现.git泄露 .git泄露 发现.git泄露后,使用Git Extract这款工具,可自动将源码clone到本地 ...
- RabbitMQ小记(二)
1.RabbitMQ相关介绍 (1)RabbitMQ整体上是一个生产者和消费者模型,主要负责接收.存储.转发消息.RabbitMQ整体结构图如下: (2)生产者:发送消息的一方,生产者创建一条消息,发 ...