title: linux/unix编程手册-55_56 date: 2018-10-05 11:53:07 categories: programming tags: tips
linux/unix编程手册-56(SOCKET 介绍)
socket是一种IPC方法。允许单台或通过网络连接的主句进程间交换数据
- 各应用程序创建一个socket
- 服务器将自己的socket绑定到一个都知道的地址上
fd=socket(domain, type, protocol)
系统调用
#include<sys/socket.h>
int socket(int domain, int type, int protocal)
//return fd success, -1 error
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//return 0 s, -1 e
struct sockaddr{
sa_family_t sa_family;
char sa_data[14];
};
socket
- domain:识别出socket的地址格式,表明通信范围
- type:
SOCKET_STREAM(TCP)和SOCKET_DGRAM(UDP)- protocol 一般为0
对于fd,
getsockname,getpeername
流socket

#include<sys/socket.h>
int listen(int sockfd, int backlog);
//return 0 s, -1 e
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//return fd success, -1 error
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//return 0 s, -1 e
主动和被动socket
- 一个主动socket通过connect 建立一个到被动socket的连接
- 一个被动socket通过listen 被标记成允许接入连接的socket
listen
- backlog: 如果在服务器调用accept前,客户端connect,会产生一个未决的连接,backlog限制这个数量,在这个数量内,客户端连接请求会立即成功,数量外的会阻塞到一个未决连接被accept,并从未决连接队列删除
accept
- 会创建一个新的socket,这个新的socket会与connect另一端的socket连接
- addr和addrlen在调用过后会为对端地址信息,如果不关心对端信息传入时设为NULL,0
数据报socket

