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接口的更多相关文章

  1. Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差

    Nginx 中 fastcgi_pass 监听端口 unix socket和tcp socket差别   Nginx连接fastcgi的方式有2种:unix domain socket和TCP,Uni ...

  2. socket接口详解

    1. socket概述 socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket起源于UNIX,在Unix一切 ...

  3. 【转】nginx 和 php-fpm 通信使用unix socket还是TCP,及其配置

    原文: http://blog.csdn.net/pcyph/article/details/46513521 -------------------------------------------- ...

  4. UNIX SOCKET编程简介

    1  .  Layered Model of Networking Socket  编程的层次模型如下图所示,   最上面是应用层,应用层下面的是  SOCKET API  层,再下面是传输层和网络层 ...

  5. 基于Unix Socket的可靠Node.js HTTP代理实现(支持WebSocket协议)

    实现代理服务,最常见的便是代理服务器代理相应的协议体请求源站,并将响应从源站转发给客户端.而在本文的场景中,代理服务及源服务采用相同技术栈(Node.js),源服务是由代理服务fork出的业务服务(如 ...

  6. Windows Socket 接口简介

    Windows Socket接口是Windows下网络编程的接口,在介绍Windows Socket接口之前,首先要简单介绍一下TCP/IP协议和描述网络系统架构的 OSI模型,以及TCP/IP模型 ...

  7. Another MySQL daemon already running with the same unix socket的解决

    问题出现: 每周一需要备份一次数据库,即从服务器MySQL导出sql文件,再导入到我机器上虚拟机的MySQL里.但是今天早上连不上,我进入控制台用#service mysqld start强行启动,报 ...

  8. 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 特征 ...

  9. UNIX系统接口

    UNIX系统接口 8.1 文件描述符 UNIX操作系统中,所有的外围设备(包括键盘和显示器)都被看作是文件系统中的文件.系统通过文件描述符来标识文件:标准输入为0,标准输出为1,标准错误为2. 当程序 ...

随机推荐

  1. java 二维码生成(可带图片)springboot版

    本文(2019年6月29日 飞快的蜗牛博客) 有时候,男人和女人是两个完全不同的世界,男人的玩笑和女人的玩笑也完全是两码事,爱的人完全不了解你,你也不要指望一个女人了解你,所以男的不是要求别人怎么样, ...

  2. python中unittest参数化

    先安装 pip install parameterized 在测试用例类中,@parameterized.parameterized.expand(这里面传一个列表),这个就是参数化的意思

  3. [LeetCode]647. 回文子串(DP)

    ###题目 给定一个字符串,你的任务是计算这个字符串中有多少个回文子串. 具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串. 示例 1: 输入: "abc&q ...

  4. [HDU2577]How to Type(DP)

    题目链接 题意 给一个大小写字符串,求最少敲击字符串次数,最开始和最后要求shift都是down的.如日常,大小写转换可以ctrl+z或者shift保持 up/down. 题解 两个dp数组,一个表示 ...

  5. python中库引用与import

    在蟒蛇绘制函数中,多有turtle.   ,称它为<a>.<b>的编码风格 库引用 扩充python程序功能的方式 使用import保留字完成,采用<a>.< ...

  6. windows下搭建ElasticSearch

      1.官网下载ElasticSearch,需要java环境支持       地址:https://www.elastic.co/products/elasticsearch   2.下载后解压到目录 ...

  7. 自己实现一个 DFA 串模式识别器(二)

    正规表达式的实现原理 ​ 上文讨论了串的模式的表达,即正规表达式.那么这一小节将讨论我们实现一个正规表达式的方法和原理.因为我们知道,一个正规表达式对应着一个串模式,而一个串模式又对应着一些列符合该模 ...

  8. 刷题[FBCTF2019]Event

    解题思路 信息收集 打开发现是这样的登陆框,信息泄露,弱口令什么的尝试一下,无果,正常注册登陆 发现需要通过admin用户登陆,并且发现有/flag这样的路由,猜测后台为python编写 抓包发现有看 ...

  9. Mac Catalina 下 gdb codesign问题解决

    在 macOS 上,无法直接使用 gdb 进行 debug. 这是因为 Darwin 内核在你没有特殊权限的情况下,不允许调试其它进程.调试某个进程,意味着你对这个进程有完全的控制权限,所以为了防止被 ...

  10. 解决 SecureCRT 和 SecureFX 中文乱码

    引言 最近老是有小伙伴给我发消息说,下载的 SecureCRT 和 SecureFX 安装打开后连接了自己的服务器或虚拟机后会出现中文乱码,每次都要给一一回复,我倒没事,主要是有时候因为工作的原因,所 ...