文章目录
1. 接口
1.1 转换操作
转换操作主要分为三类:字节序转换操作、IP地址转换操作和主机名转换操作
字节序转换操作
网络序转主机序
函数 | 含义 | 作用 |
---|---|---|
ntohs() | network to host short | 把unsigned short类型从网络序转换到主机序 |
ntohl() | network to host long | 把unsigned long类型从网络序转换到主机序 |
主机序转网络序
函数 | 含义 | 作用 |
---|---|---|
htons() | host to network short | 把unsigned short类型从主机序转换到网络序 |
htonl() | host to network long | 把unsigned long类型从主机序转换到网络序 |
头文件: #include <arpa/inet.h>
网络序和主机序的转换:
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <iomanip>
using namespace std;
void Hex(int n){
char bytes[2];
memcpy(bytes,&n,2); // 把n的地址复制进来,两个字节
cout << hex << setw(2) << setfill('0') << (int)bytes[0] << setw(2) << (int) bytes[1] << endl;
// 16进制,用0把两个字节补满
}
int main(){
uint16_t n;
cin >> n;
Hex(n);
uint16_t m = htons(n); // 主机序n转换为网络序m
Hex(m);
uint16_t t = ntohs(m); // 网络序m转换为主机序n
Hex(t);
}
结果为:
100
6400
0064
6400
无符号的16位是两个字节
IP地址转换操作
函数 | 功能 | 特点 |
---|---|---|
int inet_aton(const char *string, struct in_addr*addr) | 点分十进制数串转网络字节序长整型 | IPv4专用 |
in_addr_t inet_addr(const char* string) | 点分十进制数串转网络字节序长整型 | IPv4专用 |
char* inet_ntoa(struct in_addr addr) | 网络字节序长整型转点分十进制数串 | IPv4专用 |
int inet_pton(int af, const char *src, void *dst) | 点分十进制数串转网络字节序长整型 | IPv4/IPv6通用(推荐) |
const char *inet_ntop(int af, const void *src, char *dst, socklen_t cnt) | 网络字节序长整型转点分十进制数串 | IPv4/IPv6通用(推荐) |
结构体:
结构体 | 功能 | 特性 |
---|---|---|
struct sockaddr | 套接字地址结构 | IPv4/IPv6通用 |
struct sockaddr_in | IPv4套接字地址结构 | IPv4专用 |
struct in_addr | IPv4地址结构 | IPv4专用 |
in_addr_t | IPv4地址类型 | IPv4专用 |
struct sockaddr_in6 | IPv6套接字地址结构 | IPv6专用 |
点分十进制数串和网络字节序长整型转换:
#include <iostream>
#include <arpa/inet.h>
#include <cstring>
#include <iomanip>
using namespace std;
int main(){
string s;
cin >> s;
in_addr addr; // IPv4地址类型结构体
inet_aton(s.c_str(),&addr); // 点分十进制数串转网络字节序长整型
cout << addr.s_addr << endl; // s_addr为成员变量,可以直接用
cout << inet_ntoa(addr) << endl; // 网络字节序长整型转点分十进制数串
cout << inet_addr(s.c_str()) << endl; // 点分十进制数串转网络字节序长整型(简单)
}
结果为:
192.168.0.1
16820416
192.168.0.1
16820416
主机名转换操作
函数 | 功能 |
---|---|
struct hostent *gethostbyname(const char *hostname) | 主机名转地址 |
struct hostent *gethostbyaddr(const char * addr, int len, int type) | 地址转主机名 |
struct hostent *gethostbyaddr(const char * addr, int len, int type) | 地址转主机名 |
2. socket操作(TCP)
2.1 客户端
socket创建
int socket(int domain, int type, int protocol)
参数 | 含义 | 说明 |
---|---|---|
domain | 协议域 | AF_INET:IPv4;AF_INET6:IPv6;AF_LOCAL:Unix域 |
type | 类型 | SOCK_STREAM:流式套接字;SOCK_DGRAM:数据报套接字;SOCK_RAW:原始套接字 |
protocol | 协议 | 0:自动根据type匹配协议;IPPROTO_TCP / IPPROTO_UDP |
返回值:
返回值 | 含义 |
---|---|
-1 | 失败 |
>0 | socket描述符 |
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd){
perror("open socket error");
return 1;
}
connect连接
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数 | 含义 |
---|---|
sockfd | 客户端的socket描述字 |
addr | 服务器的socket地址 |
addrlen | 服务器的socket地址的长度 |
返回值:
返回值 | 含义 |
---|---|
0 | 成功 |
-1 | 失败 |
sockaddr_in addr; // IPv4套接字地址结构体
addr.sin_family = AF_INET; // 放入协议
inet_aton("127.0.0.1",&addr.sin_addr); // 点分十进制数串转网络字节序长整型
addr.sin_port = htons(8080); // 主机序转网络序,然后传入端口
int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
if(-1 == res){
perror("connect error");
return 1;
}
write发送
ssize_t write(int fd, const void *buf, size_t len);
参数 | 含义 |
---|---|
fd | 文件描述符 |
buf | 写入数据 |
len | 写入数据的长度 |
返回值:
返回值 | 含义 |
---|---|
>0 | 实际所写的字节数 |
<0 | 出错 |
string s;
cin >> s;
write(fd,s.c_str(),s.size()+1);
read接收
ssize_t read(int fd, void *buf, size_t len);
参数 | 含义 |
---|---|
fd | 文件描述符 |
buf | 读取数据 |
len | 读取数据的长度 |
返回值:
返回值 | 含义 |
---|---|
0 | 读到文件的结束 |
>0 | 实际所读的字节数 |
<0 | 出错 |
char buffer[256] = {0};
read(fd,buffer,256);
cout << buffer << endl;
close关闭
int close(int sockfd)
参数 | 含义 |
---|---|
sockfd | socket套接字 |
close(fd);
2.2 服务器
socket创建
与客户端相同
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd){
perror("open socket error");
return 1;
}
bind绑定
int bind(int socket, const struct sockaddr* address, socklen_t address_len)
参数 | 含义 |
---|---|
socket | 套接字描述符 |
address | 地址和端口号 |
address_len | address 缓冲区的长度 |
返回值:
返回值 | 含义 |
---|---|
0 | 成功 |
SOCKET_ERROR | 失败 |
sockaddr_in addr; // IPv4套接字地址结构体
addr.sin_family = AF_INET; // 放入协议
inet_aton("127.0.0.1",&addr.sin_addr); // 点分十进制数串转网络字节序长整型
addr.sin_port = htons(8080); // 主机序转网络序,然后传入端口
int res = bind(fd,(sockaddr*)&addr,sizeof(addr));
if(-1 == res){
perror("bind error");
return 1;
}
listen监听
int listen(int sockfd, int backlog)
参数 | 含义 |
---|---|
sockfd | 监听的socket描述符 |
backlog | 排队的最大连接个数 |
返回值:
返回值 | 含义 |
---|---|
0 | 成功 |
-1 | 失败 |
res = listen(fd,4);
if(-1 == res){
perror("listen error");
return 1;
}
accept接受
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
参数 | 含义 |
---|---|
sockfd | 服务器的socket描述符,监听socket描述符 |
addr | 客户端的socket地址,不需要就给NULL |
addrlen | 客户端的socket地址的长度,不需要就给NULL |
返回值:
返回值 | 含义 |
---|---|
非-1 | 连接描述符 |
-1 | 失败 |
int connfd = accept(fd,NULL,NULL);
if(-1 == connfd){
perror("accept error");
return 1;
}
read接收
这里接收的是用于通信的描述符connfd
char buffer[256] = {0};
read(connfd,buffer,256);
cout << buffer << endl;
write发送
这里发送的是用于通信的描述符connfd
string s;
cin >> s;
write(connfd,s.c_str(),s.size()+1);
close关闭
不仅需要关闭用于监听的描述符fd,还需要关闭用于通信的描述符connfd
close(connfd);
close(fd);
2.3 客户端和服务器的程序
客户端:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h> // AF_INET sockaddr_in
#include <unistd.h> // write() read() close()
using namespace std;
int main(){
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd){
perror("open socket error");
return 1;
}
sockaddr_in addr; // IPv4套接字地址结构体
addr.sin_family = AF_INET; // 放入协议
inet_aton("127.0.0.1",&addr.sin_addr); // 点分十进制数串转网络字节序长整型
addr.sin_port = htons(8080); // 主机序转网络序,然后传入端口
int res = connect(fd,(sockaddr*)&addr,sizeof(addr));
if(-1 == res){
perror("connect error");
return 1;
}
string s;
cin >> s;
write(fd,s.c_str(),s.size()+1);
char buffer[256] = {0};
read(fd,buffer,256);
cout << buffer << endl;
close(fd);
}
服务器:
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h> // AF_INET sockaddr_in
#include <unistd.h> // write() read() close()
using namespace std;
int main(){
int fd = socket(AF_INET,SOCK_STREAM,0);
if(-1 == fd){
perror("open socket error");
return 1;
}
sockaddr_in addr; // IPv4套接字地址结构体
addr.sin_family = AF_INET; // 放入协议
inet_aton("127.0.0.1",&addr.sin_addr); // 点分十进制数串转网络字节序长整型
addr.sin_port = htons(8080); // 主机序转网络序,然后传入端口
int res = bind(fd,(sockaddr*)&addr,sizeof(addr));
if(-1 == res){
perror("bind error");
return 1;
}
res = listen(fd,4);
if(-1 == res){
perror("listen error");
return 1;
}
int connfd = accept(fd,NULL,NULL);
if(-1 == connfd){
perror("accept error");
return 1;
}
char buffer[256] = {0};
read(connfd,buffer,256);
cout << buffer << endl;
string s;
cin >> s;
write(connfd,s.c_str(),s.size()+1);
close(connfd);
close(fd);
}
操作步驟:
- 在一边启动服务器,在另一端启动客户端
- 服务器等待客户端发送消息
- 客户端发送消息,服务器接收到
- 服务器发送消息,客户端接收到,两边退出