#nclude<sys/socket.h>
ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
//return num of bytes received, 0 EOF, -1 error
ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags, struct sockaddr *dest_addr, socklen_t *addrlen);
//return num of bytes send, -1 error
- 客户端如果需要接受服务端发送的数据报的话还需要
bind一下- 数据报可以
connect之后调用write和直接send_to一样,只适用于发起connect的一端,connect之后内核会记录socket的对端,多次调用connect后面的会修改前面的
linux/unix编程手册-57(SOCKET UNIX DOMAIN)
struct sockaddr{
sa_family_t sa_family;
char sa_data[108];
};
const char *SOCKNAME = "/tmp/mysock";
int sfd;
struct sockaddr_un addr;
sfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sfd == -1)
errExit("socket");
/* Create socket */
memset(&addr, 0, sizeof(struct sockaddr_un));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, SOCKNAME, sizeof(addr.sun_path) - 1);
if (bind(sfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_un)) == -1)
errExit("bind");
- 无法将socket绑定到一个已有路径名上,bind会返回EADDRINUSE
- 无法open 打来一个socket
- 当不需要一个socket时使用unlink
- 如果sun_path的第一个字符为NULL字节,会创建抽象socket命名空间
- 无需担心名字和文件系统冲突
- socket关闭后自动删除抽象名
- 权限无需写权限
其他
#include<sys/socket.h>
int socketpair(int domain, int type, int protocol, int sockfd[2]);
- domian只能是AF_UNIX
- 调用完sockpair之后,进程会fork一个子进程,子进程继承fd副本进行通信
linux/unix编程手册-58(SOCKET TCP/IP网络基础)略
netstat -i
linux/unix编程手册-59(SOCKET Internet Domain)
网络字节序
- 由于端口号和IP地址需要被网络中的所有主机理解,需要统一标准字节序,即网络字节序,它是大端的
#include<arpa/inet.h>
// return netword byte order
uint16_t htons(uint16_t host_uint16);
uint32_t htonl(uint32_t host_uint32);
// return host byte order
uint16_t ntohs(uint16_t net_uint16);
uint32_t ntohl(uint32_t net_uint32);
internet socket 地址
#include<netinet/in.h>
struct in_addr { /* IPv4 4-byte address */
in_addr_t s_addr; /* Unsigned 32-bit integer */
};
struct sockaddr_in { /* IPv4 socket address */
sa_family_t sin_family; /* Address family (AF_INET) */
in_port_t sin_port; /* Port number */
struct in_addr sin_addr; /* IPv4 address */
unsigned char __pad[X]; /* Pad to size of 'sockaddr'
structure (16 bytes) */
};
struct in6_addr { /* IPv6 address structure */
uint8_t s6_addr[16]; /* 16 bytes == 128 bits */
};
struct sockaddr_in6 { /* IPv6 socket address */
sa_family_t sin6_family; /* Address family (AF_INET6) */
in_port_t sin6_port; /* Port number */
uint32_t sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
uint32_t sin6_scope_id; /* Scope ID (new in kernel 2.4) */
};
#include<arpa/inet.h>
// 二进制ip地址和可读ip地址('127.0.0.1')转换
int inet_pton(int domain, const char *src_str, void *addrptr);
//return 1 success, 0 格式str不催, -1 error
const char *inet_ntop(int domain, const void *addrptr, char *dst_str, size_t len);
//return dst_str的指针success, NULL error
域名系统(DNS)
- 解析也是从顶级域名开始一级级解析
- 域名的补全规则定义在
/etc/resolv.conf文件中,默认会用本机域名补全
/etc/services记录了(IANA)端口号和服务名
主机和服务的转换
#include<sys/socket.h>
#include<netdb.h>
int getaddrinfo(const char *host, const char *service, const struct addrinfo *hints, struct addrinfo **result);
//return 0 success, other error,可能需要向DNS服务器发请求
struct addrinfo {
int ai_flags; /* Input flags (AI_* constants) */
int ai_family; /* Address family (AF_INET | AF_INET6)*/
int ai_socktype; /* Type: SOCK_STREAM, SOCK_DGRAM */
int ai_protocol; /* Socket protocol */
size_t ai_addrlen; /* Size of structure pointed to by ai_addr */
char *ai_canonname; /* Canonical name of host */
struct sockaddr *ai_addr; /* Pointer to socket address structure */
struct addrinfo *ai_next; /* Next structure in linked list */
};
int freeaddrinfo(struct addrinfo *result);
//释放result分配的内存
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, size_t hostlen, char *service, size_t servlen, int flags);
// 0 success, other fail,不想要获取的参数传NULL,len设为0
getaddrinfo
- host 为主机名或者ip字符串
- service 为一个服务名或者十进制端口号
- hint指向的结构规定了,result返回的socket地址结构的标准。hint可以设置
ai_flags,ai_family,ai_socktype,ai_protocol字段用作过滤- result指向一个包含addrinfo结构链表的表头
CS实现代码(略)
linux/unix编程手册-60(SOCKET 服务器的设计)(大部分略)
并发型服务器的其他设计方案
- 预先创建进/线程池,动态改变池大小
- 单个进程处理多个客户端(I/O多路复用,信号驱动,或者epoll)
- 服务器集群
- DNS轮转负载共享,缓存,无法良好的负载均衡,无法确保高可用等一些问题
- 负载均衡
inetd守护进程(看不到在用的)
用来消除运行大量非常用服务器进程的需要
- 监视一组指定套接字端口,按需要启动其它服务
- 简化了启动其它服务的编程工作
inetd守护进程通常在系统启动时运行。之后执行如下步骤
- 在
/etc/inetd.conf中指定的每项服务,inetd都会创建一个恰当类型的套接字,然后绑定到指定端口上,TCP的话会listen- 通过
select监视select阻塞直到有请求,在TCP中会先accept创建新的socket- inetd调用
fork创建一个新的进程,exec()启动服务器程序,在exec()之前
- 除了socket描述符外,关闭其他继承的文件描述符
- 在0,1,2上复制套接字文件描述符,关闭套接字文件描述符本身(这样相应代码直接从标准输出输入读取写入客户端信息就好了)
- 为启动进程设定与用户和组ID
- 如果是tcp并且
accept创建了socket fd,inetd关闭这个描述符- 继续
select监视



