linux socket_stream,Linux socket类,支持AF_INET/AF_UNIX STREAM/DGRAM

本文介绍了一个使用C++实现的Socket类,该类模仿了Python的Socket和SocketServer模块的功能。作者提供了详细的代码实现,包括创建、绑定、监听等操作,并支持AF_INET和AF_UNIX地址族。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习python时发现,socket和SocketServer的方便快捷,于是研究了一下socket和SocketServer.py写了一下C++版本的。

陆续分享一下。

1. socket类socket_t

socket++.h

/* class socket_t

* Description

* Simulation to python socket class

* Author

* wfb

* Version

* 0.1.0

* 2010/7/25

*/

#ifndef __SOCKET_PLUS_H__

#define __SOCKET_PLUS_H__

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

namespace ss

{

// it's NOT defined in (JUST A MAGIC, don't know y)

const size_t UNIX_PATH_MAX = 108;

/* address_t

* host[string] for ipaddress in ip4, unix-domain in AF_UNIX (ip6 NOT SUPPOSED RIGHT NOW, it's easy to)

* port[ushort] invalid for AF_UNIX

*/

struct address_t

{

std::string host;

unsigned short port;

address_t():host(""), port(0){}

address_t(const std::string& h, unsigned short p) : host(h), port(p){}

address_t(const address_t& add) : host(add.host), port(add.port){}

};

/* class socket_t

* int create();

* int create_from(int fileno);

* int accept(socket_t& socket, address_t& address);

* int bind(address_t& address);

* int listen(int backlog=5);

* int connect(const address_t& address);

* int close();

* int setsockopt(int level, int optname, int optval);

* int getsockopt(int level, int optname, int& optval);

* int reuse_address(int on=1

* int linger(int timeout=5);

* int send();

* int recv();

* int sendto();

* int recvfrom();

* int setblocking(int on=1);

* int fileno()const{return m_fileno;}

* int last_errno()const;

* int makefile();

*/

class socket_t

{

public:

socket_t():m_fileno(-1), m_ref(NULL){}

socket_t(int family, int type, int proto)

: m_fileno(::socket(family, type, proto)), m_family(family), m_type(type), m_proto(proto), m_ref(new int(1))

{}

socket_t(int fileno, int* pref, int family, int type, int proto)

: m_fileno(fileno), m_family(family), m_type(type), m_proto(proto), m_ref(pref ? pref : new int(1))

{}

socket_t(socket_t& sock)

: m_fileno(sock.m_fileno), m_family(sock.m_family), m_type(sock.m_type), m_proto(sock.m_proto), m_ref(sock.m_ref)

{

ref();

}

socket_t& operator=(socket_t& sock);

~socket_t(){close();}

int create();

int create_from(int fileno);

int accept(socket_t& socket, address_t& address);

int bind(address_t& address);

int listen(int backlog=5);

int connect(const address_t& address);

int close();

int setsockopt(int level, int optname, int optval);

int getsockopt(int level, int optname, int& optval);

int reuse_address(int on=1){return setsockopt(SOL_SOCKET, SO_REUSEADDR, on);}

int linger(int timeout=5);

/* TODO

int send();

int recv();

int sendto();

int recvfrom();

*/

int setblocking(int on=1);

int fileno()const{return m_fileno;}

int last_errno()const{return m_errno;}

int makefile(){return 0;}

private:

int ref(){if(NULL==m_ref)return 0;return ++*m_ref;}

int dref(){if(NULL==m_ref)return 0;return --*m_ref;}

private:

int m_fileno;

int m_family;

int m_type;

int m_proto;

int m_errno;

int* m_ref;

};

} // namespace ss

#endif //__SOCKET_PLUS_H__

socket++.cpp

#include "socket++.h"

namespace ss

