linux下,socket服务器和客户端TCP方式建立了连接,如何使它们之间相互发送消息?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了linux下,socket服务器和客户端TCP方式建立了连接,如何使它们之间相互发送消息?相关的知识,希望对你有一定的参考价值。

我的写的程序建立连接后,服务器只能接受客户端一行消息?服务器不能向客户端发送消息。

1.可能是在获取客户端的ip和端口时,处理出现问题,导致无法正确发送到客户端。
2.客户端是否使用固定的端口来接收服务器信息,或服务器是否正确发送到客户端的相应的端口。
3.通过上面分析,最大可能是在处理端口出现问题,请重新检查。
4.实在不行,最好使用抛出异常方法来捕获错误消息,或是通过一步一步调试分析数据发送过程。
参考技术A 你的接收端有没有放在循环或者线程里反复接收?有没有设置同步,如果没设置同步的话就不是没收到,而是收的速度太快了,数据混乱了。既然说了已经能接收到一行消息了,端口应该是没问题的,因为端口问题主要发生在绑定阶段,既然能接收到,说明绑定没问题。最大的问题应该就是消息同步问题,可以用一收一发的方式,接收放到if里,因为当recv的返回值大于0(就是直接if(recv(...))时),再发送下一条消息 参考技术B 通过系统调用的函数可以访问,比如:int read(int socketfd,char *buffer,size_t size) ; int write(int socketfd,char *buffer,size_t size); 这是两个读取数据和写入数据的函数原型(具体的型参名字忘记了,不过型参类型是正确的)。网上有个聊天程序的源代码,你可以看看,希望对你有帮助。 参考技术C 检查你的程序是否正确使用了建立连接后生成的,代表这个连接的唯一socket对象。

Linux 建立 TCP 连接的超时时间分析

inux 系统默认的建立 TCP 连接的超时时间为 127 秒,对于许多客户端来说,这个时间都太长了, 特别是当这个客户端实际上是一个服务的时候,更希望能够尽早失败,以便能够选择其它的可用服务重新尝试。

socket 是 Linux 下实现的传输控制层协议,包括 TCP 和 UDP,一个 socket 端点由 IP 和端口对来唯一标识; 如果开启了地址复用,那么可以进一步由协议,IP 和端口来唯一标识。

系统调用 connect(2) 则是用来尝试建立 socket 连接(TCP)或者和远程协商一致(UDP)的函数。 connect 对于 UDP 来说并不是必须的,而对于 TCP 来说则是一个必须过程,注明的 TCP 3 次握手实际上也由 connect 来完成。

注:这里只分析 TCP 连接超时

网络中的连接超时非常常见,不管是广域网还是局域网,为了一定程度上容忍失败,所以连接加入了重试机制, 而另一方面,为了不给服务端带来过大的压力,重试也是有限制的。

在 Linux 中,连接超时典型为 2 分 7 秒,而对于一些 client 来说,这是一个非常长的时间; 所以在编程中,可以使用非阻塞的方式来实现,例如:使用 poll(2), epoll(2), select(2) 等系统调用来实现多路复用等待。

下面来看看 2 分 7 秒是怎样来的,以及怎样配置 Linux kernel 来缩短这个超时。

 

2 分 7 秒


2 分 7 秒即 127 秒,刚好是 2 的 7 次方减一,聪明的读者可能已经看出来了,如果 TCP 握手的 SYN 包超时重试按照 2 的幂来 backoff, 那么:

  1. 第 1 次发送 SYN 报文后等待 1s(2 的 0 次幂),如果超时,则重试
  2. 第 2 次发送后等待 2s(2 的 1 次幂),如果超时,则重试
  3. 第 3 次发送后等待 4s(2 的 2 次幂),如果超时,则重试
  4. 第 4 次发送后等待 8s(2 的 3 次幂),如果超时,则重试
  5. 第 5 次发送后等待 16s(2 的 4 次幂),如果超时,则重试
  6. 第 6 次发送后等待 32s(2 的 5 次幂),如果超时,则重试
  7. 第 7 次发送后等待 64s(2 的 6 次幂),如果超时,则超时失败

上面的结果刚好是 127 秒。也就是说 Linux 内核在尝试建立 TCP 连接时,最多会尝试 7 次。

那么下面通过具体方法来验证。

首先,配置 iptables 来丢弃指定端口的 SYN 报文

 iptables -A INPUT --pr iptables -A INPUT --protocol tcp --dport 5000 --syn -j DROPotocol tcp --dport 5000 --syn -j DROP

然后,打开 tcpdump 观察到达指定端口的报文

# tcpdump -i lo -Ss0 -n src 127.0.0.1 and dst 127.0.0.1 and port 5000

最后,使用 telnet 连接指定端口

$ date; telnet 127.0.0.1 5000; date

上面命令的输出如下:

Tue Jan  3 16:39:05 CST 2017
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection timed out
Tue Jan  3 16:41:12 CST 2017

而从 tcpdump 命令的输出可以看到:

16:39:05.690238 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179222486 ecr 0,nop,wscale 7], length 0
16:39:06.686988 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179222736 ecr 0,nop,wscale 7], length 0
16:39:08.690980 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179223237 ecr 0,nop,wscale 7], length 0
16:39:12.702973 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179224240 ecr 0,nop,wscale 7], length 0
16:39:20.718991 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179226244 ecr 0,nop,wscale 7], length 0
16:39:36.766986 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179230256 ecr 0,nop,wscale 7], length 0
16:40:08.830996 IP 127.0.0.1.58933 > 127.0.0.1.5000: Flags [S], seq 2286786481, win 43690, options [mss 65495,sackOK,TS val 179238272 ecr 0,nop,wscale 7], length 0

其中,Flags [S] 表示为 SYN 报文,可以看到总共发送了 7 次 SYN 报文,最后一次的时间为 16:40:08,而 telnet 超时退出的时间为 16:41:12相差 64 秒。

怎样修改 connect timeout


对于很多客户端程序来说,127 秒都是一个很长的时间,特别是对于局域网来说,公司内部往往都具有网络质量较好的局域网, 访问内部的服务并不需要等待这么长的超时,而可以 fail earlier。

Linux 内核中,net.ipv4.tcp_syn_retries 表示建立 TCP 连接时 SYN 报文重试的次数,默认为 6,可以通过 sysctl 命令查看。

# sysctl -a | grep tcp_syn_retries
net.ipv4.tcp_syn_retries = 6

将其修改为 1,则可以将 connect 超时时间改为 3 秒,例如:

# sysctl net.ipv4.tcp_syn_retries=1

再次使用 telnet 验证超时时间,如下:

$ date; telnet 127.0.0.1 5000; date
Fri Feb 17 09:50:12 CST 2017
Trying 127.0.0.1...
telnet: Unable to connect to remote host: Connection timed out
Fri Feb 17 09:50:15 CST 2017

注意:sysctl 修改的内核参数在系统重启后失效,如果需要持久化,可以修改系统配置文件

例如:,对于 CentOS 7 来说,添加 net.ipv4.tcp_syn_retries = 1 到 /etc/sysctl.conf 中即可。

 

以上是关于linux下,socket服务器和客户端TCP方式建立了连接,如何使它们之间相互发送消息?的主要内容,如果未能解决你的问题,请参考以下文章

网络编程-Socket编程(异步通讯)(Tcp,Udp)

网络编程-Socket编程(异步通讯)(Tcp,Udp)

linux网络编程-socket

linux系统socket通信编程1

Linux网络编程——TCP和UDP通信

Linux-TCP编程流程-Socket编程-单线程实现TCP客户端和服务端交互-多进程实现TCP客户端和服务端交互