unix下socket套接口编程详解
先上IPV4套接口的数据结构
struct in_addr{
in_addr_t s_addr;//32位的ipv4地址
}
struct sockaddr_in{// ipv4 socket结构
uint8_t sin_len//套接口的长度,为了增加osi协议支持随4.3bsd_reno添加的,posix规范也不要求有这个成员,你就当它不存在就可以了。
sa_family_t sin_family;//8位的无符号整数,AF_INET为IPV4网际套接口,AF_INET6为ipv6套接口
in_port_t sin_port;//端口号
struct in_addr sin_ addr;//IP地址
char sin_zero[8];//未使用
}
POSIX规范中只需要这个结构中的三个成员,sin_family ,sin_port, sin_ addr即套接口类型,端口号,IP地址。对于符合POSIX规范的实现来说,定义额外的结构成员是可以接受的,这对于网际套接口地址结构来说也是正常的。几乎所有的实现都增加了sin_zero成员,所以所有的套接口地址结构至少是16字节。
通用套接口地址结构:(在现在程序员看来,这个结构是没有必要的)
struct sockaddr{
uint8_t sa_len
sa_family_t sa_family;
char sa_data[14];
}
通用套接口的作用:当作为参数传递给任一个套接口函数,套接口地址结构总是通过指针来传递,但通过指针来取得此参数的套接口必须处理来自所支持的任何协议族的套接口地址结构。有一个问题是如何声明所传指针的类型,ANSI C中有很多简单的解决方法:它又通用的指针类型void*。但是套接口函数是在ANSI C之前定义的,1982年采用了这样的办法,即定义一个通用的套接口地址结构。在应用程序的开发人员来看,这些通用套接口的唯一用途就是给指向指定于协议的地址结构的指针转换类型。例如:
bind(sockfd,(struct sockaddr*)&serv,sizeof(serv));
其中通用套接口用来转换serv的指针。
从内核角度看,使用指向通用套接口地址结构的原因是:内核必须依据调用者的指针,将其转换为struct sockaddr*类型,然后检查sa_family的值来确定结构的类型。但从应用出现开发人员的角度看,指针类型为void*更简单,不需要进行明确的类型转换。
套接口编程中的主要函数:
socket函数:int socket(int family,int type,int propocol)
三个参数依次为:协议族,socket类型,协议类别,返回一个int类型socket描述符。
bind函数:int bind(int sockfd,const stuct sockaddr*myaddr,socket_t addrlen)
三个参数依次为:套接口描述符,通用套接口指针,指针大小。
connect函数: int connect(int sockfd,const stuct sockaddr*servaddr,socket_t addrlen)
三个参数依次为:套接口描述符,服务器套接口地址,服务器套接口大小
listen函数把一个未连接的套接口转换成一个被动的套接口,指示内核接收指向该套接口的连接请求
int listen(int sockfd,intbacklog);
两个参数依次为:套接口描述符,最大连接数。
accept函数:由tcp服务器调用,用于从已完成连接队列头部返回下一个已完成连接。
一个简单的服务器端程序:
int main(int argc,char**argv)
{
int listenfd,connfd;
pid_t childpid;
socklet_t clilen;
struct sockaddr_in cliaddr,servaddr;
listenfd=socket(AF_INET,SOCK_STREAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=hton1(INADDR_ANY);
servaddr.sin_port=htons(serv_port);
bind(listenfd,(SA*)&servaddr,sizeof(servaddr));
listen(listenfd,LISTENQ);
for(;;)
{
clilen=sizeof(cliaddr);
connfd=accept(listenfd,(SA*)&cliaddr,&clilen);
if((childpid==fork())==0))
{
close(listenfd);//子进程关闭监听接口
str_echo(connfd);//服务器处理函数
exit(0);
}
close(connfd);//关闭连接
}
}
上面这段代码只为了说明套接口各个函数的使用,上面的代码中是有问题的,比如僵死进程,IO复用的东西都没体现出来,另外服务器处理连接发送数据的功能也没给出具体实现。不够基本上用到了所有的socket函数,体现了服务器架构的特性:一个进程监听,获得请求时,复制一个子进程处理用户请求。