1.相关函数:
1.1 socket()
获取套接字socket( int domain , int type , int potacl);
参数解析:
domain
指定通信域,主要的类型:
-
UNIX(AF_UNIX) 同一台主机的进程间通信。
-
IPV4域(AF_INET)跨网络通信。
-
IPV6域(AF_INET6)跨网络通信。
type:
- SOCK_STREAM (流式套接字)
- 通信模式:提供面向连接的、可靠的、双向的、基于字节流的通信。
- 核心特性:
- 可靠:数据无差错、无丢失、无重复,且按发送顺序到达(TCP 协议的特性)。
- 流式:数据没有明确的"消息"边界。发送方多次写入的数据,接收方可能一次读出,反之亦然。应用层需要自己处理消 息边界(如添加长度头或分隔符)。
- 连接导向:通信前必须建立连接(三次握手),通信后需要断开连接(四次挥手)。
- 底层协议:通常与 TCP 协议配对。
- 典型应用:HTTP、HTTPS、FTP、SSH、数据库连接等所有需要可靠数据传输的场景。
- SOCK_DGRAM (数据报套接字)
- 通信模式:提供无连接的、不可靠的、固定最大长度的、基于数据报的通信。
- 核心特性:
- 不可靠:不保证数据一定到达,不保证顺序,不保证不重复。
- 有边界:每个 send 或 sendto 调用产生一个独立的数据报,接收方 recvfrom 会完整收到整个数据报。消息不会粘在一 起。
- 无连接:无需建立和断开连接,直接发送给目标地址。
- 底层协议:通常与 UDP 协议配对。
- 典型应用:DNS 查询、音视频流、在线游戏、广播/多播、SNMP 等能容忍少量丢包或要求低延迟的场景。
protocal:
场景 推荐设置 说明
普通 TCP套接字 protocol = 0 系统自动选择 TCP
普通 UDP 套接字 protocol = 0 系统自动选择 UDP
原始套接字 必须显式指定 IPPROTO_ICMP、IPPROTO_RAW
使用非默认协议 指定具体协议常量 IPPROTO_SCTP
需要协议过滤 指定要过滤的协议 原始套接字按协议过滤
返回值:成功返回一个socket,失败返回-1。与文件描述符一样,通过不同的int类型的描述符打开某个文件或指定获取特定端口的数据。
sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 使用获取udp通信的套接字
1.2 struct_sockaddr_in结构体
用于指定套接字对应的端口和允许接收来自特定ip的消息。

参数解析:
_SOCKADDR_COMMON (sin)

通过拼接的方式实现的变量 sa_family_t sin_family。
用于指定通信域,对应socket中的domain。
sin_port指定套接字需要对应的端口号。
typedef uint16_t in_port_t;
struct in_addrsin_addr:指定32位的ip。
typedef uint32_t in_addr_t;
struct in_addr
{
in_addr_t s_addr;
};
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
// serveraddr.sin_addr.s_addr=inet_addr(ip.cstr()); // 指定特定的ip访问
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 接收所有的ip
注意:
1.由于主机和网络在数据表示上的差异,需要使用函数进行数据来回转化,htons(host to net shore)将shore类型(16个比特位)的数据从主机转换为网络数据。
- 我们输入ip的形式是"127.0.0.1"需要调用inet_addr函数转化为网络数据,INADDR_ANY定义为0x00000000,当ip为0.0.0.0时,表示可以接收所有访问服务端port的ip,服务器接收ip一般设为INADDR_ANY。
1.3 bind()
bind( int sockfd ,const struct sockaddr * addr ,socklen_t len ):服务器通过 bind() 告诉操作系统:"请在我的这个套接字上监听指定的 IP 和端口"。
实现初始化函数
void Init()
{
// 1.获取套接字
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
// 2.指定直接接收消息的端口和ip地址到sockaddr_in
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(port);
// serveraddr.sin_addr.s_addr=inet_addr(ip.cstr()); // 指定特定的ip访问
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); // 接收所有的ip
// 3.绑定套接字和sockaddr_in
int ret = bind(sockfd, (const sockaddr *)&serveraddr, (socklen_t)sizeof(serveraddr));
if (ret == -1)
{
cout << "绑定失败:" << strerror(errno) << endl;
exit(1);
}
}
1.4 recvfrom
recvfrom (int __fd, void *__restrict __buf, size_t __n, int __flags,
__SOCKADDR_ARG __addr, socklen_t *__restrict __addr_len)
参数解析:
_fd:自己监听的套接字。
_buf:接收的消息放在buf中,输出型参数。
_n:buf最大可以存储数据的字节数。
_addr:和struct sockaddr_in 一样,在这里,存储的是访问者(客户端)的ip,port,domain,输出型参数。
_len:_addr的长度的地址。
// 接收消息
void start()
{
while (1)
{
char buffer[1024];
struct sockaddr_in cilentmessage;
socklen_t len = sizeof(cilentmessage);
ssize_t ret = recvfrom(sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&cilentmessage,&len);
if (ret > 0)
{
buffer[ret] = 0;
int16_t port = htons(cilentmessage.sin_port);
string ip = inet_ntoa(cilentmessage.sin_addr);
string message = buffer;
cout << "ip: " << ip << " port: " << port << " message: " << message << endl;
}
}
}
2. 实现主函数
void manual()
{
printf("use manual: \n filename: port: \n");
}
int main(int argc,const char* argv[])
{
if(argc!=2)
{
manual();
exit(2);
}
string port=argv[1];
unique_ptr<server> myserver(new server(stoi(port)));
myserver->Init();
myserver->start();
return 0;
}
执行程序,查看网络情况

客户端正在执行,实现成功。
3. 用户端
cilent.cpp:
#include<iostream>
#include<memory>
#include"cilent.h"
using namespace std;
void manual()
{
printf("use manual: \n filename: ip: port: \n");
}
int main(int argc,const char* argv[])
{
if(argc!=3)
{
manual();
exit(2);
}
string port=argv[2];
string ip=argv[1];
unique_ptr<cilent> mycilent(new cilent(stoi(port),ip));
mycilent->Init();
mycilent->start();
return 0;
}
cilent.h:
class cilent
{
int16_t port;
string ip;
int sockfd;
public:
cilent(int16_t _port, string _ip) : port(_port), ip(_ip) {}
~cilent() {}
void Init()
{
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd == -1)
{
cout << "error: " << strerror(errno) << endl;
exit(2);
}
// cilent不需要bind,OS会自己选择使用哪个端口去访问客户端
}
void start()
{
struct sockaddr_in servermess;
memset(&servermess,0,sizeof(servermess));
servermess.sin_port = htons(port);
servermess.sin_family = AF_INET;
servermess.sin_addr.s_addr = inet_addr(ip.c_str()); // 使用127.0.0.1测试
char buffer[1024];
while (1)
{
cout<<"Please Enter: ";
fgets(buffer,sizeof(buffer),stdin);
socklen_t len = sizeof(servermess);
int ret = sendto(sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&servermess,len);
}
}
};
4. 测试交流