{

socket_t& socket_t::operator=(socket_t& sock)

{

if (this==&sock) return *this;

this->close();

m_family = sock.m_family;

m_type = sock.m_type;

m_proto = sock.m_proto;

m_fileno = sock.m_fileno;

m_ref = sock.m_ref;

ref();

return *this;

}

int socket_t::create_from(int fileno)

{

if (m_ref) return m_fileno;

m_fileno = fileno;

m_ref = new int(1);

return m_fileno;

}

int socket_t::close()

{

int ret = 0;

if (m_ref && dref()<=0)

{

delete m_ref;

m_ref = NULL;

ret = ::close(m_fileno);

m_fileno = -1;

}

return ret;

}

int socket_t::bind(address_t& address)

{

sockaddr_in addr_in;

sockaddr_un addr_un;

sockaddr* paddr = NULL;

socklen_t addrlen = 0;

if (AF_INET==m_family)

{

bzero(&addr_in, sizeof(addr_in));

addr_in.sin_port = ::htons(address.port);

addr_in.sin_family = m_family;

if ((::inet_pton(m_family, address.host.c_str(), &addr_in.sin_addr)<=0))

{

m_errno = errno;

return -1;

}

paddr = (sockaddr*)&addr_in;

addrlen = sizeof(addr_in);

}

else if (AF_UNIX==m_family)

{

bzero(&addr_un, sizeof(addr_un));

addr_un.sun_family = AF_UNIX;

strncpy(addr_un.sun_path, address.host.c_str(), std::min(UNIX_PATH_MAX-1, address.host.size()));

address.host = addr_un.sun_path;

paddr = (sockaddr*)&addr_un;

addrlen = sizeof(addr_un);

}

else // AF_INET6 NOT supported

{

return -1;

}

if (::bind(m_fileno, paddr, addrlen))

{

m_errno = errno;

return -1;

}

return 0;

}

int socket_t::listen(int backlog/*=5*/)

{

int ret = ::listen(m_fileno, backlog);

if (ret)

{

m_errno = errno;

}

return ret;

}

int socket_t::accept(socket_t& socket, address_t& address)

{

sockaddr_in addr_in;

sockaddr_un addr_un;

sockaddr* paddr = NULL;

socklen_t addrlen = 0;

if (AF_INET==m_family)

{

bzero(&addr_in, sizeof(addr_in));

paddr = (sockaddr*)&addr_in;

addrlen = sizeof(addr_in);

}

else if (AF_UNIX==m_family)

{

bzero(&addr_un, sizeof(addr_un));

paddr = (sockaddr*)&addr_un;

addrlen = sizeof(addr_un);

}

int fd = ::accept(m_fileno, paddr, &addrlen);

if (fd>0)

{

if (AF_INET==m_family)

{

address.host = std::string(::inet_ntoa(addr_in.sin_addr));

address.port = ::ntohs(addr_in.sin_port);

}

else if (AF_UNIX==m_family)

{

address.host = addr_un.sun_path;

}

else

{

return -1;

}

socket_t client_sock(fd, NULL, m_family, m_type, m_proto);

socket = client_sock;

}

return fd;

}

int socket_t::connect(const address_t& address)

{

sockaddr_in addr_in;

sockaddr_un addr_un;

sockaddr* paddr = NULL;

socklen_t addrlen = 0;

if (m_family==AF_INET||m_family==AF_INET6)

{

bzero(&addr_in, sizeof(addr_in));

addr_in.sin_port = ::htons(address.port);

addr_in.sin_family = m_family;

if ((::inet_pton(m_family, address.host.c_str(), &addr_in.sin_addr)<=0))

{

m_errno = errno;

return -1;

}

paddr = (sockaddr*)&addr_in;

addrlen = sizeof(addr_in);

}

else if (m_family==AF_UNIX)

{

bzero(&addr_un, sizeof(addr_un));

addr_un.sun_family = AF_UNIX;

strncpy(addr_un.sun_path, address.host.c_str(), std::min(UNIX_PATH_MAX-1, address.host.size()));

paddr = (sockaddr*)&addr_un;

addrlen = sizeof(addr_un);

}

else // AF_INET6 NOT supported

{

return -1;

}

if (::connect(m_fileno, paddr, addrlen))

{

m_errno = errno;

return -1;

}

return 0;

}

int socket_t::setsockopt(int level, int optname, int optval)

{

int ret = ::setsockopt(m_fileno, level, optname, &optval, sizeof(optval));

if (ret)

{

m_errno = errno;

}

return ret;

}

int socket_t::getsockopt(int level, int optname, int& optval)

{

socklen_t optlen = sizeof(optval);

int ret = ::getsockopt(m_fileno, level, optname, &optval, &optlen);

if (ret)

{

m_errno = errno;

}

return ret;

}

int socket_t::setblocking(int on/*=1*/)

{

int ret = 0;

int val = 0;

if ((val = ::fcntl(m_fileno, F_GETFL)) == -1 && errno)

{

m_errno = errno;

return -1;

}

switch (on)

{

case 0:

if (val & O_NONBLOCK)

{

ret = ::fcntl(m_fileno, F_SETFL, 0);

}

break;

default:

if (!(val & O_NONBLOCK))

{

ret = ::fcntl(m_fileno, F_SETFL, 1);

}

break;

}

if (ret)

{

m_errno = errno;

}

return ret;

}

int socket_t::linger(int timeout/*=5*/)

{

struct linger lg;

lg.l_onoff=timeout>0?1:0;

lg.l_linger=timeout;

int ret = ::setsockopt(m_fileno, SOL_SOCKET, SO_LINGER, &lg, sizeof(lg));

if (ret)

{

m_errno = errno;

}

return ret;

}

} //namespace ss

