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

(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 clientserver 建立了连接 tcpESTABLISHED

 但是为什么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 - 复位;中断一个连接 

    ---------------------- 优化建议 ----------------------------------------------------------------

SelectSocket编程中还是比较重要的,可是对于初学Socket的人来说都不太爱用Select写程序,他们只是习惯写诸如 
connectacceptrecvrecvfrom这样的阻塞程序(所谓阻塞方式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详解 卷一》

后续带补充 第二章节


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值