转眼又快到十一月份了,北京已经是完全进入冬天的节奏,外面冷风嗖嗖的,不过在夜深人静之时,学习永远成了我最快乐的时光,只有此时会觉得自己是如此踏实,虽说白天工作也是编一天程,但是此时的编程,是一种业余爱好,而非是生活所迫,非常享受它,好了,让享受继续。

继上次学习了select函数,这次继续对它的用法进行进一步学习,下面就首先先来回顾一下该函数:

上面是函数的原形,下面用理论阐述一下它,它可以看成是一个中心管理器,能够统一管理多个I/O,一旦其中的一个或多个I/O产生了我们所感兴趣的事件,就会被select检测到并返回,再来回顾一下参数,第一个参数是nfds,表示感兴趣的文件描述符的最大值+1,这里多解释一下,为什么是最大值+1呢?实际上select函数是在遍历它所感兴趣的文件描述符是否产生了事件,是从0开始遍历,至nfds的一个[0,nfds)的一个闭开区间,而通常我们写for循环时都会这样写:

for(i=0; i<n; i++){

//TODO

}

正好这也是一个闭开区间,所以第一个参数nfds为最大描述符+1,

第二个参数readfds:可读事件集合

第三个参数writefds:可写事件集合

第四个参数exceptfds:异常事件集合

第五个参数timeout:超时时间

关于这些参数的详细描述,可以参考上篇博文:http://www.cnblogs.com/webor2006/p/4030126.html

一旦检测到了多个事件,就需要一个一个遍历它,一一处理这些I/O事件,通常将用select实现的服务器称之为并发服务器。为啥叫并发服务器呢?因为当我们检测到多个I/O事件之后,实际上是无法并行处理这些I/O事件。实际上select处理事件是按顺序执行的,比如产生了三个事件,则是先执行第一个事件,然后再执行第二个事件,以此类推,所以说它不是并行的,而是并发,为啥是并发,是因为处理这些事件时间也不能太长,也就是说select无法实现并行处理,也就是无法充分利用多核CPU的特点,实际上对于单核的cpu来说,是根据没有并行可言的,而对于多核cpu,select是无法充分利用的,那这时该怎么办呢?可以采用多进程或多线程,关于并发与并行处理,这个之后会研究,这里先大致了解一下概念既可。

上节中用select改进了回射客户端的问题,程序可以同时监测两种事件,一种是标准输入I/O事件,还一种是网络I/O,而不至于因为程序阻塞在标准输入I/O,而同时网络I/O也已经到达了而不能处理,这就是使用select的好处。

上节中只使用了读条件了,这次会对其它事件也进行学习。

以上是可读事件产生的四种情况。

下面,就用select函数来改进回射服务器程序,上节只是改进了回射客户端程序。

先来回顾一下目前的服务器程序,是每连接成功一个客户端,就会创建一个子进程出来进行处理:

这种服务器也叫做并发服务器,通过创建一个进程来达到并发的目的,当有多个客户端连接时,就会有多个进程,那有没有可能用一个进程来实现并发呢?当然是可以做到的,也就是用select,其最终原因是因为它能管理多个I/O,实际上,对于单核CPU来说,select处理并发并不会比多进程效率低,因为多进程在单核的情况下实际上还是按顺序来进行处理的,所以,下面则正式进行修改:

首先将这些代码注释掉,因为是需要改成用select实现的:

编写方法基本跟上节当中的客户端的差不多:

【提示】:记得先记住这个allset,之后随着不断加入代码逻辑,就会自然而然显现它的作用了。

接下来,由于事件成功返回了,那就可以判断标准输入listenfd是否在rset集合里,如果在集合中就证明已经检测到了事件,然后就可以分别进行判断处理了:

另外,由于这一次是单进程的实现方式,当有多个客户端连接时,其conn客户端连接信息是需要用户个数组来保存的,

