APUE套接字

pag437
套接字描述符在unix系统是用 文件描述符实现的。
int socket( int domain, int type, intprotocol );//创建套接字

数据报(SOCK_DGRAM)接口,与对方通信时是不需要逻辑连接的(UDP)。只需要送出一个报文,其地址是一个对方进程所使用的套接字。(无连接)
字节流(SOCK_STREAM)要求在交换数据之前(TCP),在本地套接字和与之通信的远程套接字之间建立一个逻辑连接。(面向连接)

数据报是一种自包含报文。发送数据报近似于 给某人邮寄信件每封信件包含接收者地址,使这封信件独立于所有其他信件。
面向连接的协议通信就像 与对方打电话。 会话中不包含地址信息。就像呼叫两端存在一个点对点虚拟连接,并且 连接本身暗含特定的源和地址

套接字通信是双向的。
int shutdown( int sockfd, int how);//禁止套接字上的输入、输出,只操作本端套接字

close和shutdown:
1、close只有在最后一个活动引用被关闭时才释放网络端点,套接字直到关闭了最后一个引用他的文件描述符之后才会被释放。
2、shutdown允许使一个套接字处于不活动状态,无论引用他的文件描述符数目多少。

进程的标识有两个部分:计算机的网络地址可以帮助标识网络上想与之通信的 计算机,而服务可以帮助标识计算机上特定的 进程

大端字节序:最大字节地址对应于数字最低有效字节上;
-----------------------
| n  |n+1|n+2|n+3|
------------------------
MSB                  LSB
小端字节序:数字最低字节对应于最小字节地址。
------------------------
|n+3|n+2|n+1| n  |        
-----------------------
MSB                  LSB
不管字节如何排序,数字最高位总是在左边,最低位总是在右边。

TCP/IP协议采用大端字节序。

套接字实现可以 自由地添加额外的成员,并且定义sa_data成员的大小。

通用地址结构:
struct sockaddr {
	sa_family_t	sa_family; //sa取自(s)ock(a)ddr
 	char		sa_data[]
	...
}
IPV4地址结构:
struct sockaddr_in {                //in取自(in)et  
	sa_family_t	sin_family; //sin取自(s)ockaddr_(in)
	in_port_t	sin_port;
	struct in_addr	sin_addr;
};
struct in_addr {
	in_addr_t	s_addr;
};
IPV6地址结构:
struct sockaddr_in6 {
	sa_family_t	sin6_family;
	in_port_t	sin6_port;
	uint32_t	sin6_flowinfo;
	struct in6_addr	sin6_addr;
	uint32_t	sin6_scope_id;
};
struct in6_addr {
	uint8_t		s6_addr[16];
};
转换端口时常用:
uint32_t htonl( uint32_t hostint32);//以网络字节序表示32位整形数
uint16_t htons( uint16_t hostint16);//以网络字节序表示16位整形数
uint32_t ntohl( uint32_t netint32);//以主机字节序表示32位整形数
uint16_t ntohs( uint16_t netint16);//以主机字节序表示16位整形数
h(host)主机字节序
n(network)网络字节序
l(long)长整数
s(short)短整数

转换地址常用:
const char * inet_ntop( int domain, const void *restrict addr, char*restrict str, socklen_t size );//将网络字节序的二进制地址转换成文本字符串格式
int inet_pton( int domain, const char *restrict str, void *restrictaddr );//将文本字符串格式转换成网络字节序的二进制地址
p:presentation
n:net

struct hostent *gethostent ( void);//打开主机数据文件返回下一条目
void sethostent( int stayopen);//打开文件,如已打开,将其回绕
void endhostent( void );//关闭文件

获取网络名字和网络号:
struct netent * getnetbyaddr( uint32_tnet, int type );
struct netent * getnetbyname( constchar *name );
struct netent * getnetent( void);
void setnetent( int stayopen );
void endnetent( void );

映射协议名字和协议号:
struct protoent * getprotobyname( constchar *name );
struct protoent * getprotobynumber( intproto );
struct protoent * getprotoent( void);
void setprotoent( int stayopen);
void endprotoent( void );

服务是由地址的端口号部分表示的。每个服务由一个唯一的、熟知的端口号来提供。
struct servent * getservbyname( constchar *name, const char *proto );
struct servent * getservbyport( intport, const char *proto );
struct servent * getservent( void);
void setservent( int stayopen );
void endservent( void );

