网络编程(linux)

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 *fdarrayunsigned long nfdsint timeout);

poll相比select只是将三个数组换成一个fdarray,同时支持错误返回。

3.setsockopt(socket选项设置)

int setsockopt(      int sockfd,       int level,     int option,       const void * val,      socklent_t len==(sizeof(*val))   )
level的选项:SOL_SOCKET(==1),IPPROTO_TCP(==SOL_TCP==6),IPPROTO_IP(==0),IPPROTO_IPV6(==41) 或其他协议号,定义在linux/socket.h中
option的选项:SOL_SOCKET:参照APUE P465的表格。 asm/socket.h

IPPROTO_TCP: TCP_KEEPINTVL等 定义在linux/tcp.h或netinet/tcp.h
val一般是一个int a=1;表示打开,0表示关闭
len=sizeof(int)


 4.epoll(new knowledge)

APUE,UNP是好书,但是OUT了。他们都没提到epoll。而epll是2.6内核新添加的高性能网络处理函数,也是目前被主流广泛采用的。一般喜欢把它拿来和windows上著名的IOCP做对比。select要遍历所有在侦听的socket,非常低效
典型的epoll:man epoll中的例子(发送,接收是结合网上的例子添加的):
要注意两个结构体:

 typedef union epoll_data {
               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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值