(1)分析client代码执行
功能:发出请求 发送和读取字符串 然后关闭连接
Gdb调试client:
wang@ubuntu:~/study/code$ gdb -p 9008
(gdb) bt
#0 0x00a78422 in __kernel_vsyscall ()
#1 0x00ec5681 in recv () from /lib/tls/i686/cmov/libc.so.6
#2 0x08048b1a in main (argc=1, argv=0xbfe85e04) at client.cpp:73
(gdb)
查看网络状态
wang@ubuntu:~$ netstat -an |grep 12345
tcp 0 0 0.0.0.0:12345 0.0.0.0:* LISTEN
tcp 22 0 127.0.0.1:12345 127.0.0.1:46285 ESTABLISHED
tcp 0 0 127.0.0.1:46285 127.0.0.1:12345 ESTABLISHED
结论:
1 client和server 建立了连接 tcp:ESTABLISHED
但是为什么client 一直在阻塞recv()数据
应该执行完毕 马上退出
代码:
memset(send_buf,0,sizeof(send_buf));
sprintf(send_buf,"client client client \n");
if (send(client_socket,send_buf,strlen(send_buf),0) == -1)
{
printf("send error/n");
close(client_socket);
return -1;
}
// 阻塞在 recv中 直到 server 发出信息 tcp:ESTABLISHED
recvBytes=recv(client_socket,recv_buf,strlen(recv_buf),0);
if(recvBytes >0)
{
printf("server::: %s \n", recv_buf);//接收数据包正常,数据包中的数据大于0个字节
}else if(recvBytes == 0 )
{
printf("client: %s \n", recv_buf);
memset(recv_buf,0,sizeof(recv_buf));
//close(client_socket);//传输数据包操作完成,连接断开
break;
}else if(recvBytes <0)
{
close(client_socket);
perror("recv error");
return -1;
}
Tcpdump跟踪
sudo tcpdump -i 5 "tcp and port 12345"
09:19:57.059094 IP localhost.46282 > localhost.12345: Flags [S], seq 2489333847, win 32792, options [mss 16396,sackOK,TS val 32534158 ecr 0,nop,wscale 6], length 0
09:19:57.059116 IP localhost.12345 > localhost.46282: Flags [S.], seq 86594463, ack 2489333848, win 32768, options [mss 16396,sackOK,TS val 32534158 ecr 32534158,nop,wscale 6], length 0
09:19:57.059189 IP localhost.46282 > localhost.12345: Flags [.], ack 1, win 513, options [nop,nop,TS val 32534158 ecr 32534158], length 0
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
09:19:57.059712 IP localhost.12345 > localhost.46282: Flags [P.], seq 1:25, ack 1, win 512, options [nop,nop,TS val 32534158 ecr 32534158],length 24
09:19:57.059778 IP localhost.46282 > localhost.12345: Flags [.], ack 25, win 513, options [nop,nop,TS val 32534158 ecr 32534158], length 0
分析:
length 0 的行是tcp的确认信息 保证可靠连接 每次才做都进行确认 底层对外不可见
行数:
1
client(46282) 发出concet 请求 【Flags [S]】 * S : SYN - 同步; 表示开始会话请求 请求 seq =2489333847
2
server(12345) ack =seq+1= 2489333848 进行确认(tcp是可靠的传输)表明你发出的请求确认了
然后发出可以连接的请求seq
3
client(46282) --确认 Flags [.] (tcp是可靠的传输)
············································3次握手完毕 开始建立了连接----------------------------------------
4 Client--->server
Flags [P.], 表示开会传递数据
5 Server-client 确认。
没有退出Flags [R.],
然后gdb服务端在做什么
(gdb) bt
#0 0x00886422 in __kernel_vsyscall ()
#1 0x002b07b3 in read () from /lib/tls/i686/cmov/libc.so.6
#2 0x002571db in _IO_file_underflow () from /lib/tls/i686/cmov/libc.so.6
#3 0x00258a7b in _IO_default_uflow () from /lib/tls/i686/cmov/libc.so.6
#4 0x00259ea8 in __uflow () from /lib/tls/i686/cmov/libc.so.6
#5 0x0024f91c in getchar () from /lib/tls/i686/cmov/libc.so.6
#6 0x08048d6f in main (arg=1, argv=0xbf9e5164) at server.cpp:127
(gdb)
结论:
服务端功停留在输入一个 getchar()
查看代码
if(getchar() == -1)
{
close(serverSock);//停止server
break;
}
printf("server send begin....\n");
//发送客户端的信息
if (send(clientSock,send_buf,strlen(send_buf),0) == -1)
输入 a
客户端输出:
Tcpdump:
sudo tcpdump -i 5 "tcp and port 12345"
Client 发出 close 请求 程序退出 [ R : RST - 复位;中断一个连接 ]
---------------------- 优化建议 ----------------------------------------------------------------
Select在Socket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如
connect、accept、recv或recvfrom这样的阻塞程序(所谓阻塞方式block,顾名思义,就是进程或是线程执行到这些函数时必须等
待某个事件的发生,如果事件没有发生,进程或线程就被阻塞,函数不能立即返回)。
可是使用Select就可以完成非阻塞
在client中添加 select 出来
FD_ZERO(&fdsr);//清除描述符集
FD_SET(serverSock, &fdsr);//把sock_fd加入描述符集
ret = select(maxsock + 1, &fdsr, NULL, NULL, &tv);
if (ret < 0)
{ 没有找到有效的连接 失败
perror("select");
break;
}else if (ret == 0)
{ /// 指定的时间到 连接进来
//printf("timeout\n");
continue;
}
补充tcp基础知识
TCP FLAG 标记
基于标记的TCP包匹配经常被用于过滤试图打开新连接的TCP数据包。TCP标记和他们的意义如下所列:
* F : FIN - 结束; 结束会话
* S : SYN - 同步; 表示开始会话请求
* R : RST - 复位;中断一个连接
* P : PUSH - 推送; 数据包立即发送
* A : ACK - 应答
* U : URG - 紧急
* E : ECE - 显式拥塞提醒回应
* W : CWR - 拥塞窗口减少
参考
《TCP/IP详解 卷一》
后续带补充 第二章节