1.SOCKET基本用法(based on APUE UNP)
#include <sys/socket.h>
int sock=socket(AF_INET,SOCK_STREAM,0); //默认协议是TCP socket
int sock=socket(AF_INET,SOCK_DGRAM,0); //默认协议是UDP socket
in ret=shutdown(sock,SHUT_RD); //SHUT_RDWR
int ret=close(sock); //彻底关闭socket并释放资源。
char hostname[255];
int ret = gethostname(hostname,sizeof(hostname)); //初始化hostname,获取本机更多颜色名
hostent *hostinfo=gethostbyname(hostname); //通过本机名获取hostinfo(IP地址,型类等)
gethostent sethostent endhostent
getnetbyaddr, getnetbyname getnetent setnetent endnetent
getprotobyname getprotobynumber getprotoent setprotoent endprotoent
getservbyname getservbyport getservent setservent endservent
getaddrinfo freeaddrinfo
/*************************IP与端口的格式转换****************************/
//IP转换使用ntoa函数,或ntop函数,其中ntop函数较新,支持IPV6
char ip[16];
m_ulLocalIp = *(ULONG *)hostinfo->h_addr_list[0];
char IP=inet_ntop( AF_INET,m_ulLocalIp,ip,16); //类似的还有inet_pton
char IP=inet_ntop();
char IP[32];char str[32];
IP=inet_ntop(hptr->h_addrtype, *(ULONG *)hostinfo->h_addr_list[0], IP, sizeof(str)));//需要测试
//端口转换使用ntohs
int port=ntohs(sinp->sin_port);
/***************************核心函数*******************************/
bind(sock,addr,len); //sock由socket生成,struct sockaddr* addr应在之前初始化 ,len=sizeof(addr) (即sockaddr_in的长度)
connect(sock,addr,len); //一般是客户端向服务器连接时用,通常不绑定而让connect自动绑定。
listen(sock,backlog) //int backlog 指明入队连接数,服务器用
sockConnect=accept(sock,addr,len) //addr len 可为NULL,addr指向缓冲区,len为指向大小的指针。服务器用,阻塞与否取决于sock,还可以使用select 或poll
/***********************/
TCP使用全套函数
//----------------------------
UDP只需使用bind,UDP没有流量控制,当发端发送速度较大时,接收端有可能丢包。
不使用connect :未连接,默认情况
使用connect:已连接,使用send,recv传输数据,sendto也可用,但后两个参数为NULL。与无连接的区别:1.使用固定分配的IP和端口。2.因为connect自动保存目标地址,所以只能同一时刻只能用一个,不能用于多服务器通信,一般用于客户端。3.会将异步错误返回给进程。4.使用send,recv即可。5.可以再次使用connect断开这次连接。
/**********************/
select
poll
/************************发送和接收*********************************/
send(sock,buf,nbytes,flag); //有连接时按照连接指定的目标地址,无连接时除非connect设定了目标地址,否则要使用sendto
sendto(sock,buf,nbytes,flag,destaddr,destlen); //一般用于UDP
sendmsg(sock,msg,flags);
recv(sock,buf,nbytes,flags); //
recvfrom(sock,buf,len,flags,addr,addrlen); //一般用于UDP,无连接时获取源地址,其他情况等同于recv
recvmsg(sock,msg,flags);
/************************设定SOCKET的选项***********************/
setsockopt();
int reuse=1;
getsockopt(fd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(int)); //第二个选项一般默认SOL_SOCKET,TCP使用IPPROTO_TCP
/*****************************************************************/
struct sockaddr_in src_sockaddr;
src_sockaddr.sin_family = AF_INET;
src_sockaddr.sin_addr.s_addr = ulVTUIp;
src_sockaddr.sin_port = htons(wPort);
2.UNIX的5个IO模型(based on APUE UNP)
(1)阻塞IO recv阻塞
(2)非阻塞IO recv立即返回错误,不断轮询。
(3)IO复用 在select或poll阻塞,使用recv
(4)信号驱动IO 当收到Packet时发送信号,使用信号处理函数recv
前四个被称为同步IO,因为都要在recv阻塞。后者(5)为异步IO。根据UNP,IO模型分两步:
1.Waiting for the data to be ready 2.Copying the data from the kernel to the process
UNP 6.2, POSIX定义了阻塞/非阻塞IO与同步,异步的关系
POSIX defines these two terms as follows:
A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes
An asynchronous I/O operation does not cause the requesting process to be blocked.
前四者的区别是1的区别,2都一样要recv,在IO结束前都要阻塞在recv,所以是同步.(5)不使用recv,不会阻塞,所以是异步IO
(5)异步IO 使用aio_read
典型的select和epoll
listenfd = Socket(AF_INET, SOCK_STREAM, 0); //SOCKET
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr)); //bind
Listen(listenfd, LISTENQ); //listen
int maxfd = listenfd;
int maxi = -1;
for (i = 0; i < FD_SETSIZE; i++) //保存客户端socket数组
client[i] = -1;
fd_set rset, allset;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
for(;;)
{
rset = allset; /* structure assignment */
nready = Select(maxfd + 1, &rset, NULL, NULL, NULL); //阻塞在select上
if (FD_ISSET(listenfd, &rset)) //如果可读则accept并保存连接
{
clilen = sizeof(cliaddr);
connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
for (i = 0; i < FD_SETSIZE; i++)
if (client[i] < 0)
{
client[i] = connfd; /* save descriptor */
break;
}
FD_SET(connfd, &allset);
if (connfd > maxfd)
maxfd = connfd;
if (i > maxi)
maxi = i;
if (--nready <= 0)
continue;
}
//接下来检测所有客户socket,看都能读否。
for (i = 0; i <= maxi; i++)
{
if ( (sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset))
{
if ( (n = Read(sockfd, buf, MAXLINE)) == 0)
{
Close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -1;
}
else
Writen(sockfd, buf, n);
if (--nready <= 0)
break;
}
}
}
int poll (struct pollfd *fdarray, unsigned long nfds, int timeout);
poll相比select只是将三个数组换成一个fdarray,同时支持错误返回。
3.setsockopt(socket选项设置)
IPPROTO_TCP: TCP_KEEPINTVL等 定义在linux/tcp.h或netinet/tcp.hval一般是一个int a=1;表示打开,0表示关闭len=sizeof(int)
4.epoll(new knowledge)
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
} epoll_data_t;
struct epoll_event {
uint32_t events; /* Epoll events:EPOLLIN EPOLLOUT 诸如此类 */
epoll_data_t data; /* User data variable */
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10); //得到epollfd
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {//将listensock添加到epollfd
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);//等待epollfd中socket发生事件,第一次只有listen,之后有被添加进来的socket
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (i = 0; i < nfds; ++i) {
if (events[i].data.fd == listen_sock) {
conn_sock = accept(listen_sock,(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,&ev) == -1) {//向epollfd中加入新的socket
perror("epoll_ctl: conn_sock);
exit(EXIT_FAILURE);
}
}else if( events[i].events&EPOLLIN ){ //接收到数据,读socket
n = read(sockfd, line, MAXLINE)) < 0 //读
ev.data.ptr = md; //md为自定义类型,添加数据
ev.events=EPOLLOUT|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);//修改标识符,等待下一个循环时发送数据,异步处理的精髓
}else if(events[i].events&EPOLLOUT){ //有数据待发送,写socket
struct myepoll_data* md = (myepoll_data*)events[i].data.ptr; //取数据
sockfd = md->fd;
send( sockfd, md->ptr, strlen((char*)md->ptr), 0 ); //发送数据
ev.data.fd=sockfd;
ev.events=EPOLLIN|EPOLLET;
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev); //修改标识符,等待下一个循环时接收数据
}else {
do_use_fd(events[n].data.fd);
}
}
}
参考文献:
1.http://zhoulifa.bokee.com/5345930.html
2.epoll:
http://blog.csdn.net/xuexingyang/article/details/5954396
http://54min.com/post/using-epoll-method-create-non-blocking-socket.html