将主机名字和服务名字映射到一个地址:
int getaddrinfo( const char *restricthost, const char *restrict service,
                            const struct addrinfo *restrict hint, struct addrinfo **restrict res );
需提供主机名字、服务名字,或者两者都提供。如果仅仅提供一个名字,另外一个必须是一个空指针。
addrinfo结构:
struct addrinfo {
	int		ai_flag;   //ai取自(a)ddr(i)nfo
	int		ai_family;
	int		ai_socktype;
	int		ai_protocol;
	socklen_t	ai_addrlen;
	struct sockaddr	*ai_addr;
	char		*ai_canonname;   //canonname :标准名
	struct addrinfo	*ai_next;
	...
};
hint是一个用于 过滤地址的模板,仅使用ai_family、ai_flags、ai_protocol和ai_socktype字段。剩余字段必须为0或NULL。

void freeaddrinfo( struct addrinfo *ai);
const char * gai_strerror( int error);//将返回错误码转换成消息
将地址转换成主机名或服务名:
int getnameinfo( const struct sockaddr*restrict addr, socklen_t alen,
                              char *restrict host, socklen_thostlen,
                              char *restrict service, socklen_t servlen,
                              unsigned intflags );

pag449
socket:   手机
bind:          手机+SIM卡
listen:     将卡的号码告诉其他人
accept:   拿着手机等对方电话

connect:主动打电话给对方


客户端应有一种方法来发现用以连接服务器的地址,最简单的方法就是 为服务器保留一个地址并且在/etc/services或某个名字服务中 注册

int bind( int sockfd, const structsockaddr *addr, socklen_t len );//将地址绑定到一个套接字
限制:
1、在进程所运行的机器上,指定的地址必须有效,不能指定一个其他机器的地址。
2、地址必须和创建套接字时的地址族所支持的格式相匹配
3、端口号必须不小于1024,除非该进程具有超级用户特权
4、一般只有套接字端点能够与地址绑定,尽管有些协议允许多重绑定

int getsockname( int sockfd, structsockaddr *restrict addr, socklen_t *restrict alenp);//发现绑定到一个套接字的地址
int getpeername( int sockfd, structsockaddr *restrict addr, socklen_t *restrict alenp);//套接字已连接,可用此函数找到对方地址

