标签:
socket编程,通信
client端 socket() ----->connect() ------->recv() -----> close();
server端 socket() ----->bind() ------> listen() ---->accept() ------>send() ------->close();
1> socket(int family,int type,int protocol);
family ->协议类型
AF_INET --------------> IPV4
AF_INET6 ---------------> IPV6
AF_ROUTE ---------------> 路由套接口
type->是指套接字类型
SOCK_STREAM --------------------> 字节流套接字 TCP
SOCK_DGRAM --------------------> 数据报套接字 UDP
SOCK_RAW --------------------> 原始套接字
函数调用成功返回小的非负整数,文件描述符类型,否则返回-1表示调用失败
2.connect(int sockfd,struct sockaddr*addr,socklen_t addrlen)
sockfd 是从socket的返回值;
addr是指向服务器的套接字的地址结构的指针
addrlen是该套接字的地址结构的大小
1 struct sockaddr_in server; 2 bzero(&server,sizeof(server)); 3 server.sin_family = AF_INET; 4 server.sin_port = htonl(1234); 5 server.sin_addr.s_addr = inte_addr("127.0.0.1"); 6 if(connect(sockfd,(struct sockaddr *)&server,sizeof(server)) == -1) 7 { 8 //强制性地址转换,转为通用套接字结构 9 }
3>bind(int sockfd,struct sockaddr *server,socklen_len addrlen);
sockfd->socket的返回值
server->表示指向于特定协议的地址结构的指针
addrlen->表示该套接字的地址结构的长度
1 struct sockaddr_in server; 2 bzero(&server,sizeof(server)); 3 server.sin_family = AF_INET; 4 server.sin_port = htonl(1234); 5 server.sin_addr.s_addr = htonl(INADDR_ANY); 6 if(bind(sockfd,(struct sockaddr *)&server,sizeof(server)) == -1) 7 { 8 ///sockaddr 强制转换 9 }
4>listen(int sockfd,int backlog)
sockfd --->socket的返回值
backlog --->规定了请求返回队列的最大连接数,它对队列中等待服务请求的数目进行限制,如果一个服务请求到来时,输入队列已满,该套接字将拒绝连接请求。
listen(sockfd,5);
对于一个监听套接字,内核要维护两个队列:未完成连接队列和已完成连接队列
未完成连接队列:为每个请求建立连接的SYN分节设一个条目,服务器正等待完成三次握手,当前的套接字处在SYN_RECD状态
已完成连接队列:为每个完成TCP三次握手的客户端开设一个条目,当前的套接字状态时ESTABLISHED。
5>accept(int sockfd,struct sockaddr *client,socklen_t *addrlen)
sockfd 是socket()返回的套接字描述符,在调用listen的时候此套接字变成了监听套接字
client 是返回对方的套接字和地址结构
addrlen 是对应结构的长度
accetp()函数返回,已连接套接字描述符。
注:
一个服务器只能有一个监听套接字,而且会一直存在,直到服务器关闭,而已连接套接字描述符是内核为每个被接受的客户都创建一个,当服务器完成与客户的数据传输时,要关闭连接套接字,所以监听套接字接受客户的链接请求,已连接套接字描述符负责对应客户进行数据传送。
1 int listenfd,connfd; 2 struct sockaddr_in client; 3 socklen_t addrlen; 4 addrlen = sizeof(client); 5 connfd = accept(listenfd,(struct sockaddr *)&client,&addrlen); 6 if(connfd == -1) 7 { 8 9 }
原型如下:
各个参数含义如下:
select函数会在发生以下情况时返回:
当select返回时,描述符集合将被修改以指示哪些个描述符正处于可读、可写或有错误状态。可以用FD_ISSET宏对描述符进行测试以找到状态变化的描述符。如果select因为超时而返回的话,所有的描述符集合都将被清空。
select函数返回状态发生变化的描述符总数。返回0意味着超时。失败则返回-1并设置errno。可能出现的错误有:EBADF(无效描述符)、EINTR(因终端而返回)、EINVAL(nfds或timeout取值错误)。
设置描述符集合通常用如下几个宏定义:
1 FD_ZERO(fd_set *fdset); /* 将所有位设为0 */ 2 FD_SET(int fd, fd_set *fdset); /* 将fd位设为1 */ 3 FD_CLR(int fd, fd_set *fdset); /* 将fd位设为0 */ 4 int FD_ISSET(int fd, fd_set *fdset); /* 检测fd位是否为1 */
如:
1 fd_set set; 2 FD_ZERO(&rset); /* i初始化rset */ 3 FD_SET(1, &rset); /* 将fd = 1 的描述符设为1 */ 4 FD_SET(4, &rset); /* 将fd = 4 的描述符设为1 */ 5 FD_SET(5, &rset); /* 将fd = 5 的描述符设为1 */
当select返回的时候,rset位都将被置0,除了那些有变化的fd位。
当发生如下情况时认为是可读的:
当发生如下情况时认为是可写的:
注意:
select默认能处理的描述符数量是有上限的,为FD_SETSIZE的大小。
对于timeout参数,如果置为NULL,则表示wait forever;若timeout->tv_sec = timeout->tv_usec = 0,则表示do not wait at all;否则指定等待时间。
如果使用select处理多个套接字,那么需要使用一个数组(也可以是其他结构)来记录各个描述符的状态。而使用poll则不需要,下面看poll函数。
(二)poll()函数
原型如下:
各参数含义如下:
poll函数返回值及含义如下:
着重讲fdarray数组,因为这是它和select()函数主要的不同的地方:
pollfd的结构如下:
1 struct pollfd { 2 int fd; /* 测试描述符*/ 3 short events; /* 测试条件*/ 4 short revents; /* 测试结果 */ 5 };
其实poll()和select()函数要处理的问题是相同的,只不过是不同组织在几乎相同时刻同时推出的,因此才同时保留了下来。select()函数把可读描述符、可写描述符、错误描述符分在了三个集合里,这三个集合都是用bit位来标记一个描述符,一旦有若干个描述符状态发生变化,那么它将被置位,而其他没有发生变化的描述符的bit位将被clear,也就是说select()的readset、writeset、errorset是一个value-result类型,通过它们传值,而也通过它们返回结果。这样的一个坏处是每次重新select 的时候对集合必须重新赋值。而poll()函数则与select()采用的方式不同,它通过一个结构数组保存各个描述符的状态,每个结构体第一项fd代表描述符,第二项代表要监听的事件,也就是感兴趣的事件,而第三项代表poll()返回时描述符的返回状态。合法状态如下:
对于POLLIN | POLLPRI等价与select()的可读事件;POLLOUT | POLLWRBAND等价与select()的可写事件;POLLIN 等价与POLLRDNORM | POLLRDBAND,而POLLOUT等价于POLLWRBAND。如果你对一个描述符的可读事件和可写事件以及错误等事件均感兴趣那么你应该都进行相应的设置。
对于timeout的设置如下:
socket编程之select(),poll(),epoll()
标签:
原文地址:http://www.cnblogs.com/chenyang920/p/5469513.html