分类: C/C++
2008-08-23 20:30:27
总结
TCP流套接字
0.一些细节
0.1数据结构
通用套接字接口
struct sockaddr
{
Uint8_t sa_len
unsigned short sa_family; /* 地址家族, AF_xxx */
char sa_data[14]; /*14字节协议地址*/
}
sa_family 能够是各种各样的类型,
sa_data包含套接字中的目标地址和端口信息。
这好像有点 不明智。 为了处理struct sockaddr,程序员创造了一个并列的结构: struct sockaddr_in ("in" 代表 "Internet"。)
struct sockaddr_in
{
Uint8_t sin_len
short int sin_family; /* 通信类型 */
unsigned short int sin_port; /* 端口 */
struct in_addr sin_addr; /* Internet 地址 */
unsigned char sin_zero[8]; /* 与sockaddr结构的长度相同*/
}
我们一般只需要定义sin_family, sin_port 和sin_addr选项。
套接口地址结构有sockaddr_in,sockaddr_in6,sockaddr_un(unix套接字)等等
从程序员的角度来看,通用套接口地址的唯一用途是指向特定与协议的地址结构的指针转换类型,(就是给人转换用的,比如,bind,connect函数中的等等,你用的啥协议,你就用啥套接口)
0.2参数传递
套接口地址结构传递给套接口函数时,总是通过指向结构的指针传递,然后在跟一个结构的长度,长度参数是否去地址,取决与传递方向,从进程传内核还是从内核传进程。
1从进程传内核,bind,connect,,sendto
Struct
sockaddr_in serv;
Connect(fd,(sa*)&serv,sizeof(serv));
2.从内核传进程accept,getsockname
长度要取地址
int accept(int sockfd, void *addr, int *addrlen);
0.3 字节排序
大端小端big_endian,little_endian(低字节在地位)如何判断见程序unpv65
一些转换函数hton,ntoh(主要是转换sin_port)的
0.4 IP地址处理
旧的那些土函数就都别用了,直接用最好的inet_pton,和inet_ntop吧
0.5常用函数
socket()函数
我想我不能再不提这个了-下面我将讨论一下socket()系统调用。
下面是详细介绍:
#include
#include
int socket(int domain, int type, int protocol);
socket() 只是返回你以后在系统调用种可能用到的 socket 描述符,或 者在错误的时候返回-1。全局变量 errno 中将储存返回的错误值。(请参考 perror() 的 man 帮助。)
bind()函数
一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联 起来。(如果你想用listen()来侦听一定端口的数据)
在 bind() 主题中最后要说的话是,在处理自己的 IP 地址和/或端口的 时候,有些工作是可以自动处理的。
my_addr.sin_port = 0; /* 随机选择一个没有使用的端口 */
my_addr.sin_addr.s_addr = INADDR_ANY; /* 使用自己的IP地址 */
通过将0赋给 my_addr.sin_port,你告诉 bind() 自己选择合适的端 口。同样,将 my_addr.sin_addr.s_addr 设置为 INADDR_ANY,你告诉 它自动填上它所运行的机器的 IP 地址。
为了防止出现“address already in use”在调用bind前记得调用setsockopt(listenfd,SOL,SOCKET,SO_REUSEADDR,&on,sizeof(on)
Connect()函数
调用connect将激发tcp的3次握手过程。
Listen()函数
第2个参数backlog即侦听队列的长度,它的真正含义是:侦听socket 能处理的最大并发连接请求数,其最大取值为128。
半连接队列和已连接队列
Accept()函数
用tcp服务器调用,用于从已完成连接队列对头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠
Accept()函数得处理EINTR和ECONNABORTED(软件引起的连接夭折)的错误,而不能在碰到这些错误时候推出
断开连接 close和shutdown()
你已经整天都在发送 (send()) 和接收 (recv()) 数据了,现在你准备关 闭你的套接字描述符了。这很简单,你可以使用一般的 Unix 文件描述符 的 close() 函数:
close(sockfd);
它将防止套接字上更多的数据的读写。任何在另一端读写套接字的企 图都将返回错误信息。
close是把描述字的引用技术减1,仅在该技术为0时才关闭他.
所以父进程必须关闭accept以后的套接字
如果你想在如何关闭套接字上有多一点的控制,你可以使用函数 shutdown()。它允许你将一定方向上的通讯或者双向的通讯(就象close()一 样)关闭,你可以使用:
int shutdown(int sockfd, int how);
sockfd 是你想要关闭的套接字文件描述复。how 的值是下面的其中之 一:
SHUT_RD
SHUT_WR
SHUT_RDWR
shutdown() 成 功时返回 0,失败时返回 -1(同时设置 errno。) 如果在无连接的数据报套接字中使用shutdown(), 那么只不过是让 send() 和 recv() 不能使用(记住你在数据报套接字中使用了 connect 后 是可以使用它们的)。
Getsockname()和getpeername()
2个函数参数一样
Getsockname()返回用内核赋予该连接的本地ip地址和本地端口号
函数 getpeername() 告诉你在连接的流式套接字上谁在另外一边。
gethostname()函数
甚至比 getpeername() 还简单的函数是 gethostname()。它返回你程 序所运行的机器的主机名字。然后你可以使用 gethostbyname() 以获得你 的机器的 IP 地址。
下面是定义:
#include
int gethostname(char *hostname, size_t size);
主机名。size是hostname 数组的字节长度。
函数调用成功时返回 0,失败时返回 -1,并设置 errno。
struct hostent *gethostbyname(const char *name);
很明白的是,它返回一个指向 struct hostent 的指针。这个数据结构 是这样的:
struct hostent {
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
};
#define h_addr h_addr_list[0]
这里是这个数据结构的详细资料:
struct hostent:
h_name – 地址的正式名称。
h_aliases – 空字节-地址的预备名称的指针。
h_addrtype –地址类型; 通常是AF_INET。
h_length – 地址的比特长度。
h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
h_addr - h_addr_list中的第一地址。
gethostbyname() 成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。(但是和以前不同,不设置errno,h_errno 设置错 误信息。请看下面的 herror()。)