基于Linux的套接字相关函数
在 linux 下,socket 也被认为是文件的一种。
文件的读写:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcnt1.h>
int open(const char* path, int flag);
#include <unistd.h>
ssize_t write(int fd, const void* buf, size_t nbytes);
int close(int fd);
#include <unistd.h>
ssize_t read(int fd, void* buf, size_t nbytes);
open 函数的返回值即文件描述符,也即 close 函数的参数。
ps: size_t 是通过 typedef 声明的 unsigned int 类型;ssize_t 是通过 typedef 声明的 signed int 类型。
- 服务端套接字创建过程
#include <sys/socket.h>
#include <unistd.h>
// 创建 socket
int socket(int domaink, int type, int protocol);
// 绑定ip,端口号
int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);
// 创建监听
int listen(int sockfd, int backlog);
// 接受连接
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
// 发送数据
ssize_t write(int fd, const void* buf, size_t nbytes);
// 关闭套接字
int close(int fd);
- 客户端套接字创建过程
#include <sys/socket.h>
#include <unistd.h>
// 创建 socket
int socket(int domaink, int type, int protocol);
// 请求连接
int connect(int sockfd, struct sockaddr* serv_addr, socklen_t addrlen);
// 接收数据
ssize_t read(int fd, void* buf, size_t nbytes);
// 关闭套接字
int close(int fd);
基于Windows的套接字相关函数
#include <WinSock2.h>
// 初始化,可借助 MAKEWORD(1, 2);函数构建WORD型版本信息
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
Windows 严格区分文件 I/O 函数和套接字 I/O 函数。
#include <WinSock2.h>
int send(SOCKET s, const char* buf, int len, int flags);
int recv(SOCKET s, const char* buf, int len, int flags);
- 服务端套接字创建过程
#include <WinSock2.h>
// 初始化
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
// 创建套接字
SOCKET socket(int af, int type, int protocol);
// 绑定ip,端口号
int bind(SOCKET s, const struct sockaddr* name, int namelen);
// 创建监听
int listen(SCOKET s, int backlog);
// 接受连接
SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
// 发送数据
int send(SOCKET s, const char* buf, int len, int flags);
// 关闭套接字
int closesocket(SOCKET s);
// 注销初始化
int WSACleanup(void);
- 客户端套接字创建过程
#include <WinSock2.h>
// 初始化
int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
// 创建套接字
SOCKET socket(int af, int type, int protocol);
// 请求连接
int connect(SOCKET s, const char* buf, int len, int flags);
// 接收数据
int recv(SOCKET s, const char* buf, int len, int flags);
// 关闭套接字
int closesocket(SOCKET s);
// 注销初始化
int WSACleanup(void);
套接字类型及协议
int socket(int domaink, int type, int protocol);
domain
PF_INET IPv4 互联网协议族
PF_INET6 IPv6 互联网协议族
PF_LOCAL 本地通信的 UNIX 协议族
PF_PACKET 底层套接字的协议族
PF_IPX IPX Novell 协议族
type
面向连接的套接字 SOCK_STREAM,特点:可靠传输,有序传输,不存在数据边界
面向消息的套接字 SOCK_DGRAM,特点:快速船务,无序传输,有数据边界,限制传输数据大小
protocol
满足 PF_INET + SOCK_STREAM 的只有 IPPROTO_TCP
满足 PF_INET + SOCK_DGRAM 的只有 IPPROTO_UDP
地址族与数据序列
int bind(int sockfd, struct sockaddr* myaddr, socklen_t addrlen);
struct sockaddr_in
struct sockaddr_in
{
sa_family_t sin_family; // 地址族
uint16_t sin_port; // 16位 TCP/UDP 端口号
struct in_addr; // 32位 IP 地址
char sin_zero[8]; // 不使用
}
struct in_addr
{
In_addr_t s_addr; //32位 IP 地址
}
sockaddr_in 成员分析
sin_family
sin_port
保存16位端口号,重点在于,它以网络字节序保存.
sin_addr
该成员保存32位IP地址信息,且也以网络字节序保存。
sin_zero
只为使 sockaddr_in 的大小与 sockaddr 结构体保持一致而插入的成员,必须填充为0。
使用举例:
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(atoi(argv[1]));
int bind(serv_sock, (struct sockaddr*) serv_addr, sizeof(serv_addr));
网络字节序与地址变换
在通过网络传输数据时约定统一方式,这种约定称为网络字节序,统一为大端序。
字节序转换函数:
unsigned short htons(unsigned short);
unsigned short ntols(unsigned short);
unsigned long htonl(unsigned long);
unsigned long ntohl(unsigned long);
h
代表 host, n
代表 network, s
代表 short, l
代表 long
那么 htons 即“把short型数据从网络字节序转化为主机字节序”
网络地址的初始化与分配
将点分十进制格式的字符串转为32位整型数据的函数
#include <arpa/inet.h>
in_addr_t inet_addr(const char* string);
与之类似的函数
#include <arpa/inet.h>
int inet_aton(const char* string, struct in_addr* addr);
成功时返回1,失败返回0;string 含有需要转换IP地址信息的字符串地址,将保存转换结果的 in_addr 结构体变量的地址值。
请查看该函数的源码,了解一下是怎么转换的。
介绍一个相反的函数,将32位整型数据转化为点分十进制的IP字符串形式
#include <arpa/inet.h>
char * inet_ntoa(struct in_addr adr);
失败时返回-1
网络地址初始化:
结合前面所述,套接字创建过程中常见的网络地址信息初始化方法如下:
struct sockaddr_in addr;
char* serv_ip = "211.217.168.13";
char* serv_port = "9190";
memset(&addr, 0, sizeof(addr)); // 结构体变量 addr 的所有成员初始化为0
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(serv_ip);
addr.sin_port = htons(atoi(serv_port)); // atoi 函数,将字符串类型的值转为整数型
而采用 INADDR_ANY 的方式,则可自动获取运行服务器端的计算机 IP 地址。
addr.sin_addr.s_addr = htonl(INADDR_ANY);
(未完待续)