Linux网络编程基础API

Linux网络编程基础API

Linux网络API主要可分为:

  • socket地址API
  • socket基础API
  • 网络信息API

socket地址API

主机字节序和网络字节序

两种字节序

  • 大端字节序:整数的高位字节存储在内存的低地址处。
  • 小端字节序:整数的高位字节存储在内存的高地址处。

主机字节序(小端)

现代PC大多采用小端字节序,因此小端字节序又被称为主机字节序。

网络字节序(大端)

发送方主机通过网络传递数据时,将机器字节序转化为大端字节序然后进行传输;接收方接收大端数据后,将转化为本地机器字节序。

机器字节序判断

#include <stdio.h>

void byteorder()
{
	union
	{
		short value;
		char union_bytes[sizeof(short)];
	} test;
	test.value = 0x0102;
	if ((test.union_bytes[0] == 1) && (test.union_bytes[1] == 2))
	{
		printf("big endian\n");
	}
	else if ((test.union_bytes[0] == 2) && (test.union_bytes[1] == 1))
	{
		printf("little endian\n");
	}
	else
	{
		printf("unknown...\n");
	}
}

字节序的转换

以下4个函数可用来完成主机字节序和网络字节序之间的转换:

#include<netinet/in.h>
unsigned long int htonl( unsigned long int hostlong)    // host to network long
unsigned short int htons( unsigned short int hostshort) // host to network short
unsigned long int ntohl( unsigned long int netlong)     // network to host long
unsigned short int ntohs( unsigned short int hostlong)  // network to host short

通用socket地址

socket地址结构体

#inlcude<bits/socket.h>

//旧,sa_data太小,无法存放部分协议的地址值
struct sockaddr{
    sa_family_t sa_family;	// 协议簇
    char sa_data[14];		// 存放socket地址值
}
//新
struct sockaddr_storage{
    sa_family_t sa_family;						//协议簇
    unsigned long int __ss_align;				//用于内存对齐
    char __ss_padding[128-sizeof(__ss_align)];	//存放地址值
}

协议簇及其地址值

协议簇/地址簇 描述 地址值含义和长度
PF_UNIX/AF_UNIX UNIX本地域协议簇 文件的路径名
PF_INET/AF_INET TCP/IPv4协议簇 IPv4地址,IP+PORT,6字节
PF_INET6/AF_INET6 TCP/IPv6协议簇

专用socket地址

注:所有专用socket地址类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有socket编程接口使用的地址参数类型都是sockaddr。

通用socket地址结构使用麻烦,设置IP地址和端口号需要执行繁琐的位操作。Linux为各个协议簇提供了专门的socket地址结构体。

  • UNIX本地协议簇
#include <sys/un.h>
struct sockaddr_un{
    sa_family_t sin_family;	// 地址簇:AF_UNIX
    char sun_path[108];		// 文件路径名
}
  • TCP/IPv4协议簇
#include <netinet/in.h>

struct sockaddr_in
{
	sa_family_t sin_family;		//地址族:AF_INET
	u_int16_t sin_port;			//端口号,要用网络字节序表示
	struct in_addr sin_addr;	//IPV4地址结构体
}
struct in_addr 
{
	u_int32_t s_addr;			//ipv4地址,要用网络字节序
}
  • TCP/IPv6协议簇
struct sockaddr_in6
{
	sa_family_t sin6_family;   //地址族:AF_INET6
	u_int16_t sin6_port;       //端口号,要用网络字节序表示
	u_int32_t sin6_flowinfo;   //流信息,应设置为0
	struct in6_addr sin6_addr; //IPv6地址结构体
	u_int32_t sin6_scope_id;
};
struct in6_addr
{
	unsigned char sa_addr[16]; //IPv6地址,要用网络字节序表示
}

IP地址转换函数

IP地址的表示方式

  • 点分十进制:便于阅读
  • 二进制整数(网络字节序):便于机器表示与处理

表示法之间的转换

//IPv4地址和网络字节序整数表示的IPv4地址之间的转换:

#include <arpa/inet.h>

//点分十进制 转 网络字节序整数
in_addr_t inet_addr(const char* strptr);

//功能同于inet_addr,并将结果存于inp
int inet_aton(const char* cp, strcut in_addr* inp);

//网络字节序整数 转 点分十进制,函数内部使用静态变量存储转化结果(不可重入)
char* inet_ntoa(struct in_addr in);

socket基础API

在linux中,socket是一个可读、可写、可控制、可关闭的文件描述符。

创建socket

socket系统调用创建socket,创建时指定协议簇、服务类型。

#include <sys/types.h>
#include <sys/socket.h>

// domain:		使用哪个底层协议簇。	PF_INET、PF_INET6或PF_UNIX
// type:		指定服务类型。	SOCK_STREAM(流服务)、SOCK_UGRAM(数据报)
// protocol:	几乎所有情况下,设置为0
int socket(int domain, int type, int protocol);