int main(void)
{
/* signal(SIGCHLD, SIG_IGN);*/
signal(SIGCHLD, handle_sigchld);
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ int on = ;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn; /*
pid_t pid;
while (1)
{
if ((conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen)) < 0)
ERR_EXIT("accept"); printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); pid = fork();
if (pid == -1)
ERR_EXIT("fork");
if (pid == 0)
{
close(listenfd);
echo_srv(conn);
exit(EXIT_SUCCESS);
}
else
close(conn);
}
*/ int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
int i;
for (i=0; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
client[i] = -1; int nready;//检测到的事件个数
int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大 fd_set rset;//声明一个可读的集合
fd_set allset;
//以下两句是将集合清空
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);//将监听套接口放到allset当中
while ()
{
rset = allset;
nready = select(maxfd+, &rset, NULL, NULL, NULL); if (nready == -)
{
if (errno == EINTR)
continue; ERR_EXIT("select");
}
if (nready == )
continue; if (FD_ISSET(listenfd, &rset))
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
if (conn == -)
ERR_EXIT("accept"); for (i=0; i<FD_SETSIZE; i++)//将conn放到数组当中
{
if (client[i] < 0)
{
client[i] = conn;
break;
}
} if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}
  printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));//打印输出对方的IP和端口信息
}
} return ;
}

接下来要做一件事,这时已经得到了conn的套接口,下一次再调用select时我们也需要关心它的可读事件,这时需要做如下处理:

那思考一下为什么要用到allset这个变量?这时因为rset会被select函数所改变,所以对于所有感兴趣的事件需要存放在allset当中,

接下来,则处理已连接套接口事件了,对于这个套接口会有很多个,因为可以连接很多客户端,所以处理如下:

int main(void)
{
/* signal(SIGCHLD, SIG_IGN);*/
signal(SIGCHLD, handle_sigchld);
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ int on = ;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn; int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
int i;
for (i=; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
client[i] = -; int nready;//检测到的事件个数
int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大 fd_set rset;//声明一个可读的集合
fd_set allset;
//以下两句是将集合清空
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);//将监听套接口放到allset当中
while ()
{
rset = allset;
nready = select(maxfd+, &rset, NULL, NULL, NULL); if (nready == -)
{
if (errno == EINTR)
continue; ERR_EXIT("select");
}
if (nready == )
continue; if (FD_ISSET(listenfd, &rset))
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
if (conn == -)
ERR_EXIT("accept"); for (i=; i<FD_SETSIZE; i++)//将conn放到数组当中
{
if (client[i] < )
{
client[i] = conn;
break;
}
} if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); FD_SET(conn, &allset); if (--nready <= )
continue;
} for (i=0; i<=FD_SETSIZE; i++)
{
conn = client[i];
if (conn == -1)
continue; if (FD_ISSET(conn, &rset))
{
char recvbuf[1024] = {0};
int ret = readline(conn, recvbuf, 1024);
if (ret == -1)
ERR_EXIT("readline");
if (ret == 0)
{//对方关闭
printf("client close\n");
FD_CLR(conn, &allset);//从集合中将此已连接接口清除
client[i] = -1;//并且还原默认标识
} fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf)); if (--nready <= 0)
break
; }
}

} return ;
}

另外,还需要关心一下最大描述符maxfd,当产生了新的连接套接口时,是需要将其进行更新的,于是修改代码如下:

好了,下面来编译运行一下:

可见,用单进程的方式也实现了多个客户端并发的处理。

另外,此处程序还有可优化的地方,就是处理已连接事件的时候,总是遍历FD_SETSIZE,可以再加一个变量,用来记录最大的不空闲的i值,修改代码如下:

int main(void)
{
/* signal(SIGCHLD, SIG_IGN);*/
signal(SIGCHLD, handle_sigchld);
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ int on = ;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn; int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
int maxi = 0;//用来记录最大的可连接套接口存放的位置
int i;
for (i=; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
client[i] = -; int nready;//检测到的事件个数
int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大 fd_set rset;//声明一个可读的集合
fd_set allset;
//以下两句是将集合清空
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);//将监听套接口放到allset当中
while ()
{
rset = allset;
nready = select(maxfd+, &rset, NULL, NULL, NULL); if (nready == -)
{
if (errno == EINTR)
continue; ERR_EXIT("select");
}
if (nready == )
continue; if (FD_ISSET(listenfd, &rset))
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
if (conn == -)
ERR_EXIT("accept"); for (i=; i<FD_SETSIZE; i++)//将conn放到数组当中
{
if (client[i] < )
{
client[i] = conn;
if (i > maxi)
maxi = i;
break;
}
} if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); FD_SET(conn, &allset);
if (conn > maxfd)
maxfd = conn; if (--nready <= )
continue;
} for (i=; i<=maxi; i++)//这时就会减少循环的次数
{
conn = client[i];
if (conn == -)
continue; if (FD_ISSET(conn, &rset))
{
char recvbuf[] = {};
int ret = readline(conn, recvbuf, );
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{//对方关闭
printf("client close\n");
FD_CLR(conn, &allset);//从集合中将此已连接接口清除
client[i] = -;//并且还原默认标识
} fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf)); if (--nready <= )
break; }
}
} return ;
}

