#include
#include
#include
#include
#include
#include
#include
#include
#include "errno.h"
#include "signal.h"
#include
//#define NULL 0
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define PIDNUMB 2
static void tcp_handle_request(int sc)
{
printf("[pid=0x%x]tcp_handle_request start sc=%d\n", getpid(), sc);
int n=0;
char buff[BUFFLEN];
time_t now;
int count = 0;
while(1)
{
memset(buff,0,BUFFLEN);
n=recv(sc,buff,BUFFLEN,0);
if(n>0 && !strncmp(buff,"TIME",4))
{
printf("[pid=0x%x]recv data sc=%d data=[%s], count=%d\n",getpid(), sc ,buff,++count);
memset(buff,0,BUFFLEN);
now=time(NULL);
sprintf(buff,"[%24s]\r\n",ctime(&now));
send(sc,buff,strlen(buff),0);
}
else if(n<=0)
{
printf("[pid=0x%x]recv data res=%d errno=%d,msg=%s,close & exit\n",getpid(), n, errno,strerror(errno));
break;
}
}
close(sc);
printf("[pid=0x%x]tcp_handle_request end sc=%d\n", getpid(), sc);
}
static int tcp_handle_connect(int ss)
{
printf("[pid=0x%x]tcp_handle_request start ss=%d\n", getpid(), ss);
int sc;
struct sockaddr_in from;
socklen_t len = sizeof(from);
while(1)
{
sc=accept(ss,(struct sockaddr*)&from,&len);
if(sc>0)
{
static char tempStr[32] = {0};
inet_ntop(AF_INET,&(from.sin_addr),tempStr,sizeof(tempStr));
printf("[pid=0x%x]handle_connect ss=%d sc=%d connect from %s:%u\n", getpid(),ss,sc,tempStr,ntohs(from.sin_port));
if(fork()>0){
int res = close(sc);
if(res==-1)
{
printf("[pid=0x%x]close sc res=%d close,errno=%d,msg=%s\n",getpid(), res,sc, errno,strerror(errno));
}
}
else{
tcp_handle_request(sc);
}
}
else if(sc==-1)
{
printf("accept ss=%d errno=%d,msg=%s\n",ss,errno,strerror(errno));
exit(1);
}
}
}
int tcp_main1(void)
{
int ss;
struct sockaddr_in local;
ss=socket(AF_INET,SOCK_STREAM,0);
printf("ss=%d\n",ss);
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_port = htons(SERVER_PORT);
int res = bind(ss,(struct sockaddr*)&local,sizeof(local));
if(res==-1)
{
printf("bind res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
exit(1);
}
printf("bind res=%d\n",res);
res = listen(ss,BACKLOG);
if(res==-1)
{
printf("listen res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
exit(1);
}
printf("listen res=%d\n" ,res);
tcp_handle_connect(ss);
return 0;
}
int main(void)
{
tcp_main1();
return 0;
}
通过TCP&UDP测试工具发送
ss=3
bind res=0
listen res=0
[pid=0x2203]tcp_handle_request start ss=3
[pid=0x2203]handle_connect ss=3 sc=4 connect from 192.168.10.2:4001
[pid=0x221a]tcp_handle_request start sc=4
[pid=0x221a]recv data sc=4 data=[TIMEA], count=1
[pid=0x221a]recv data sc=4 data=[TIMEA], count=2
[pid=0x221a]recv data sc=4 data=[TIMEA], count=3
[pid=0x221a]recv data sc=4 data=[TIMEA], count=4
[pid=0x221a]recv data sc=4 data=[TIMEA], count=5
[pid=0x2203]handle_connect ss=3 sc=4 connect from 192.168.10.2:4002
[pid=0x222d]tcp_handle_request start sc=4
[pid=0x222d]recv data sc=4 data=[TIMEB], count=1
[pid=0x222d]recv data sc=4 data=[TIMEB], count=2
[pid=0x222d]recv data sc=4 data=[TIMEB], count=3
[pid=0x222d]recv data sc=4 data=[TIMEB], count=4
[pid=0x222d]recv data sc=4 data=[TIMEB], count=5
[pid=0x221a]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x221a]tcp_handle_request end sc=4
[pid=0x222d]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x222d]tcp_handle_request end sc=4
1、当客户端主动挥手断开连接,发送FIN信号过来时,服务端(本测试中服务端运行在ubuntu18.04)recv调用会返回0,TCP状态会处于CLOSE_WAIT.
$ netstat -tap | grep 8888
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 7436/test0001
tcp 0 0 armlinux:8888 192.168.10.2:4001 CLOSE_WAIT 7488/test0001
此时需要应用层主动关闭,发送FIN给客户端,切换到LAST_ACK,当接收到客户端应答的ACK后完成最终关闭;
$ netstat -tap | grep 8888
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 7873/test0001
2、客户端(本测试中客户端运行在windows)在主动断开连接时,接收到服务器的FIN和ACK后,进一步发送ACK,并切换到TIME_WAIT状态,一般需要等待2MSL超时才能完成最终关闭(通过调整相关系统参数,可以让系统快速关闭,具体自行搜索资料);
netstat -anto | grep 4001
TCP 192.168.10.2:4001 192.168.10.40:8888 TIME_WAIT 0 InHost
#include
#include
#include
#include
#include
#include
#include
#include
#include "errno.h"
#include "signal.h"
#include
#include
//#define NULL 0
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define PIDNUMB 2
static void *tcp_handle_request(void * argv)
{
int sc = *((int*)argv);
printf("[pid=0x%x threadid=0x%lx]tcp_handle_request start sc=%d\n", getpid(),pthread_self(), sc);
int n=0;
char buff[BUFFLEN];
time_t now;
int count = 0;
while(1)
{
memset(buff,0,BUFFLEN);
n=recv(sc,buff,BUFFLEN,0);
if(n>0 && !strncmp(buff,"TIME",4))
{
printf("[pid=0x%x threadid=0x%lx]recv data sc=%d data=[%s], count=%d\n",getpid(),pthread_self(), sc ,buff,++count);
memset(buff,0,BUFFLEN);
now=time(NULL);
sprintf(buff,"[%24s]\r\n",ctime(&now));
send(sc,buff,strlen(buff),0);
}
else if(n<=0)
{
printf("[pid=0x%x threadid=0x%lx]recv data res=%d errno=%d,msg=%s,close & exit\n",getpid(),pthread_self(), n, errno,strerror(errno));
break;
}
}
close(sc);
printf("[pid=0x%x threadid=0x%lx]tcp_handle_request end sc=%d\n", getpid(),pthread_self(), sc);
}
static int tcp_handle_connect(int ss)
{
printf("[pid=0x%x threadid=0x%lx]tcp_handle_request start ss=%d\n", getpid(),pthread_self(), ss);
int sc;
struct sockaddr_in from;
socklen_t len = sizeof(from);
pthread_t thread_do;
while(1)
{
sc=accept(ss,(struct sockaddr*)&from,&len);
if(sc>0)
{
static char tempStr[32] = {0};
inet_ntop(AF_INET,&(from.sin_addr),tempStr,sizeof(tempStr));
printf("[pid=0x%x]handle_connect ss=%d sc=%d connect from %s:%u\n", getpid(),ss,sc,tempStr,ntohs(from.sin_port));
int res = pthread_create(&thread_do,NULL,tcp_handle_request,&sc);
if(res == -1){
printf("[pid=0x%x]pthread_create res=%d ,errno=%d,msg=%s\n",getpid(), res , errno,strerror(errno));
}
}
else if(sc==-1)
{
printf("accept ss=%d errno=%d,msg=%s\n",ss,errno,strerror(errno));
exit(1);
}
}
}
int tcp_main1(void)
{
int ss;
struct sockaddr_in local;
ss=socket(AF_INET,SOCK_STREAM,0);
printf("ss=%d\n",ss);
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_port = htons(SERVER_PORT);
int res = bind(ss,(struct sockaddr*)&local,sizeof(local));
if(res==-1)
{
printf("bind res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
exit(1);
}
printf("bind res=%d\n",res);
res = listen(ss,BACKLOG);
if(res==-1)
{
printf("listen res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
exit(1);
}
printf("listen res=%d\n" ,res);
tcp_handle_connect(ss);
return 0;
}
int main(void)
{
tcp_main1();
return 0;
}
客户端连接后,各发5次,再关闭连接
ss=3
bind res=0
listen res=0
[pid=0x2796 threadid=0x7f08734ce740]tcp_handle_request start ss=3
[pid=0x2796]handle_connect ss=3 sc=4 connect from 192.168.10.2:4001
[pid=0x2796 threadid=0x7f0872cb2700]tcp_handle_request start sc=4
[pid=0x2796]handle_connect ss=3 sc=5 connect from 192.168.10.2:4002
[pid=0x2796 threadid=0x7f08724b1700]tcp_handle_request start sc=5
[pid=0x2796 threadid=0x7f0872cb2700]recv data sc=4 data=[TIMEA], count=1
[pid=0x2796 threadid=0x7f0872cb2700]recv data sc=4 data=[TIMEA], count=2
[pid=0x2796 threadid=0x7f0872cb2700]recv data sc=4 data=[TIMEA], count=3
[pid=0x2796 threadid=0x7f0872cb2700]recv data sc=4 data=[TIMEA], count=4
[pid=0x2796 threadid=0x7f0872cb2700]recv data sc=4 data=[TIMEA], count=5
[pid=0x2796 threadid=0x7f08724b1700]recv data sc=5 data=[TIMEB], count=1
[pid=0x2796 threadid=0x7f08724b1700]recv data sc=5 data=[TIMEB], count=2
[pid=0x2796 threadid=0x7f08724b1700]recv data sc=5 data=[TIMEB], count=3
[pid=0x2796 threadid=0x7f08724b1700]recv data sc=5 data=[TIMEB], count=4
[pid=0x2796 threadid=0x7f08724b1700]recv data sc=5 data=[TIMEB], count=5
[pid=0x2796 threadid=0x7f0872cb2700]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x2796 threadid=0x7f0872cb2700]tcp_handle_request end sc=4
[pid=0x2796 threadid=0x7f08724b1700]recv data res=0 errno=0,msg=Success,close & exit
[pid=0x2796 threadid=0x7f08724b1700]tcp_handle_request end sc=5
与前面的方案,当客户端连接变多时,会新创建连接相同个数的进程或者线程,当此数值比较大时,如上千个连接,此时线程/进程资料存储占用,以及CPU在上千个进程/线程之间的时间片调度成本凸显,造成性能下降。需要一种新的模型来解决,select IO模型即是一种方案。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "errno.h"
#include "signal.h"
#include
#include
#include
#include
#ifndef NULL
#define NULL 0
#endif
#define MAX_MSG_LEN 1024
#define BUFFLEN 1024
#define SERVER_PORT 8888
#define BACKLOG 5
#define PIDNUMB 2
#define CLIENTNUM 5 //最大支持客户端数量
int connect_host[CLIENTNUM];
int connect_number=0;
void printMsg(const char* pcFmt, ...);
static void * handle_requst(void * argv)
{
printMsg("[pid=0x%x threadid=0x%lx]handle_requst start \n", getpid(),pthread_self() );
int n=0;
char buff[BUFFLEN]={0};
time_t now;
int maxfd=-1;//最大侦听文件描述符
fd_set scanfd;//侦听描述符集合
int i=0;
int res = -1;
struct timeval timeout;//超时时间
for (;;)
{
maxfd = -1;
FD_ZERO(&scanfd);
//将已经建立的连接描述符添加到侦听描述符集合中,并获取最大的描述符
int totalfdset=0;
for(i=0;i<CLIENTNUM;i++)
{
if(connect_host[i]!=-1)
{
//printMsg("scanfd set fds:%d\n",connect_host[i]);
FD_SET(connect_host[i],&scanfd);
if(maxfd<connect_host[i])
{
maxfd=connect_host[i];
}
totalfdset++;
}
}
if(totalfdset<=0)
{
sleep(1);
continue;
}
//printMsg("totalfdset=%d,maxfd=%d\n",totalfdset,maxfd);
//timeout数值会被select改写,每次循环调用需要重新赋值,否则变成立即返回效果
timeout.tv_sec = 1;
timeout.tv_usec = 0;
res = select(maxfd+1,&scanfd,NULL,NULL,&timeout);
switch (res) {
case 0:
//printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d timeout \n", getpid(),pthread_self() ,res);
break;
case -1:
printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d err,errno=%d,msg=%s\n", getpid(),pthread_self() ,res,errno,strerror(errno));
break;
default:
printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d read event \n", getpid(),pthread_self() ,res );
if(connect_number<=0)
{
printMsg("[pid=0x%x threadid=0x%lx]handle_requst select res=%d ,connect_number<=0 ,break; \n", getpid(),pthread_self() ,res );
break;
}
//查找激活的文件描述符
for(i=0;i<CLIENTNUM;i++)
{
if(connect_host[i]!=-1)
{
//是匹配的有效的文件描述符
if(FD_ISSET(connect_host[i],&scanfd))
{
memset(buff,0,BUFFLEN);
n = recv(connect_host[i],buff,BUFFLEN,0);
if(n>0 && !strncmp(buff,"TIME",4))
{
printMsg("[pid=0x%x threadid=0x%lx]recv data sc=%d data=[%s]\n",getpid(),pthread_self(), connect_host[i] ,buff);
memset(buff,0,BUFFLEN);
now=time(NULL);
sprintf(buff,"[%24s]\r\n",ctime(&now));
send(connect_host[i],buff,strlen(buff),0);
}
else if(n<=0)
{
printMsg("[pid=0x%x threadid=0x%lx]recv data res=%d errno=%d,msg=%s,close\n",getpid(),pthread_self(), n, errno,strerror(errno));
close(connect_host[i]);
connect_host[i] = -1;
connect_number--;
break;
}
}
}
}
break;
}
}
return 0;
}
static void *handle_connect(void *argv)
{
int ss=*((int*)argv);
printMsg("[pid=0x%x threadid=0x%lx]handle_connect start ss=%d\n", getpid(),pthread_self(), ss);
struct sockaddr_in from;
socklen_t len = sizeof(from);
while(1)
{
//接收连接
int sc=accept(ss,(struct sockaddr*)&from,&len);
if(sc>0)
{
static char tempStr[32] = {0};
inet_ntop(AF_INET,&(from.sin_addr),tempStr,sizeof(tempStr));
printMsg("[pid=0x%x threadid=0x%lx]handle_connect ss=%d sc=%d connect from %s:%u\n", getpid(),pthread_self(),ss,sc,tempStr,ntohs(from.sin_port));
int i =0;
for(i=0; i < CLIENTNUM;i++)
{
if(connect_host[i]==-1)
{
//客户端连接未已经达到上线,则记录连接
connect_host[i]=sc;
connect_number++;
printMsg("[pid=0x%x threadid=0x%lx]client queue is set ,count=%d,sc=%d\n",getpid(),pthread_self(),connect_number,sc);
break;
}
}
if(i>=CLIENTNUM)
{
//客户端连接已经达到上线,则主动关闭连接
printMsg("[pid=0x%x threadid=0x%lx]client queue is full ,count=%d,cannot add ,close sc=%d\n",getpid(),pthread_self(),connect_number,sc);
close(sc);
}
}
else if(sc==-1)
{
printMsg("accept error sc=%d errno=%d,msg=%s\n",sc,errno,strerror(errno));
exit(1);
}
}
return 0;
}
int main(void)
{
int ss;
struct sockaddr_in local;
ss=socket(AF_INET,SOCK_STREAM,0);
printMsg("ss=%d\n",ss);
memset(&local,0,sizeof(local));
local.sin_family = AF_INET;
local.sin_addr.s_addr=htonl(INADDR_ANY);
local.sin_port = htons(SERVER_PORT);
//设置端口复用
int opt = 1;
errno = 0;
int res = setsockopt(ss,SOL_SOCKET,SO_REUSEADDR,(const void *)&opt,sizeof(opt));
if(res == -1)
{
printMsg("set SO_REUSEADDR error, errno=%d,msg=%s\n", errno,strerror(errno));
}
res = bind(ss,(struct sockaddr*)&local,sizeof(local));
if(res==-1)
{
printMsg("bind res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
exit(1);
}
printMsg("bind res=%d\n",res);
res = listen(ss,BACKLOG);
if(res==-1)
{
printMsg("listen res=%d,errno=%d,msg=%s\n",res,errno,strerror(errno));
exit(1);
}
printMsg("listen res=%d\n" ,res);
//原书是memset(connect_host,-1,CLIENTNUM);,不能初始化完全,此函数是按字节进行设置的
memset(connect_host,-1,CLIENTNUM*sizeof (connect_host[0]));
pthread_t thread_do_connect;
pthread_t thread_do_request;
res = pthread_create(&thread_do_connect,NULL,handle_connect,(void*)&ss);
printMsg("pthread_create thread_do_connect res=%d\n",res);
res = pthread_create(&thread_do_request,NULL,handle_requst,NULL);
printMsg("pthread_create thread_do_request res=%d\n",res);
res = pthread_join(thread_do_connect,NULL);
printMsg("pthread_join thread_do_connect res=%d\n",res);
res = pthread_join(thread_do_request,NULL);
printMsg("pthread_join thread_do_request res=%d\n",res);
res = close(ss);
printMsg("close ss res=%d\n",res);
return 0;
}
void printMsg(const char* pcFmt, ...)
{
va_list vList;
char acBuf[MAX_MSG_LEN];
va_start(vList, pcFmt);
va_end(vList);
vsnprintf(acBuf, MAX_MSG_LEN, pcFmt, vList);
struct tm *tm_now;
struct timeval nowtime;
gettimeofday(&nowtime,0);
tm_now = localtime(&nowtime.tv_sec);
printf("[%04d%02d%02d% 02d:%02d:%02d.%03d]%s", tm_now->tm_year+1900, tm_now->tm_mon+1, tm_now->tm_mday,
tm_now->tm_hour, tm_now->tm_min, tm_now->tm_sec,nowtime.tv_usec/1000,
acBuf);
}
略
[20190813 22:01:56.191]ss=3
[20190813 22:01:56.191]bind res=0
[20190813 22:01:56.191]listen res=0
[20190813 22:01:56.191]pthread_create thread_do_connect res=0
[20190813 22:01:56.192]pthread_create thread_do_request res=0
[20190813 22:01:56.192][pid=0x53e6 threadid=0x7fb7cfcee700]handle_connect start ss=3
[20190813 22:01:56.192][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst start
[20190813 22:02:00.856][pid=0x53e6 threadid=0x7fb7cfcee700]handle_connect ss=3 sc=4 connect from 192.168.10.2:4001
[20190813 22:02:00.856][pid=0x53e6 threadid=0x7fb7cfcee700]client queue is set ,count=1,sc=4
[20190813 22:02:05.579][pid=0x53e6 threadid=0x7fb7cfcee700]handle_connect ss=3 sc=5 connect from 192.168.10.2:4002
[20190813 22:02:05.579][pid=0x53e6 threadid=0x7fb7cfcee700]client queue is set ,count=2,sc=5
[20190813 22:02:10.819][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:10.819][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data sc=4 data=[TIMEA]
[20190813 22:02:12.172][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:12.172][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data sc=4 data=[TIMEA]
[20190813 22:02:15.023][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:15.023][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data sc=4 data=[TIMEA]
[20190813 22:02:25.703][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:25.703][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data sc=5 data=[TIMEB]
[20190813 22:02:26.552][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:26.552][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data sc=5 data=[TIMEB]
[20190813 22:02:27.214][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:27.214][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data sc=5 data=[TIMEB]
[20190813 22:02:37.233][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:02:37.233][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data res=0 errno=0,msg=Success,close
[20190813 22:03:23.523][pid=0x53e6 threadid=0x7fb7cf4ed700]handle_requst select res=1 read event
[20190813 22:03:23.523][pid=0x53e6 threadid=0x7fb7cf4ed700]recv data res=0 errno=0,msg=Success,close
1、connect_host和connect_number存在多线程读写问题,正常需要进行加锁同步,这里只是简单测试,不进行处理;
2、注意点,这里初始化连接描述符不能使用
int connect_host[CLIENTNUM]={-1};
3、当select在调用过程中被被改写超时参数,因此在每次select之前需要进行重新赋值,否则会变成立即返回状态。
timeout.tv_sec = 1;
timeout.tv_usec = 0;
res = select(maxfd+1,&scanfd,NULL,NULL,&timeout);
~$ netstat -h
usage: netstat [-vWeenNcCF] [<Af>] -r netstat {-V|--version|-h|--help}
netstat [-vWnNcaeol] [<Socket> ...]
netstat { [-vWeenNac] -i | [-cnNe] -M | -s [-6tuw] }
-r, --route display routing table
-i, --interfaces display interface table
-g, --groups display multicast group memberships
-s, --statistics display networking statistics (like SNMP)
-M, --masquerade display masqueraded connections
-v, --verbose be verbose
-W, --wide don't truncate IP addresses
-n, --numeric don't resolve names
--numeric-hosts don't resolve host names
--numeric-ports don't resolve port names
--numeric-users don't resolve user names
-N, --symbolic resolve hardware names
-e, --extend display other/more information
-p, --programs display PID/Program name for sockets
-o, --timers display timers
-c, --continuous continuous listing
-l, --listening display listening server sockets
-a, --all display all sockets (default: connected)
-F, --fib display Forwarding Information Base (default)
-C, --cache display routing cache instead of FIB
-Z, --context display SELinux security context for sockets
<Socket>={-t|--tcp} {-u|--udp} {-U|--udplite} {-S|--sctp} {-w|--raw}
{-x|--unix} --ax25 --ipx --netrom
<AF>=Use '-6|-4' or '-A ' or '--' ; default: inet
List of possible address families (which support routing):
inet (DARPA Internet) inet6 (IPv6) ax25 (AMPR AX.25)
netrom (AMPR NET/ROM) ipx (Novell IPX) ddp (Appletalk DDP)
x25 (CCITT X.25)
>netstat -h
显示协议统计信息和当前 TCP/IP 网络连接。
NETSTAT [-a] [-b] [-e] [-f] [-n] [-o] [-p proto] [-r] [-s] [-x] [-t] [interval]
-a 显示所有连接和侦听端口。
-b 显示在创建每个连接或侦听端口时涉及的
可执行程序。在某些情况下,已知可执行程序承载
多个独立的组件,这些情况下,
显示创建连接或侦听端口时
涉及的组件序列。在此情况下,可执行程序的
名称位于底部 [] 中,它调用的组件位于顶部,
直至达到 TCP/IP。注意,此选项
可能很耗时,并且在你没有足够
权限时可能失败。
-e 显示以太网统计信息。此选项可以与 -s 选项
结合使用。
-f 显示外部地址的完全限定
域名(FQDN)。
-n 以数字形式显示地址和端口号。
-o 显示拥有的与每个连接关联的进程 ID。
-p proto 显示 proto 指定的协议的连接;proto
可以是下列任何一个: TCP、UDP、TCPv6 或 UDPv6。如果与 -s
选项一起用来显示每个协议的统计信息,proto 可以是下列任何一个:
IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 或 UDPv6。
-q 显示所有连接、侦听端口和绑定的
非侦听 TCP 端口。绑定的非侦听端口
不一定与活动连接相关联。
-r 显示路由表。
-s 显示每个协议的统计信息。默认情况下,
显示 IP、IPv6、ICMP、ICMPv6、TCP、TCPv6、UDP 和 UDPv6 的统计信息;
-p 选项可用于指定默认的子网。
-t 显示当前连接卸载状态。
-x 显示 NetworkDirect 连接、侦听器和共享
终结点。
-y 显示所有连接的 TCP 连接模板。
无法与其他选项结合使用。
interval 重新显示选定的统计信息,各个显示间暂停的
间隔秒数。按 CTRL+C 停止重新显示
统计信息。如果省略,则 netstat 将打印当前的
配置信息一次。
基础代码来源于《linux网络编程 第二版》宋敬彬等编著,这里进行了改造和扩展;