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. leetcode刷题-75颜色分类

    题目 给定一个包含红色.白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色.白色.蓝色顺序排列. 此题中,我们使用整数 0. 1 和 2 分别表示红色.白色和蓝 ...

  2. JVM性能调优(1) —— JVM内存模型和类加载运行机制

    一.JVM内存模型 运行一个 Java 应用程序,必须要先安装 JDK 或者 JRE 包.因为 Java 应用在编译后会变成字节码,通过字节码运行在 JVM 中,而 JVM 是 JRE 的核心组成部分 ...

  3. db2错误代码可能的原因

    一. SQL5005C运行cmd未用管理员权限打开 SQLCODE-303  数据类型不兼容 SQLCODE-305   查出的数据有NULL未处理直接写入接收变量 二.应该不会有人直接写sql用这个 ...

  4. selenium中各个模块操作:下拉框、鼠标悬浮连贯、拼图拖拽操作、以及其他拖拽操作、连线操作

    1.下拉框的修改操作 方法一:定位到元素后,通过select选择对应的值 方法二:通过两次点击的方法:没有select的value属性时,采用click两次的方法去选择: click第一次后,出现下拉 ...

  5. 秒懂JVM的三大参数类型,就靠这十个小实验了

    秒懂JVM的三大参数类型,就靠这十个小实验了 你好,我是悟空哥,「7年项目开发经验,全栈工程师,开发组长,超喜欢图解编程底层原理」.手写了2个小程序,Java刷题小程序,PMP刷题小程序,已发布到公众 ...

  6. [LeetCode]21. 合并两个有序链表(递归)

    题目 将两个有序链表合并为一个新的有序链表并返回.新链表是通过拼接给定的两个链表的所有节点组成的. 示例: 输入:1->2->4, 1->3->4 输出:1->1-> ...

  7. Video.js + HLS 在production环境下webpack打包后出错的解决方案

    Video.js是一个非常强大的视频播放库,能在微信下完美提供inline小窗口播放模式,但当涉及到hls格式视频播放时就比较麻烦,出现的数种现象都不好解决. 错误现象:  1.  PC Chrome ...

  8. day53:django:URL别名/反向解析&URL分发&命名空间&ORM多表操作修改/查询

    目录 1.URL别名&反向解析 2.URL分发&命名空间 3.ORM多表操作-修改 4.ORM多表操作-查询 4.1 基于对象的跨表查询 4.2 基于双下划线的跨表查询 4.3 聚合查 ...

  9. nginx upstream一致性哈希的实现

    地址:http://wiki.nginx.org/HttpUpstreamConsistentHash 首先声明一个命令: static ngx_command_t  ngx_http_upstrea ...

  10. Unity 如何在窗口大小可以随意改变的情况下让游戏世界完整的显示在镜头中

    当我们开发游戏时,如果是开发手机游戏,屏幕窗口的比例是固定的,不会说在运行时改变的. 但是,PC端的游戏就不一定,我希望它能被用户随意拉扯,但完整的内容还是能显示出来,这里我直接放例子: 请注意黑色的 ...