我们知道在TCPIP中客户端同服务端建立连接,需要三次握手:
第一次:客户端向服务端请求发起连接,发送SYN
第二次:服务端收到客户端的请求后,向客户端回送ACK和SYN
第三次:客户端接收到服务端可以连接的信息,再向服务端发送ACK,表示收到服务端信息完成连接。
在刚刚接触网络编程时,很长一段时间都以为只有服务端调用accept后,客户端才会connect成功,但是实际上只要服务端开启listen,客户端就会连接成功,并可以发送数据,下面就通过试验验证这一点:
以下的服务端的代码:
#include
#inlude
#include
#include
#include
#include
#define BUF_SIZE 100
void print_time();
void error_handling(char* message);
int main(int argc,char* argv[])
{
int serv_sock,clnt_sock;
struct sockaddr_in serv_addr,clnt_addr;
int clnt_addr_sz;
int str_len,i,j;
char buf[BUF_SIZE];
if(argc!=2)
{
printf("Usage %s \n",argv[0]);
exit(1);
}
//创建socket
serv_sock=socket(AF_INET,SOCK_STREAM,0);
if(serv_sock==-1)
error_handling("socket error");
//填充地址信息
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
serv_addr.sin_port=htons(atoi(argv[1]));
//socket和ip地址的绑定
if(bind(serv_sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
error_handling("bind error");
//开启监听
if(listen(serv_sock,5)==-1)
error_handling(" listen error");
sleep(10);
fputs("end sleep:",stdout);
print_time();
for(i=0;i<5;i++)
{
clnt_addr_sz=sizeof(clnt_addr);
clnt_sock=accept(serv_sock,(struct sockaddr*)&clnt_addr,&clnt_addr_sz);
if(clnt_sock==-1)
error_handling("accept error");
else
printf("clnt:%s connected\n",inet_ntoa(clnt_addr.sin_addr));
//接受数据
while(1)
{
str_len=read(clnt_sock,buf,BUF_SIZE);
write(clnt_sock,buf,str_len);
memset(buf,0,sizeof(buf));
if(str_len<=0)
break;
}
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void print_time()
{
time_t now=time(0);
struct tm* ptm=localtime(&now);
char buf[256]={0};
sprintf(buf,"time now:[%02d-%02d-%02d %02d:%02d:%02d]",
ptm->tm_year+1900,
ptm->tm_mon+1,
ptm->tm_mday,
ptm->tm_hour,
ptm->tm_min,
ptm->tm_sec);
puts(buf);
}
void error_handling(char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
请留意在调用 listen()开启监听后的sleep(10),我们让服务器睡了10s中,以此来验证客户端程序依然可以正常的connect,同时sleep结束后调用了一下print_time(),打印了一下当前的时间。接下来是客户端的代码:
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 100
void print_time();
void error_handling(const char* message);
int main(int argc,char* argv[])
{
int sock;
struct sockaddr_in serv_addr;
int str_len;
char buf[BUF_SIZE];
int recv_len=0;
//创建socket
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock==-1)
error_handling("socket error");
//准备地址
memset(&serv_addr,0,sizeof(serv_addr));
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(argv[1]);
serv_addr.sin_port=htons(atoi(argv[2]));
//链接
if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1)
error_handling("connect error");
else
puts("connect succeed");
print_time();
while(1)
{
memset(buf,0,BUF_SIZE);
fputs("请输入数据:",stdout);
fgets(buf,BUF_SIZE,stdin);
if(!strcmp(buf,"q\n")||!strcmp(buf,"Q/n"))
break;
print_time();
str_len=write(sock,buf,strlen(buf));
puts("writed");
print_time();
sizeof(buf,0,sizeof(buf));
while(recv_lentm_year+1900,
ptm->tm_mon+1,
ptm->tm_mday,
ptm->tm_hour,
ptm->tm_min,
ptm->tm_sec);
puts(buf);
}
void error_handling(const char* message)
{
fputs(message,stderr);
fputc('\n',stderr);
exit(1);
}
我们在进行connect服务端后,如果connect成功,就会打印出来一个“connected succeed”的字符串,同时利用print_time打印出当前的时间,只要打印出来的时间早于服务器中打印出来的时间就证明了我们的猜想,那么请看打印的结果:
客户端中:
[Hyman@Hyman-PC echoSever]$ ./clnt 127.0.0.1 9190
connect succeed
time now:[2016-10-08 11:31:53]
请输入数据:
[Hyman@Hyman-PC echoSever]$ ./serv 9190
end sleep:time now:[2016-10-08 11:32:02]
clnt:127.0.0.1 connected
也可以用netstat查看当前的端口监听的情况:
[Hyman@Hyman-PC echoSever]$ netstat -an >> sock.txt