<think>首先,用户的问题是:“请问socket(AF_INET SOCK_DGRAM 0) 的作用和使用方法”。这是一个关于网络编程的查询,具体是关于使用socket API创建UDP套接字。 根据系统级指令,我需要: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 确保LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 在回答中引用的段落末尾自然地添加引用标识,例如[^1]。 - 回答结构清晰,帮助用户逐步解决问题。 - 保证回答真实可靠。 参考提供的引用: - 引用[1]:解释了SocketAF_UNIXAF_INET、SOCK_STREAM和SOCK_DGRAM。 - 引用[2]:描述了使用SOCK_DGRAM创建UDP套接字。 - 引用[3]:给出了Python中使用UDP套接字的代码示例。 - 引用[4]:解释了UDP套接字的特点,如不可靠、开销小。 用户意图是了解socket(AF_INET, SOCK_DGRAM, 0)的作用和使用方法。这似乎是C语言中的调用,因为0通常是协议参数,表示默认协议。 关键点: - `AF_INET`:表示地址家族,用于IPv4地址。 - `SOCK_DGRAM`:表示套接字型,用于数据报,即UDP。 - `0`:协议参数,通常表示使用默认协议,对于UDP是IPPROTO_UDP。 在socket函数中,函数签名通常是:`int socket(int domain, int type, int protocol);` - domain: AF_INET(IPv4) - type: SOCK_DGRAM(数据报) - protocol: 0(表示自动选择协议,对于SOCK_DGRAM,默认是UDP) 所以,`socket(AF_INET, SOCK_DGRAM, 0)`的作用是创建一个UDP套接字。 使用方法: 1. 创建套接字:调用socket函数。 2. 绑定地址(可选):对于服务器端,通常绑定到特定IP和端口。 3. 发送和接收数据:使用sendto和recvfrom函数。 4. 关闭套接字。 在回答中,我需要结构化: - 解释作用。 - 描述使用方法。 - 提供简单示例(由于用户问题似乎是C语言相关,我可以给一个C代码片段)。 - 确保使用中文。 - 生成相关问题。 最后,生成相关问题,基于主题。 回答结构: 1. **引入**:简要介绍socket函数和UDP。 2. **作用**:解释`socket(AF_INET, SOCK_DGRAM, 0)`的具体作用。 3. **使用方法**:分步说明如何使用,包括创建、绑定、发送、接收和关闭。 4. **代码示例**:提供一个简单的C代码示例。 5. **注意事项**:提到UDP的特性,如不可靠性。 6. **相关问题**:生成几个相关问题。 引用标识:在引用内容末尾添加[^1]、[^2]等。参考引用[2]和[4],因为它们直接相关。 例如: - 根据引用[2],UDP套接字可以使用socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)创建,但0是等价的。 - 引用[4]解释了UDP的特点。 确保LaTeX:如果涉及数学,但这里可能不涉及数学表达式,除非有公式。用户问题中没有数学,所以可能不需要LaTeX,但指令说如果有变量等使用$...$。这里可能没有数学内容。 生成相关问题: - 例如:如何绑定UDP套接字?UDP与TCP的区别?如何处理UDP数据报丢失? 完整回答: - 开头:直接回答问题。 - 主体:逐步解释。 - 结尾:生成相关问题。</think>### socket(AF_INET, SOCK_DGRAM, 0) 的作用和使用方法 `socket(AF_INET, SOCK_DGRAM, 0)` 是网络编程中用于创建 UDP 套接字的函数调用。UDP(User Datagram Protocol)是一种无连接的传输协议,适用于对实时性要求高但允许少量数据丢失的场景,如视频流、DNS 查询等。下面我将逐步解释其作用和使用方法,确保内容真实可靠,基于标准网络编程实践。 #### 1. **作用** - **核心功能**:创建一个基于 IPv4 的 UDP 套接字。套接字是网络通信的端点,用于发送和接收数据报。 - `AF_INET`:指定地址家族为 IPv4,表示使用 32 位 IP 地址(如 `192.168.1.1`)[^1]。 - `SOCK_DGRAM`:指定套接字型为数据报(datagram),即 UDP 模式。这种模式不支持连接建立,数据以独立报文形式传输,开销小但不可靠(可能丢失或乱序)[^4]。 - `0`:协议参数,表示使用默认协议。对于 `SOCK_DGRAM`,系统会自动选择 UDP 协议(等价于 `IPPROTO_UDP`)。因此,`socket(AF_INET, SOCK_DGRAM, 0)` 等同于 `socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)`[^2]。 - **关键特性**: - **无连接性**:无需建立连接,直接发送数据。 - **低开销**:传输效率高,适合小数据包。 - **不可靠性**:不保证数据顺序、完整性和可达性,需应用层处理错误[^4]。 #### 2. **使用方法** 使用 `socket(AF_INET, SOCK_DGRAM, 0)` 创建 UDP 套接字后,需配合其他函数完成通信。以下是标准步骤(以 C 语言为例),适用于服务器端和客户端。 ##### 步骤 1: 创建套接字 - 调用 `socket()` 函数创建套接字句柄。 - 检查返回值:成功返回非负文件描述符(fd),失败返回 -1(需错误处理)。 ```c #include <sys/socket.h> #include <netinet/in.h> int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket creation failed"); exit(EXIT_FAILURE); } ``` ##### 步骤 2: 绑定地址(可选,通常用于服务器) - 服务器端需绑定到特定 IP 和端口,以监听数据。 - 使用 `struct sockaddr_in` 设置地址结构。 - 调用 `bind()` 函数绑定套接字。 ```c struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; // IPv4 server_addr.sin_addr.s_addr = INADDR_ANY; // 绑定到所有本地 IP server_addr.sin_port = htons(8080); // 端口号(主机字节序转网络字节序) if (bind(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("bind failed"); close(sockfd); exit(EXIT_FAILURE); } ``` ##### 步骤 3: 发送和接收数据 - **发送数据**:使用 `sendto()` 函数,需指定目标地址。 - **接收数据**:使用 `recvfrom()` 函数,获取数据和发送者地址。 - 示例:服务器接收数据并回显(客户端似,但无需绑定)。 ```c char buffer[1024]; struct sockaddr_in client_addr; socklen_t addr_len = sizeof(client_addr); // 接收数据 ssize_t recv_len = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr*)&client_addr, &addr_len); if (recv_len < 0) { perror("recvfrom failed"); } // 发送响应(例如回显数据) ssize_t send_len = sendto(sockfd, buffer, recv_len, 0, (struct sockaddr*)&client_addr, addr_len); if (send_len < 0) { perror("sendto failed"); } ``` ##### 步骤 4: 关闭套接字 - 通信结束后,关闭套接字释放资源。 ```c close(sockfd); ``` #### 3. **注意事项** - **错误处理**:UDP 无连接,需处理丢包和乱序(如添加序列号或使用超时重传)。 - **跨平台兼容性**:在 Windows 中,需包含 `winsock2.h` 并初始化 Winsock 库。 - **性能优化**:对于高负载场景,可设置套接字选项(如 `SO_RCVBUF` 增大缓冲区)。 - **安全提示**:UDP 易受攻击(如 DDoS),建议添加数据验证(如校验和)。 #### 4. **简单示例:UDP 客户端** 以下是一个完整 C 语言客户端示例,发送消息到服务器。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> int main() { int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) { perror("socket failed"); return 1; } struct sockaddr_in server_addr; memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(8080); // 服务器端口 inet_pton(AF_INET, "192.168.1.102", &server_addr.sin_addr); // 服务器 IP char *msg = "Hello, UDP Server!"; ssize_t sent = sendto(sockfd, msg, strlen(msg), 0, (struct sockaddr*)&server_addr, sizeof(server_addr)); if (sent < 0) { perror("sendto failed"); } close(sockfd); return 0; } ``` #### 引用说明 - 创建 UDP 套接字时,`SOCK_DGRAM` 直接对应 UDP 协议,确保数据报传输[^2]。 - UDP 的无连接特性使其适用于低延迟应用,但需注意可靠性问题[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值