linux高性能服务器编程第五章(Linux网络编程基础API)
SOCKET地址API主机字节序和网络字节序:大端字节序是一个数的高位字节存储在内存的低地址位;小端字节序是一个数的高位字节序在内存的高位地址位;主机字节序是小端字节序;网络字节序是大端字节序;JAVA虚拟机是大端字节序。Linux下提供了4个函数完成大小端的转换,即为:#include <netinet/in.h>unsigned long int htonl(unsigned lo
SOCKET地址API
主机字节序和网络字节序:
大端字节序是一个数的高位字节存储在内存的低地址位;小端字节序是一个数的高位字节序在内存的高位地址位;主机字节序是小端字节序;网络字节序是大端字节序;JAVA虚拟机是大端字节序。
Linux下提供了4个函数完成大小端的转换,即为:
#include <netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short htons(unsigned short hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);
通用SOCKET地址:
#include <bits/socket.h>
struct sockaddr
{
sa_family_t sa_family;//AF_UNIX AF_INET AF_INET6
char sa_data[14]; //socket地址值,由于各个协议族长度不一,有如下的结构体
}
struct socketaddr_storage
{
sa_family_t sa_family;
unsigned long int __ss_allgn;
char __ss_pading[128 -sizeof(__ss_align)];
}
专用socket地址:
#include <sys/un,h>
struct sockaddr_un
{
sa_family_t sin_family;
char sun_path[108];
}
struct sockaddr_in
{
sa_family_t sin_family;
u_int16_t sin_port;
struct in_addr sin_addr;
}
struct in_addr
{
u_int32_t s_addr;
};
struct sockaddr_in6
{
sa_family_t sin6_family
u_int16_t sin6_port;
u_int32_t sin6_flowinfo;
struct in6_addr sin6_addr;
u_int32_t sin6_scope_id;
};
struct in6_addr
{
unsigned char sa_addr[16];
};
IP地址转换函数:
#include<arpa/inet.h>
in_addr_t inet_addr(const char* strptr);
int inet_aton(const char* cp ,struct in_addr* inp);
char* inet_ntoa(struct in_addr in);
int inet_pton(int af ,char*src,void* dst);
const char* inet_ntop(int af , const char *src ,char *dst ,socklen_t cnt);
其中cnt参数为下列两个参数
#include <netinet/in.h>
#define INET_ADDRSTRLEN 16
#define INET6_ADDRSTRLEN 46
创建socket
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain ,int type ,int protocol);
domain:PE_INET / PE_INET6 / PE_UNIX
type:SOCK_STREAM / SOCK_UGRAM / SOCK_DGRAM
protocol:0
失败返回-1
命名socket
#include <sys/types.h>
#include <sys/socket.h>
int bind(int socketfd,const struct sokaddr* my_addr ,socklen_t addlen);
成功返回0,失败为-1,errno是EACCES和EADDRINUSE
监听socket
#include <sys/socket.h>
int listen(int sockfd,int backlog);
backlog指的是监听队列的最大值,经典值为5,监听个数个backlog+1
接受连接
#include <sys/types.h>
#include <sys/socket.h>
int accept(int sockfd , struct sockaddr * addr ,socklen_t addrlen);
accept不关心socket的状态,即使断网了他也可以返回成功
发起连接
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd,const sockaddr *serv_addr , socklen_t addrlen);
关闭连接
#include <sys/socket.h>
int close(int fd);并不是立即关闭,而是将socket的fd的引用计数减1,必须父子进程都关闭才生效,如果要关闭,那么用
int shutdown(int sockfd ,int howto);
其中howto的值可以设置为SHUT_RD (关闭读,可以写)/ SHUT_WR(关闭写,可以读) / SHUT_RDWR(读写都关闭)
成功返回0,失败返回-1,并设置errno的值;
数据读写
tcp数据读写
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd , void * buf ,size_t len ,int flags);
flags通常设置为0,返回值>0,返回值=0表示对方关闭连接了,返回值=-1代表出错了;
ssize_t send(int sockfd ,const void*buf , size_t len, int flags);
flags通常设置为0,返回值大于0代表写入成功,失败返回-1
flags的值可以是:
MSG_CONFIRM 数据链路层监听对方回应,直到有回复。仅仅用于SOCK_DGRAM和SOCK_RAW
MSG_DONTROUTE直接发送到本机,不经过路由,明确直到数据是发往本机的
MSG_MORE 告诉内核应用程序还有更多的数据要发送,内核超时等待写入TCP缓冲区一并发送,解决发过多的小报文的问题
MSG_WAITALL 读操作读到指定的字节才返回
MSG_PEEK 窥探缓存中的数据,此次读操作不会导致数据被清除
MSG_OOB 发送或接受紧急数据
NSG_NOSUGNAL 往读端关闭管道,或者socket连接中写数据不引发SIGPIPE信号
UDP的读写
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom (int sockfd , void *buf ,size_t len ,int flags, struct sockaddr* src_addr .socklen_t* addrlen);
ssize_t sendto (int sockfd , const void *buf ,size_t len , int flags,cosnt struct sockaddr* dst_addr .socklen_t* addrlen);
值得一提的是这两个函数也可设置为stream之间的读写,只需要把最后的指针设置为NULL
外带数据
可以用 int sockatmark(int sockfd);判断下一个读到的数据是否是外带数据,如果是返回1 ,否则返回0;
地址信息函数
int getsockname(int sockfd , sockaddr* address ,socklen_t address_len); //获取本端的socket地址
int getpeername(int sockfd .sockaddr* address , socklen_t address_len); //获取对段的socket地址
socket选项
SO_DEBUG
SO_REUSEADDR
SO_TYPE
SO_ERROR
SO_DONTROUTE
SO_RECVBUF
SO_SNDBUF
SO_KEEPLIVE
SO_OBBINLINE
SO_LINGER
SO_RCVLOWAT
SO_SNDLOWAT
SO_RCVTIMEO
SO_SNDTIMEO
IP_TOS
IP_TTL
IPV6_NEXTHOP
IPV6_RECVPKTINFO
IPV6_DONTFRAG
IPV6_RECVCLASS
TCP_MAXSEG
TCP_NODELAY
重要的socket选项:
0)SO_REUSEADDR
解决TIME_WAIT
int sock = socket(PF_INET , SOCK_STREAM , 0);
assert(sock >= 0);
int reuse = 1;
setsockopt(sock , SO_SOCKET , SO_REUESEADDR , &reuse , sizeof(reuse));
socketaddr_in address;
bzero(&address , sizeof(address));
address,sin_family = AF_INET;
....
也可以在 /proc/sys/net/ipv4/tcp_wt_recycle中设置;
1)SO_RCVBUF 和SO_SNDBUF
setsockopt(sockfd , SO_SOCKET , SO_RCVBUF , &buf ,sizeof(buf));
getsockopt(sockfd , SO_SOCKET ,SO_RCVBUF , &buf,sizeof(buf));
2)SO_RCVLOWAT和SO_SNDLOWAT
这两个选项的作用是,一般在IO复用时候使用,当超过低水位时,就通知可读和可写事件
3)SO_LINGER
strucr linger
{
int l_offset;//开启(非0)关闭(0)
int l_linger; //滞留时间
}
l_offset = 0 ,按默认的来
l_osset >0 ,l_linger =0;立即关闭close,丢弃数据,并向对端发送复位
l_offset>0,l_linger >0;若是阻塞的,等待l_linger时间,close返回-1
若是非堵塞的,close立即返回,通过返回值和errno判断数据是否发送完毕
网络信息API
#include <sockdb.h>
hostent* gethostbyname(const char * name);
hostent* gethostbyaddr(const void * addr ,size_t len ,int type);
其中
struct hostent{
char* h_name; //主机名字
char**h_aliases;//主机别名,可有多个
int h_adrtype; //地址类型
int h_length; //地址长度
char** h_addr_list;//按网络字节序列出的IP地址
};
servent* getservbyname(const char* name ,const char * proto);
servent* getservbyport(int port ,const char* proto);
struct servent
{
char* s_name; //服务名字
char** s_aliases;//服务别名列表
int s_port;//端口号
char* s_proto;//服务类型,tcp/udp
}
getaddrinfo
int getaddrinfo(const char* hostname , const char * service ,const struct addrinfo* hints , addrinfo ** result);
struct addrinfo{
int ai_flags;
int ai_family;
int ai_socktype;//SOCK_STREAM SOCK_DGRAM
int ai_protocol; // 0 nomal
socklen_t ai_addelen;
char* al_canonname;
struct so
ckaddr* ai_addr;
struct sockinfo *ai_nest;
}
flag选项:
AI_PASSIVE
AI_CANONNAME
AI_NUMERICSERV
AI_V4MAPPED
AI_ALL
AO_ADDRCONFIG
getnameinfo
int getnameinfo(const sockaddr* sockaddr , socklen_t addrlen , char* host ,socklen_t hostlen ,char serv, socklen_t serlen ,int flags);
NI_NAMEREQD
NI_DGRAM
NI_NUMERICHOST
NI_NUMEROCSERV
NI_NOFQDN
更多推荐
所有评论(0)