面向连接的服务网络 (SOCK_STREAM、SOCK_SEQPACKET),在开始交换数据以前,需要在请求服务的进程套接字(客户端)和提供服务的进程套接字(服务器)之间建立一个连接。( 三路握手
SOCK_DGRAM套接字上调用connect,所有发送报文的目标地址设为connect调用中所指定的地址,这样每次传送报文时就不需要再提供地址。仅能接收来自指定地址的报文。( 没有三路握手,内核只是检查是否有立即可知的错误, 记录对端的IP地址和端口号,然后立即返回到调用进程)

int connect( int sockfd, const structsockaddr *addr, socklen_t len );//主动发起三路握手
int listen( int sockfd, int backlog);//宣告可以调用的连接请求,backlog表示该进程所要入队(已完成连接队列+未完成连接队列)的连接请求数量。
                                                          //一旦服务器调用了listen,套接字就能接收连接请求。
int accept( int sockfd, structsockaddr *restrict addr, socklen_t *restrict len);//获得连接请求并建立连接,一个监听套接字,多个已连接套接字

六个数据传送函数:
ssize_t send( int sockfd, const void*buf, size_t nbytes, int flags );//使用时套接字必须已建立连接
ssize_t sendto( int sockfd, const void*buf, size_t nbytes, int flags,
                            const struct sockaddr *destaddr,socklen_t destlen );//允许在无连接的套接字上指定一个目标地址
ssize_t sendmsg( int sockfd, conststruct msghdr *msg, int flags );指定多重缓冲区传输数据(类似writev)
msghdr结构:
struct msghdr {
	void		*msg_name;
	socklen_t	msg_namelen;
	struct iovec	*msg_iov;
	int		*msg_iovlen;
	void		*msg_control;  //辅助数据(传送描述符时可用)
	socklen_t	msg_controllen;
	int		msg_flags;
	...
};
ssize_t recv( int sockfd, void *buf,size_t nbytes, int flags );//接收数据
ssize_t recvfrom( int sockfd, void*restrict buf, size_t len, int flags,
                               struct sockaddr *restrict addr, socklen_t *restrict addrlen );//得到数据发送者的源地址(通常用于无连接套接字)
ssize_t recvmsg( int sockfd, structmsghdr *msg, int flags );//将接收到的数据送入多个缓冲区(类似readv)

pag464
int setsockopt( int sockfd, int level,int option, const void *val, socklen_t len );//设置套接字选项(SO_REUSERADDR最好在每个bind前指定,重用bind中的地址)
  1. 通用选项,工作在所有套接字类型上
  2. 在套接字层次管理的选项,但是依赖于下层协议的支持
  3. 特定与某协议的选项,为每个协议所独有

如果选项是通用的套接字选项,level设置成SOL_SOCKET。否则,level设置成控制这个选项的协议号。例如,TCP(IPPROTO_TCP),IP(IPPROTO_IP)。

int getsockopt( int sockfd, int level,int option, void *restrict val, socklen_t *restrict lenp);//获取当前设置选项

带外数据:
         TCP支持,UDP不支持。TCP仅支持一个字节的紧急数据(带外数据),多个取最后一个。
fcntl(sockfd, F_SETOWN, pid);//安排进程接收一个套接字信号。

通过两个步骤使用异步IO
  1. 建立套接字拥有者关系,信号可以被传送到合适的进程。(fcntl,F_SETOWN)
  2. 通知套接字当IO操作不会阻塞时发信号告知。(fcntl,F_SETFL,O_ASYNC)

int sockatmark( int sockfd);//判断是否接收到紧急标记

客户端--------------------------------------------------服务器
gethostname-----------------------------------------gethostname
getaddeinfo-------------------------------------------getaddrinfo
socket--------------------------------------------------socket
                    --------------------------------------------------bind
                    --------------------------------------------------listen
connect------------------------------------------------accept
recv-----------------------------------------------------send
close----------------------------------------------------close

getaddrinfo出现错误:
http://blog.csdn.net/andyxie407/article/details/1672325

pag476
unix域套接字:(仅仅复制数据)
非命名unix域套接字:(没有名字意味着无关进程不能使用他们)
int socketpair (int domain, int type, int protocol, int sockfd[2]);//创建非命名互联的unix域套接字,进程端:0输入,1输出

命名unix域套接字:
struct sockaddr_un {  //sun取自(s)ockaddr_(un)
	sa_family_t	sun_family;  //AF_UNIX(AF_LOCAL)
	char		sun_path[108];//pathname
}
系统用sun_path中的路径名创建一类型为S_IFSOCK的文件。
该文件仅用于向 客户进程告知套接字名字。该文件不能打开,也不能由应用程序用于通信。

关闭套接字时并不自动删除文件,所以要由我们来删除。
(一般在程序结束前调用unlink,如果有open的话在open后就可以调用unlink,不然会导致文件已存在无法bind)

确定绑定地址长度的方法,先确定sun_path成员在sockaddr_un结构中的 偏移量(offsetof),然后将此与 路径名长度(不包括终止null字符)相加。


unix域套接字传送文件描述符

struct msghdr {
	void		*msg_name; //发送数据报文目的地址
	socklen_t 	msg_namelen;
	struct iovec	*msg_iov;
	int			*msg_iovlen;
	void		*msg_control;    //辅助数据
	socklen_t	msg_controllen;
	int			 msg_flags;
}

struct cmsghdr {         //控制信息首部
	socklen_t	cmsg_len;  //辅助数据长度
	int			 cmsg_level;  //SOL_SOCKET
	int			cmsg_type;  //SCM_RIGHT
	/* data */         //这里为(int)描述符
}

unsigned char *CMSG_DATA(struct cmsghdr *cp);                                          //获取cmsghdr结构数据部分指针
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *mp);                                //获取与msghdr结构相关联的第一个cmshdr结构指针
struct cmsghdr *CMSG_NXTHDR(struct msghdr *mp, struct cmsghdr *cp);//获取与msghdr结构关联的下一个cmsghdr结构指针
unsigned int CMSG_LEN(unsigned int nbytes);                                                //不计填充,返回为cmsg_len中的值。nbytes为上述结构中data的大小
unsigned int CMSG_SPACE(unsigned int length);                                           //计填充,返回为辅助对象动态分配空间的大小值。length同上


你可能感兴趣的:(APUE套接字)