不得不提的是select
#include <sys/select.h>
int select(int maxfdp1, fd_set *readset, fd_set *writeset,
fd_set *exceptset,struct timeval *timeout);
有三种情况:
- timeout == NULL 等待无限长的时间。等待可以被一个信号中断。当有一个描述符做好准备或者是捕获到一个信号时函数会返回。如果捕获到一个信号, select函数将返回 -1,并将变量 erro设为 EINTR。
- timeout == NULL在什么情况的时候会被打断()
- timeout->tv_sec == 0 &&timeout->tv_usec == 0不等待,直接返回。加入描述符集的描述符都会被测试,并且返回满足要求的描述符的个数。这种方法通过轮询,无阻塞地获得了多个文件描述符状态。
- timeout->tv_sec !=0 || timeout->tv_usec!= 0 等待指定的时间。当有描述符符合条件或者超过超时时间的话,函数返回。在超时时间即将用完但又没有描述符合条件的话,返回 0。对于第一种情况,等待也会被信号所中断。
- 返回 0,表示超时
- 返回-1,所有描述集清0
- 当返回为正数时,表示已经准备好的描述符数。
AF_INET域与AF_UNIX域socket通信原理对比
1 建立socket传递的地址域,及bind()的地址结构稍有区别:
socket() 分别传递不同的域AF_INET和AF_UNIX
bind()的地址结构分别为sockaddr_in(制定IP端口)和sockaddr_un(指定路径名)
2 AF_INET需经过多个协议层的编解码,消耗系统cpu,并且数据传输需要经过网卡,受到网卡带宽的限制。AF_UNIX数据到达内核缓冲区后,由内核根据指定路径名找到接收方socket对应的内核缓冲区,直接将数据拷贝过去,不经过协议层编解码,节省系统cpu,并且不经过网卡,因此不受网卡带宽的限制。
3 AF_UNIX的传输速率远远大于AF_INET
3 AF_INET不仅可以用作本机的跨进程通信,同样的可以用于不同机器之间的通信,其就是为了在不同机器之间进行网络互联传递数据而生。而AF_UNIX则只能用于本机内进程之间的通信。
// *IPV6
struct sockaddr_in6 {
sa_family_t sin6_family; // AF_INET6
in_port_t sin6_port; // port number
uint32_t sin6_flowinfo; // IPv6 flow information
struct in6_addr sin6_addr; // IPv6 address
uint32_t sin6_scope_id; // Scope ID (new in 2.4)
};
struct in6_addr {
unsigned char s6_addr[16]; // IPv6 address
};
Unix域对应的是:
#define UNIX_PATH_MAX 108
struct sockaddr_un {
sa_family_t sun_family; // AF_UNIX
char sun_path[UNIX_PATH_MAX]; // pathname
};
demo1
server端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
//#include <sys/un.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr; // struct sockaddr_un servaddr;
char buff[4096];
int n;
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) // AF_UNIX
{
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; //AF_UNIX
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //
servaddr.sin_port = htons(6066); // strcpy(servaddr.sun_path, "server_socket");
//unlink("server_socket"); //在unix域会创建一个server_socket文件来操作,unlink是删除server_socket的意思
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
{
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
if( listen(listenfd, 100) == -1)
{
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
while(1)
{
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1)
{
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
printf("begin recv ");
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("server recv msg from ..%d..%d.. client: %s...\n", listenfd, connfd, buff);
if( send(connfd,"11", 2, 0) < 0)
{
printf("send failed\n");
}
close(connfd);
}
close(listenfd);
}
client端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
//#include <sys/un.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n;
char recvline[4096], sendline[4096];
struct sockaddr_in servaddr; // sockaddr_un
if( argc != 3)
{
printf("usage: ./client <ipaddress>\n");
exit(0);
}
while(1)
{
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) // AF_UNIX
{
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
close(sockfd);
exit(0);
}
printf("sockfd = %d\n",sockfd);
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET; // AF_UNIX
servaddr.sin_port = htons(6066); //
/* AF_UNIX 下面要屏蔽*/
if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
{
printf("inet_pton error for %s\n",argv[1]); exit(0);
}
/* AF_UNIX 上面要屏蔽*/
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
{
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
close(sockfd);
}
strcpy(sendline,argv[2]);
printf("send %s\n",argv[2]);
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
exit(0);
}
int n = recv(sockfd, recvline, MAXLINE, 0);
if(n > 0){
printf("recv %s \n",recvline);
}
sleep(1);
close(sockfd);
}
exit(0);
}
在unix域中unlink的功能
如果未加unlink("");可能会报如下错误
bind socket error: Address already in use(errno: 98)
demo2 — (使用epoll解决多客户端连接)
server 端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <sys/epoll.h>
#define MAXLINE 4096
#define EPOLL_MAX_EVENTS 16
int mEpollFd = -1;
static const int EPOLL_SIZE_HINT = 8;
int epoll_init()
{
mEpollFd = epoll_create(EPOLL_SIZE_HINT);
if(mEpollFd < 0)
return -1;
return 0;
}
int epoll_addevent(int event_fd, int itemvalue)
{
int result;
struct epoll_event eventItem;
eventItem.events = EPOLLIN;
eventItem.data.u32 = itemvalue;//mINotifyFd;
result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, event_fd, &eventItem);
printf("epoll_ctl..EPOLL_CTL_ADD.. result = %d\n",result);
return 0;
}
int epoll_rmevent(int event_fd)
{
int result;
result = epoll_ctl(mEpollFd, EPOLL_CTL_DEL, event_fd , NULL);
return result;
}
int main(int argc, char** argv)
{
int listenfd, connfd, readfd;
struct sockaddr_un servaddr; //struct sockaddr_in servaddr;
char buff[4096];
int n ,i;
int timeoutMillis = -1;
int ipollResult;
struct epoll_event mPendingEventItems[16];
if(epoll_init() >= 0){
printf("epill init success !\n");
}else{
exit(0);
}
if( (listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1 ) //if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
{
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sun_family = AF_UNIX; //servaddr.sin_family = AF_INET;
strcpy(servaddr.sun_path, "serversocket_unix"); //servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
unlink("serversocket_unix");
//servaddr.sin_port = htons(6066);
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1)
{
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
if( listen(listenfd, 100) == -1)
{
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}
printf("======waiting for client's request======\n");
epoll_addevent(listenfd, 0x1234);
while(1)
{
printf("epoll wait\n");
ipollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS
, timeoutMillis);
printf("ipollResult = %d\n", ipollResult);
for(i = 0; i < ipollResult; i++){
printf("mPendingEventItems->fd=%d\n",mPendingEventItems[i].data.u32);
printf("reason : 0x%x\n",mPendingEventItems[i].events);
if(mPendingEventItems[i].data.u32 == 0x1234){
printf("...hava new connect...\n");
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1)
{
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
else{
epoll_addevent(connfd, connfd);
printf("add connfd = %d\n",connfd);
}
}else{
//注意如果reason为0x11代表此连接被关闭
readfd = mPendingEventItems[i].data.u32;
if(mPendingEventItems[i].events == 0x11){
epoll_rmevent(readfd);
close(readfd); // 关闭已经断开的客户端
}else{
printf("begin recv connfd=%d, listenfd=%d\n", connfd, listenfd);
n = recv(readfd, buff, MAXLINE, 0);
buff[n] = '\0';
printf("server recv msg from ..%d..%d.. client: %s...\n", listenfd, readfd, buff);
if( send(readfd,"11", 2, 0) < 0)
{
printf("send failed\n");
}
}
}
}
}
close(connfd);
close(listenfd);
}
client 端
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include <sys/un.h>
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n;
int ret, i;
char recvline[4096], sendline[4096];
struct sockaddr_un serv_addr; //struct sockaddr_in servaddr;
if( argc != 2)
{
printf("usage: ./client <message>\n");
exit(0);
}
{
if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) //if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);
close(sockfd);
exit(0);
}
printf("sockfd = %d\n",sockfd);
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX; //servaddr.sin_family = AF_INET;
strcpy(serv_addr.sun_path, "serversocket_unix"); //servaddr.sin_port = htons(6066);
if( connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
{
printf("connect error: %s(errno: %d)\n",strerror(errno),errno);
close(sockfd);
}
strcpy(sendline,argv[1]);
for(i = 0; i < 3; i++)
{
printf("send %s, sockfd=%d\n",argv[1], sockfd);
//下面如果是服务端未继续进行监听此连接(也就是每次重新accept),会直接退出程序
if( (ret = send(sockfd, sendline, strlen(sendline), 0)) < 0 )
{
printf("send failed\n");
printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);
//exit(0);
}
printf("send return = %d\n", ret);
int n = recv(sockfd, recvline, MAXLINE, 0);
if(n > 0){
printf("recv %s \n",recvline);
}
sleep(1);
}
close(sockfd);
}
exit(0);
}