php socket af unix,unix socket接口(示例代码)

socket

创建套接字文件:

#include

// 成功返回非负套接字描述符,失败返回-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的宏,需要额外引入

sockaddr

sockaddr用来记录ip和端口信息,sockaddr是通用的结构体,没有具体划分ip和端口的信息字段,对于ipv4地址需要用sockaddr_in结构体,对于ipv6需要用sockaddr_in6结构体:

// 通用地址信息结构体

struct sockaddr {

sa_family_t sa_family; // 地址簇

char sa_data[14]; // 填充字符

};

// 存储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:

// 转换成功返回1,ip字符串不合法返回0,地址簇不支持返回-1

int inet_pton(int af, const char *src, void *dst);

除了通过字符串转换ip地址之外,头文件定义了INADDR_ANY来表示0.0.0.0这个通配地址。

bind

将socket套接字绑定到指定ip地址和端口上:

#include

// 失败返回-1,成功返回0

int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

对于服务器来说,如果要同时监听多张网卡,那么就需要绑定到INADDR_ANY上,如果只接受本机访问,那么需要绑定到127.0.0.1上。

listen

将服务器的套接字描述符状态从CLOSED转移到LISTEN:

#include

// 失败返回-1,成功返回0

int listen(int sockfd, int backlog);

backlog表示连接队列的大小,虽然标准中并没有说明,但BSD4.2的实现中,连接队列里面包括两种状态的连接:

一部分连接刚接收到SYN包,处于SYN_RCVD状态。

一部分连接已经完成了三次握手过程,处于ESTABLISHED状态

不能将backlog设置为0,因为这一行为标准未定义,如果不想客户端可以连接自己,那么需要关闭该监听套接字。并且实际的连接队列的大小不一定等于backlog,比如有的实现会乘以一个系数1.5。

accept

用于接受连接队列中处于ESTABLISHED状态的连接:

#include

// 成功返回非负的连接套接字描述符,失败返回-1

int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

cliaddr可以用来获取客户端的地址和端口信息。

#include

// 转换成功返回dst指针,转换失败返回NULL

const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

头文件定义了保存ipv4地址和ipv6字符串所需的字符数组长度:

#define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */

#define INET6_ADDRSTRLEN 46 /* for IPv6 hex string */

connect

客户端用来连接服务端:

#include

// 失败返回-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

#include

#include // memset strlen

#include // uint16_t

#include // snprintf

#include // socket

#include // IPPROTO_TCP htons

#include // inet_pton

#include // 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;

std::string ipAddr = "127.0.0.1";

uint16_t portNum = 23333;

memset(&servAddr, 0, sizeof(servAddr));

servAddr.sin_family = AF_INET;

servAddr.sin_port = htons(portNum);

int res = inet_pton(AF_INET, ipAddr.c_str(), &servAddr.sin_addr);

if (1 != res) {

std::cout << "presentation(" << ipAddr << ") to numeric failed" << std::endl;

return -1;

}

// 连接服务器

res = connect(connectfd, reinterpret_cast(&servAddr), sizeof(servAddr));

if (-1 == res) {

std::cout << "connect server failed" << std::endl;

return -1;

}

// 发送消息

constexpr int MAX = 1024;

char sendBuf[MAX + 1];

snprintf(sendBuf, sizeof(sendBuf), "hello");

write(connectfd, sendBuf, strlen(sendBuf));

// 接受回执

char recvBuf[MAX + 1];

read(connectfd, recvBuf, sizeof(recvBuf));

std::cout << recvBuf << std::endl;

// 关闭套接字

close(connectfd);

return 0;

}

服务端代码:

#include

#include

#include // memset

#include // uint16_t

#include // socket

#include // IPPROTO_TCP htons ntohs INET_ADDRSTRLEN

#include // inet_pton inet_ntop

#include // 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;

std::string ipAddr = "0.0.0.0";

uint16_t portNum = 23333;

memset(&servAddr, 0, sizeof(servAddr));

servAddr.sin_family = AF_INET;

servAddr.sin_port = htons(portNum);

int res = inet_pton(AF_INET, ipAddr.c_str(), &servAddr.sin_addr);

if (1 != res) {

std::cout << "presentation(" << ipAddr << ") to numeric failed" << std::endl;

return -1;

}

std::cout << "set ipv4 address and port number success" << std::endl;

// 绑定服务器地址

res = bind(listenfd, reinterpret_cast(&servAddr), sizeof(servAddr));

if (0 != res) {

std::cout << "bind listenfd failed" << std::endl;

return -1;

}

std::cout << "bind listenfd 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(&clientAddr), &len);

if (-1 == connectfd) {

std::cout << "accept connectfd failed" << std::endl;

return -1;

}

char clientIpAddr[INET_ADDRSTRLEN];

uint16_t clientPortNum = ntohs(clientAddr.sin_port);

inet_ntop(AF_INET, &clientAddr.sin_addr, clientIpAddr, sizeof(clientIpAddr));

std::cout << "accept connectfd(" << clientIpAddr << ":" << clientPortNum << ") success" << std::endl;

// 接受消息

constexpr int MAX = 1024;

char recvBuf[MAX + 1];

read(connectfd, recvBuf, sizeof(recvBuf));

// echo消息

char sendBuf[MAX + 1];

snprintf(sendBuf, sizeof(sendBuf), "%s", recvBuf);

write(connectfd, sendBuf, strlen(sendBuf));

// 本轮服务结束,关闭连接套接字

close(connectfd);

}

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值