您的位置:首页 > 理论基础 > 计算机网络

liunx网络编程(1) 使用select 改造现有代码

2013-12-04 20:38 281 查看
(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



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"


03:51:55.506292 IP localhost.46283 > localhost.12345: Flags [R.], seq 23, ack 49, win 513, options [nop,nop,TS val 35611448 ecr 35610261], length 0

 
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详解 卷一》

后续带补充 第二章节
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