最后再来看一下整个服务端的代码如下:

echosrv.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > )
{
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nread == )
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while ()
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if (ret == - && errno == EINTR)
continue;
return ret;
}
} ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;
while ()
{
ret = recv_peek(sockfd, bufp, nleft);
if (ret < )
return ret;
else if (ret == )
return ret; nread = ret;
int i;
for (i=; i<nread; i++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+);
if (ret != i+)
exit(EXIT_FAILURE); return ret;
}
} if (nread > nleft)
exit(EXIT_FAILURE); nleft -= nread;
ret = readn(sockfd, bufp, nread);
if (ret != nread)
exit(EXIT_FAILURE); bufp += nread;
} return -;
} void echo_srv(int conn)
{
char recvbuf[];
while ()
{
memset(recvbuf, , sizeof(recvbuf));
int ret = readline(conn, recvbuf, );
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{
printf("client close\n");
break;
} fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf));
}
} void handle_sigchld(int sig)
{
/* wait(NULL);*/
while (waitpid(-, NULL, WNOHANG) > )
;
} int main(void)
{
/* signal(SIGCHLD, SIG_IGN);*/
signal(SIGCHLD, handle_sigchld);
int listenfd;
if ((listenfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
/* if ((listenfd = socket(PF_INET, SOCK_STREAM, 0)) < 0)*/
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");*/
/*inet_aton("127.0.0.1", &servaddr.sin_addr);*/ int on = ;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < )
ERR_EXIT("setsockopt"); if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("bind");
if (listen(listenfd, SOMAXCONN) < )
ERR_EXIT("listen"); struct sockaddr_in peeraddr;
socklen_t peerlen = sizeof(peeraddr);
int conn; int client[FD_SETSIZE];//定义一个数组用来保存conn,其中FD_SETSIZE为最大文件描述符个数,不能超过它
int maxi = ;//用来记录最大的可连接套接口存放的位置
int i;
for (i=; i<FD_SETSIZE; i++)//对里面的数组都初使化为-1
client[i] = -; int nready;//检测到的事件个数
int maxfd = listenfd;//获取最大的文件描述符,目前listenfd最大 fd_set rset;//声明一个可读的集合
fd_set allset;
//以下两句是将集合清空
FD_ZERO(&rset);
FD_ZERO(&allset);
FD_SET(listenfd, &allset);//将监听套接口放到allset当中
while ()
{
rset = allset;
nready = select(maxfd+, &rset, NULL, NULL, NULL); if (nready == -)
{
if (errno == EINTR)
continue; ERR_EXIT("select");
}
if (nready == )
continue; if (FD_ISSET(listenfd, &rset))
{
peerlen = sizeof(peeraddr);
conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
if (conn == -)
ERR_EXIT("accept"); for (i=; i<FD_SETSIZE; i++)//将conn放到数组当中
{
if (client[i] < )
{
client[i] = conn;
if (i > maxi)
maxi = i;
break;
}
} if (i == FD_SETSIZE)//没有找到空闲的位置,也就是连接数已经达到了上线,则给出提示
{
fprintf(stderr, "too many clients\n");
exit(EXIT_FAILURE);
}
printf("ip=%s port=%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port)); FD_SET(conn, &allset);
if (conn > maxfd)
maxfd = conn; if (--nready <= )
continue;
} for (i=; i<=maxi; i++)
{
conn = client[i];
if (conn == -)
continue; if (FD_ISSET(conn, &rset))
{
char recvbuf[] = {};
int ret = readline(conn, recvbuf, );
if (ret == -)
ERR_EXIT("readline");
if (ret == )
{//对方关闭
printf("client close\n");
FD_CLR(conn, &allset);//从集合中将此已连接接口清除
client[i] = -;//并且还原默认标识
} fputs(recvbuf, stdout);
writen(conn, recvbuf, strlen(recvbuf)); if (--nready <= )
break; }
}
} return ;
}

echocli.c:

#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h> #include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h> #define ERR_EXIT(m) \
do \
{ \
perror(m); \
exit(EXIT_FAILURE); \
} while() ssize_t readn(int fd, void *buf, size_t count)
{
size_t nleft = count;
ssize_t nread;
char *bufp = (char*)buf; while (nleft > )
{
if ((nread = read(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nread == )
return count - nleft; bufp += nread;
nleft -= nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
size_t nleft = count;
ssize_t nwritten;
char *bufp = (char*)buf; while (nleft > )
{
if ((nwritten = write(fd, bufp, nleft)) < )
{
if (errno == EINTR)
continue;
return -;
}
else if (nwritten == )
continue; bufp += nwritten;
nleft -= nwritten;
} return count;
} ssize_t recv_peek(int sockfd, void *buf, size_t len)
{
while ()
{
int ret = recv(sockfd, buf, len, MSG_PEEK);
if (ret == - && errno == EINTR)
continue;
return ret;
}
} ssize_t readline(int sockfd, void *buf, size_t maxline)
{
int ret;
int nread;
char *bufp = buf;
int nleft = maxline;
while ()
{
ret = recv_peek(sockfd, bufp, nleft);
if (ret < )
return ret;
else if (ret == )
return ret; nread = ret;
int i;
for (i=; i<nread; i++)
{
if (bufp[i] == '\n')
{
ret = readn(sockfd, bufp, i+);
if (ret != i+)
exit(EXIT_FAILURE); return ret;
}
} if (nread > nleft)
exit(EXIT_FAILURE); nleft -= nread;
ret = readn(sockfd, bufp, nread);
if (ret != nread)
exit(EXIT_FAILURE); bufp += nread;
} return -;
} void echo_cli(int sock)
{
/*
char sendbuf[1024] = {0};
char recvbuf[1024] = {0};
while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
{
writen(sock, sendbuf, strlen(sendbuf)); int ret = readline(sock, recvbuf, sizeof(recvbuf));
if (ret == -1)
ERR_EXIT("readline");
else if (ret == 0)
{
printf("client close\n");
break;
} fputs(recvbuf, stdout);
memset(sendbuf, 0, sizeof(sendbuf));
memset(recvbuf, 0, sizeof(recvbuf));
} close(sock);
*/ fd_set rset;
FD_ZERO(&rset); int nready;
int maxfd;
int fd_stdin = fileno(stdin);
if (fd_stdin > sock)
maxfd = fd_stdin;
else
maxfd = sock; char sendbuf[] = {};
char recvbuf[] = {}; while ()
{
FD_SET(fd_stdin, &rset);
FD_SET(sock, &rset);
nready = select(maxfd+, &rset, NULL, NULL, NULL);
if (nready == -)
ERR_EXIT("select"); if (nready == )
continue; if (FD_ISSET(sock, &rset))
{
int ret = readline(sock, recvbuf, sizeof(recvbuf));
if (ret == -)
ERR_EXIT("readline");
else if (ret == )
{
printf("server close\n");
break;
} fputs(recvbuf, stdout);
memset(recvbuf, , sizeof(recvbuf));
}
if (FD_ISSET(fd_stdin, &rset))
{
if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL)
break;
writen(sock, sendbuf, strlen(sendbuf));
memset(sendbuf, , sizeof(sendbuf));
}
} close(sock);
} void handle_sigpipe(int sig)
{
printf("recv a sig=%d\n", sig);
} int main(void)
{
/*
signal(SIGPIPE, handle_sigpipe);
*/
signal(SIGPIPE, SIG_IGN);
int sock;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < )
ERR_EXIT("socket"); struct sockaddr_in servaddr;
memset(&servaddr, , sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons();
servaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < )
ERR_EXIT("connect"); struct sockaddr_in localaddr;
socklen_t addrlen = sizeof(localaddr);
if (getsockname(sock, (struct sockaddr*)&localaddr, &addrlen) < )
ERR_EXIT("getsockname"); printf("ip=%s port=%d\n", inet_ntoa(localaddr.sin_addr), ntohs(localaddr.sin_port)); echo_cli(sock); return ;
}

好了,今天就先学到这,下次继续。

linux网络编程之socket编程(九)的更多相关文章

  1. linux网络编程之socket编程(四)

    经过两周的等待,终于可以回归我正常的学习之旅了,表哥来北京了在我这暂住,晚上回家了基本在和他聊天,周末带他在北京城到处乱转,几乎剥夺了我自由学习的时间了,不过,亲人之情还是很难得的,工作学习并不是生活 ...

  2. linux网络编程之socket编程(一)

    今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...

  3. linux网络编程之socket编程(六)

    经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:“真爱生活,珍惜生命”,好了,言归正传. 回顾一下我们之间实现 ...

  4. linux网络编程之socket编程(八)

    学习socket编程继续,今天要学习的内容如下: 先来简单介绍一下这五种模型分别是哪些,偏理论,有个大致的印象就成,做个对比,因为最终只会研究一个I/O模型,也是经常会用到的, 阻塞I/O: 先用一个 ...

  5. linux网络编程之socket编程(十六)

    继续学习socket编程,今天的内容会有些难以理解,一步步来分解,也就不难了,正入正题: 实际上sockpair有点像之前linux系统编程中学习的pipe匿名管道,匿名管道它是半双工的,只能用于亲缘 ...

  6. linux网络编程之socket编程(十五)

    今天继续学习socket编程,这次主要是学习UNIX域协议相关的知识,下面开始: [有个大概的认识,它是来干嘛的] ①.UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍. ...

  7. linux网络编程之socket编程(三)

    今天继续对socket编程进行学习,在学习之前,需要回顾一下上一篇中编写的回射客户/服务器程序(http://www.cnblogs.com/webor2006/p/3923254.html),因为今 ...

  8. linux网络编程之socket编程(二)

    今天继续对socket编程进行研究,这里会真正开如用socket写一个小例子,进入正题: TCP客户/服务器模型:   关于这个模型的流程这里就不多说了,比较容易理解,下面则利用这种模型来编写一个实际 ...

  9. linux网络编程之socket编程(十二)

    今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...

随机推荐

  1. 01点睛Spring MVC 4.1-搭建环境

    转发:https://www.iteye.com/blog/wiselyman-2213906 1.1 简单示例 通篇使用java config @Controller声明bean是一个控制器 @Re ...

  2. addEventListener兼容性问题

    参考链接:https://blog.csdn.net/lililiaaa/article/details/83960924

  3. git下载仓库的部分目录

    有这样的需求,比如某个仓库里包含可执行文件[编译后的文件]或jar包之类的,他们太大我不需要而且我自己可以编译或导入: 或者是某个仓库是自己专门用来放demos的,里面有很多的demo项目,我可能只想 ...

  4. LeetCode 172. 阶乘后的零(Factorial Trailing Zeroes)

    172. 阶乘后的零 172. Factorial Trailing Zeroes 题目描述 给定一个整数 n,返回 n! 结果尾数中零的数量. LeetCode172. Factorial Trai ...

  5. LeetCode 278. 第一个错误的版本(First Bad Version)

    278. 第一个错误的版本 LeetCode278. First Bad Version 题目描述 你是产品经理,目前正在带领一个团队开发新的产品.不幸的是,你的产品的最新版本没有通过质量检测.由于每 ...

  6. Python-04-数据结构

    一.数字 整数 Python可以处理任意大小的整数,当然包括负整数,在程序中的表示方法和数学上的写法一模一样,例如:1,100,-8080,0,等等. 计算机由于使用二进制,所以,有时候用十六进制表示 ...

  7. Python Http-server 使用

    Python内置的下载服务器 http.server  Python的Web服务器 python2 中SimpleHTTPServer python3 中 http.server 执行 python ...

  8. 利用strace & Perf分析MySQL

    strace介绍及用途 strace是一个用于诊断,分析linux用户态进程的工具 类似的工具pstrace,lsof,gdb,pstrack strace观察mysqld对my.cnf 配置文件的加 ...

  9. Linux Wireless Supported Devices

    Linux Wireless Supported Devices https://ark.intel.com/content/www/us/en/ark/products/series/59484/i ...

  10. Ext之页面多次请求问题(下拉框发送无关请求)

    extjs 下拉框在拉取本地数据,然后又要展示后台数据时,出现过此问题(加载页面,自动发送无关的请求导致后台出现错误日志) { xtype:'combo', id:'state', width:130 ...