注:协议簇为PF_INET(IPv4)时,SOCK_STREAM和SOCK_DGRAM分别表示TCP与UDP。

命名socket

命名

与socket地址绑定后的socket,被称为命名socket。

  • 服务端:使用命名socket,绑定固定地址,便于客户端连接。
  • 客户端:使用匿名socket,操作系统自动分配的socket地址。

地址绑定

bind系统调用将socket绑定固定IP地址。

#include <sys/types.h>
#include <sys/socket.h>

// sockfd:		socket文件描述符
// sockaddr:	socket地址
// addrlen:		socket地址长度
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);

监听socket

listen系统调用创建一个监听队列以存放待处理的客户连接。

#include <sys/socket.h>

// sockfd:	socket文件描述符
// backlog:	内核监听队列的最大长度(2.2以上版本指处于完全连接状态的socket的上限)
int listen(int sockfd, int backlog);

接受连接

accept系统嗲用从listen监听队列中接受一个连接。

#include <sys/types.h>
#include <sys/socket.h>

// sockfd:	监听socket文件描述符
// addr:	客户端socket地址
// addrlen:	客户端socket地址长度
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

注:accept只是从监听队列中取出连接,而不论连接处于何种状态(完全连接、半连接或关闭),更不关心任何网络状况的变化。

发起连接

connect系统调用用于与目标服务器建立连接。

#include <sys/types.h>
#include <sys/socket.h>

//sockfd:		socket文件描述符
//serv_addr:	服务器监听socket地址
//addrlen:		服务器监听socket地址长度
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);

关闭连接

  • close:可用于关闭连接对应的socket,引用计数减1,引用计数为0是真正关闭连接。
#include <unistd.h>

int close(int fd);
  • shutdown:shutdown系统调用可立即终止连接。
#include <sys/socket.h>

int shutdown(int sockfd, int howto); //howto参数决定了shutdown的行为
howto可选值 含义
SHUT_RD 关闭socket读端
SHUT_WR 关闭socket写端
SHUT_RDWR 关闭socket读与写端

数据读写

TCP数据读写

读写socket可用文件读写系统调用(read/write),也可用专用的读写系统调用(recv/send)。

  • 通用读写接口read/write
  • socket专用读写系统调用

recv /send系统调用

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recv(int socket, void *buf, size_t len, int flags);
ssize_t send(int socket, const void *buf, size_t len, int flags);

UDP数据读写

recvfrom/sendto系统调用用于UDP数据报读写。

#include <sys/types.h>
#include <sys/socket.h>

ssize_t recvfrom(int socket, void *buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
ssize_t sendto(int socket, const void *buf, size_t len, int flags, struct sockaddr* dest_addr, socklen_t addrlen);

通用数据读写

#include <sys/socket.h>

ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr* msg, int flags);

地址信息函数

  • getsockname:获取sockfd对应的本端socket地址

  • getpeername:后去sockfd对应的远端socket地址

#include <sys/socket.h>

int getsockname(int sockfd, struct sockaddr* address, socklen_t* address_len);
int getpeername(int sockfd, struct sockaddr* address, socklen_t* address_len);

带外标记

socket选项

网络信息API

获取主机信息

  • gethostbyname: 根据主机名称获取主机的完整信息。优先本地的/etc/hosts文件中查找,不存在则访问DNS服务器。
  • gethostaddr:根据IP地址获取主机的完整信息
#include <netdb.h>

struct hostent* gethostbyname(const char* name);
struct hostent* gethostbyaddr(const void* addr, size_t len, int type);

获取服务信息

  • getservbyname:根据名称获取某个服务的完整信息
  • getservbyport:根据端口号获取某个服务的完整信息

注:两者都是通过读取/etc/services文件来获取服务的信息的。

#include <netdb.h>

struct servent* getservbyname(const char* name, const char* proto);
struct servent* getservbyport(int port, const char* proto);

getaddrinfo

  • 获取IP地址:通过主机名获取IP地址,内部调用gethostbyname
  • 获取端口号:通过服务名获取端口号,内部调用getservbyname
#include <netdb.h>

int getaddrinfo(const char* hostname, const char* service, const struct addrinfo* hints, struct addrinfo** result);

getnameinfo

通过socket地址同时获得以字符串表示的主机名(调用gethostbyaddr)和服务名(调用getservbyport)。

#inlcude <netdb.h>
int getnameinfo(const struct sockaddr* sockaddr, socklen_t addrlen, char* host, socklen_t hostlen, char* serv, socklen_t servlen, int flags);
posted @ 2022-04-28 21:42  LazyFish  阅读(122)  评论(0编辑  收藏  举报