Linux高性能服务器编程学习记录——五、linux网络编程基础api

本文详细介绍了Linux网络编程的基础API,包括网络字节序、socket地址结构体、IP地址转换函数、创建socket连接的步骤,以及数据读写、socket选项和其他相关API的使用。内容涵盖TCP/IP协议族的socket地址结构、数据读写操作,如TCP和UDP的读写,以及socket选项如SO_REUSEADDR和TCP_NODELAY等。
摘要由CSDN通过智能技术生成

1、网络字节序

字节序点这里

2、socket地址结构体

TCP/IP协议族有sockaddr_in和sockaddr_in6两个专用的socket地址结构体,分别用于IPv4和IPv6。

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地址,要用网络字节序表示
};

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; 	//scope ID,尚处于实验阶段
};
struct in6_addr
{
   
	usigned char sa_addr[16]; 	//IPv6地址,要用网络字节序表示
};

3、IP地址转换函数

#include <arpa/inet.h>
int inet_pton(int af, const char* src, void* dst);
const char* inet_ntop(int af, const void* src, char* dst, socklen_t cnt);

inet_pton函数将用字符串表示的IP地址src转换成用网络字节序整数表示的IP地址,并将结果存于dst指向的内存中。成功返回1,失败返回0。
af参数指定地址族,可以是AF_INET或AF_INET6。
inet_ntop进行相反的转换,前三个参数与inet_pton参数相同,最后一个cnt指定目标存储单元的大小。下面的两个宏能帮我们指定这个大小:

#include<netinet/in.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46

inet_ntop成功时返回目标存储单元的地址,失败返回NULL,并设置errno。

4、创建socket连接

创建一个socket,
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

domain指定要使用的底层协议族,对于TCP/IP协议族而言,为PF_INET(Protocol Family of Internet)或PF_INET6,对于UNIX本地域协议族为PF_UNIX。
type指定服务类型,主要是SOCK_STREAM(流服务,TCP),SOCK_UGRAM(数据报,UDP)。自Linux内核版本2.6.17起,type参数可以接受上述服务类型与SOCK_NONBLOCK和SOCK_CLOEXEC相与的值,它们分别表示将新创建的socket设为非阻塞的,以及用fork创建子进程时在子进程中关闭该socket。
protocol表示一个具体的协议,但是通常前两个参数就唯一确定了第三个值,所以一般设为0表示使用默认协议。
socket系统调用成功返回一个socket文件描述符,失败返回-1并设置errno。

命名(绑定)socket,即将新创建的socket绑定到指定地址
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen);

bind将addr指定的地址绑定到sockfd上,addrlen为sizeof(addr)。对于TCP字节流(UDP数据报)通信,socket类型是AF_INET和SOCK_STREAM(SOCK_DGRAM),socket上绑定的是IP、Port等信息,对于本地进程间通信,socket类型是AF_LOCAL和SOCK_STREAM(SOCK_DGRAM),socket上绑定的是一个本地文件,这也是本地socket和网络socket之间的最大区别。
另外AF_UNIX也属于本地socket,与AF_LOCAL是等价的。
要注意的是sockaddr是旧的地址结构,从新的结构到旧的结构直接强制转换就可以了,看后面的代码就清楚了。
bind成功返回0,失败返回-1,并设置errno。

监听socket
#include <sys/socket.h>
int listen(int sockfd, int backlog);

sockfd指定被监听的socket。(面试预警)backlog参数提示内核监听队列的最大长度,即accept队列长度。监听队列的长度如果超过backlog,服务器将不受理新的客户端连接,客户端也将收到ECONNREFUSED错误信息。在内核版本2.2前,backlog指所有处于半连接状态(SYN_RCVD)和全连接状态的socket(ESTABLISHED)的上限。2.2之后只表示全连接队列上限,半连接队列上限由/proc/sys/net/ipv4/tcp_max_syn_backlog定义,典型值是5。但是我的默认值是256。

接收连接
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t* addrlen);

addr用来获取被接受连接的远端socket地址,长度由addrlen指定。
accept成功时返回一个新的连接socket文件描述符,失败时返回-1,设置errno。
accept函数是阻塞的。
(面试预警)处于ESTABLISHED状态的连接在被accept前,如果出现异常,如掉线(仍处于ESTABLISHED)或提前退出(处于CLOSE_WAIT),accpet是否能正常返回。答案为是的,能正确返回,因为accpet只负责从对队列中取出连接,而不检查连接状态,更不关心网络状况的变化。
由上面的CLOSE_WAIT想到另外一个问题,即处于accept队列里的连接在收到客户端的断开请求后只是回了个ACK,却并不能发送自己的FIN,因此会一直处于CLOSE_WAIT即半关闭状态,直到它被accept后调用close或着程序退出。下面是我的测试代码

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <stdbool.h>
#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值