tcp应用服务器开发经验请教?

kacy16 2014-02-12 04:36:18
目前正在开发一个应用服务器程序,大概有十几个左右(不超过20)的客户终端机
与该服务器连接,使用tcp协议来进行网络通信。该服务器需要长时间(几个月或1年)稳定可靠的
运行,为各客户端提供服务。服务器采用linux系统(linux2.6),客户端暂采用windows系统,
由于我对于开发稳定的产品级的网络服务程序缺乏实战的经验,所以有以下几个问题向各位高手请教,

1 服务端通信服务使用单进程多线程的架构,非阻塞通信设置。网络通信这块分三个线程来实现,
A线程为专门的连接处理线程,等待在listen的socket上的客户端连接,并把
进来的连接保存。
B线程为专门的接收处理线程,采用select方式的IO多路复用方式在Accept好的连接socket
进行接收数据,并把它放置到全局的接收数据队列。
C线程为发送处理线程,在已连接的socket上发送数据。
请问这样的架构是否合理,是否有改进的地方?

2 虽然TCP协议是可靠的通信协议,在发送时如何确定发送数据成功与否?

3 由于服务端和客户端是采用tcp上时间保持连接的方式,当由于各种原因连接意外中断时,在服务端如何确定连接的意外中断?

4 一个稳定的产品级的网络服务程序在调用几个常用的socket的API函数时有什么需要注意的细节?

我个人也下载了nagix,libevent等开源的网络程序库看了下,但是感觉较难在短时间内学习到产品级的网络开发的

经验,所以在论坛上贴出该问题,欢迎各位高手发表高见或推荐更轻量级的tcp开源程序,谢谢!
...全文
329 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
t496036222 2014-09-18
  • 打赏
  • 举报
回复
学习力,版主和beginning1126回答都很好
beginning1126 2014-02-21
  • 打赏
  • 举报
回复
引用 16 楼 kacy16 的回复:
[quote=引用 15 楼 beginning1126 的回复:] [quote=引用 13 楼 kacy16 的回复:] [quote=引用 3 楼 feiyinzilgd 的回复:] 链接断开,服务端是可以知道的。向一个断开的socket fd写数据,也会报错。
feiyinzilgd兄,关于这点,连接断开的情况有分几种, A 对端连接(指连接的另外一方)的程序退出,然后对端操作系统的tcp层会给该连接的本端发送FIN信号终止TCP连接,此时本端的tcp连接处于半关闭状态,此后当本端使用read时返回0,第一次使用write往其中往该连接写入数据时,不会有异常,第二次写入时才会触发SIGPIPE的信号产生。 B 对端主机崩溃或网络中间断开。本端write是无法当前确定网络连接的中断的,只有read的时候,返回ETIMEOUT或者EHOSTUNREACH的错误。 以上是我的一些看Steven的UNP(Unix网络编程)书的个人总结, 烦请各路高手指正,谢谢![/quote] 对于情况1:对端关闭,处于可读不可写的状态,为什么会出现第一次可以写,第二次写返回SIGPIPE信号?socket可以使用shutdown函数控制关闭情况,不过一般很少会这么用,直接close就完事儿了。 [/quote] 对端关闭,表示对端的数据发送完毕。本端的socke收到对端发来的FIN标记数据,并回复给对端ACK, 本端接收到FIN标记数据表示本端在该连接上再不需要接收数据。所以此时对本端来说是读会返回0(0表示eof,表示读结束了), 写是可以的。这点我和您的“处于可读不可写的状态”是由严重的不同理解的,请指正![/quote] 对端先关闭,处于可读不可写的状态,本端读和写都应该是可以的。我的理解是这样的。
kacy16 2014-02-21
  • 打赏
  • 举报
回复
引用 15 楼 beginning1126 的回复:
[quote=引用 13 楼 kacy16 的回复:] [quote=引用 3 楼 feiyinzilgd 的回复:] 链接断开,服务端是可以知道的。向一个断开的socket fd写数据,也会报错。
feiyinzilgd兄,关于这点,连接断开的情况有分几种, A 对端连接(指连接的另外一方)的程序退出,然后对端操作系统的tcp层会给该连接的本端发送FIN信号终止TCP连接,此时本端的tcp连接处于半关闭状态,此后当本端使用read时返回0,第一次使用write往其中往该连接写入数据时,不会有异常,第二次写入时才会触发SIGPIPE的信号产生。 B 对端主机崩溃或网络中间断开。本端write是无法当前确定网络连接的中断的,只有read的时候,返回ETIMEOUT或者EHOSTUNREACH的错误。 以上是我的一些看Steven的UNP(Unix网络编程)书的个人总结, 烦请各路高手指正,谢谢![/quote] 对于情况1:对端关闭,处于可读不可写的状态,为什么会出现第一次可以写,第二次写返回SIGPIPE信号?socket可以使用shutdown函数控制关闭情况,不过一般很少会这么用,直接close就完事儿了。 [/quote] 对端关闭,表示对端的数据发送完毕。本端的socke收到对端发来的FIN标记数据,并回复给对端ACK, 本端接收到FIN标记数据表示本端在该连接上再不需要接收数据。所以此时对本端来说是读会返回0(0表示eof,表示读结束了), 写是可以的。这点我和您的“处于可读不可写的状态”是由严重的不同理解的,请指正!
beginning1126 2014-02-20
  • 打赏
  • 举报
回复
引用 13 楼 kacy16 的回复:
[quote=引用 3 楼 feiyinzilgd 的回复:] 链接断开,服务端是可以知道的。向一个断开的socket fd写数据,也会报错。
feiyinzilgd兄,关于这点,连接断开的情况有分几种, A 对端连接(指连接的另外一方)的程序退出,然后对端操作系统的tcp层会给该连接的本端发送FIN信号终止TCP连接,此时本端的tcp连接处于半关闭状态,此后当本端使用read时返回0,第一次使用write往其中往该连接写入数据时,不会有异常,第二次写入时才会触发SIGPIPE的信号产生。 B 对端主机崩溃或网络中间断开。本端write是无法当前确定网络连接的中断的,只有read的时候,返回ETIMEOUT或者EHOSTUNREACH的错误。 以上是我的一些看Steven的UNP(Unix网络编程)书的个人总结, 烦请各路高手指正,谢谢![/quote] 对于情况1:对端关闭,处于可读不可写的状态,为什么会出现第一次可以写,第二次写返回SIGPIPE信号?socket可以使用shutdown函数控制关闭情况,不过一般很少会这么用,直接close就完事儿了。
beginning1126 2014-02-20
  • 打赏
  • 举报
回复
引用 12 楼 kacy16 的回复:
[quote=引用 8 楼 beginning1126 的回复:] 2、tcp本身有丢包重传,其可靠性是不用应用层操心的。如果想知道send数据是否ok,可以看send的返回值,小于0就是有问题了。
关于这点,我有个疑问,send函数的功能应该是把数据从复制到socket的内核发送缓冲区,不敢保证是一定能够发送至对端的。所以send返回大于0的值不一定保证数据有发送到对端。而且如果是中途网络中断,单凭这一次的调用send函数的结果是不能确定数据是否确实成功发送到对端的。不知道我的理解正确否?请各位指正,谢谢![/quote] 是这样的,但是如果不是网络断掉,是不会发生数据丢失的情况,tcp是会自动丢包重传的。 所以如果不是金融业,对丢包特别敏感,tcp的可靠性是可以接受的。
kacy16 2014-02-19
  • 打赏
  • 举报
回复
引用 3 楼 feiyinzilgd 的回复:
链接断开,服务端是可以知道的。向一个断开的socket fd写数据,也会报错。
feiyinzilgd兄,关于这点,连接断开的情况有分几种, A 对端连接(指连接的另外一方)的程序退出,然后对端操作系统的tcp层会给该连接的本端发送FIN信号终止TCP连接,此时本端的tcp连接处于半关闭状态,此后当本端使用read时返回0,第一次使用write往其中往该连接写入数据时,不会有异常,第二次写入时才会触发SIGPIPE的信号产生。 B 对端主机崩溃或网络中间断开。本端write是无法当前确定网络连接的中断的,只有read的时候,返回ETIMEOUT或者EHOSTUNREACH的错误。 以上是我的一些看Steven的UNP(Unix网络编程)书的个人总结, 烦请各路高手指正,谢谢!
kacy16 2014-02-19
  • 打赏
  • 举报
回复
引用 8 楼 beginning1126 的回复:
2、tcp本身有丢包重传,其可靠性是不用应用层操心的。如果想知道send数据是否ok,可以看send的返回值,小于0就是有问题了。
关于这点,我有个疑问,send函数的功能应该是把数据从复制到socket的内核发送缓冲区,不敢保证是一定能够发送至对端的。所以send返回大于0的值不一定保证数据有发送到对端。而且如果是中途网络中断,单凭这一次的调用send函数的结果是不能确定数据是否确实成功发送到对端的。不知道我的理解正确否?请各位指正,谢谢!
beginning1126 2014-02-14
  • 打赏
  • 举报
回复
引用 9 楼 feiyinzilgd 的回复:
[quote=引用 8 楼 beginning1126 的回复:] 1、如果只有20个连接,处理用户的listen和消息接收,用一个线程就够了,甚至如果发送数据量不大,第三个线程也可以省略的。 2、tcp本身有丢包重传,其可靠性是不用应用层操心的。如果想知道send数据是否ok,可以看send的返回值,小于0就是有问题了。 3、可以更改keepalive心跳间隔,默认值是2小时,可以通过socket option修改,不过这个值是全局的哦,修改了整个服务器都会有影响。 4、注意接收和发送buf的大小问题,注意判断recv和send的返回值。 5、强烈建议把select换成epoll,而且,仅仅20个连接,虽然是长连接,个人经验,性能应该不会有什么瓶颈,一个线程,一个epoll,用于接收和发送,我觉得就可以搞定,实在搞不定在分成2线程也来得及,毕竟一个线程,出错的几率就会小很多。
简单的通信是没问题的。应用层完全依赖于tcp协议层对于复杂的、高可靠性应用是无法满足的,还是需要应用层协议辅助使用。[/quote] 如果可靠性要求比较高,确实需要应用层的额外处理,同意
谭海燕 2014-02-13
  • 打赏
  • 举报
回复
确保稳定性,我经常使用的方案是: 1.设计应用层握手协议,tcp的握手只是协议层的,为了确保数据稳定可靠,在tcp链接之后,应用层, 也就是server/client也需要建立自己的握手协议,只有握手成功之后,才可以发送数据。 2.心跳包是为了让server知道client还活着,所以,需要client向server发。需要有个watchDog之类的线程来维护,设定一个timer,如果在规定的时间内没收到client的心跳包,说明client有可能已经断掉了。然后做出相应的处理逻辑。
kacy16 2014-02-13
  • 打赏
  • 举报
回复
引用 3 楼 feiyinzilgd 的回复:
链接断开,服务端是可以知道的。向一个断开的socket fd写数据,也会报错。
谢谢feiyinzilgd,在客户端,当客户端往连接中断的socket写数据时,应该也会报错。 另外有一个问题想请教下,心跳包的设计是由服务端主动先发出,还是由客户端主动先发出好呢?
kacy16 2014-02-13
  • 打赏
  • 举报
回复
引用 8 楼 beginning1126 的回复:
1、如果只有20个连接,处理用户的listen和消息接收,用一个线程就够了,甚至如果发送数据量不大,第三个线程也可以省略的。
谢谢beginning1126的指点,关于第1点,确实考虑过把listen和消息接收放到同一线程中,这样的话代码稍微复杂一点,我再评估看看。 之所以有一个专门的发送线程,也是因为应用服务在处理各个客户端的请求,不想在各个业务功能处理逻辑中发送返回信息,把发送消息的动作散落在各处的代码中。所以集中一个发送线程去处理了。
谭海燕 2014-02-13
  • 打赏
  • 举报
回复
引用 8 楼 beginning1126 的回复:
1、如果只有20个连接,处理用户的listen和消息接收,用一个线程就够了,甚至如果发送数据量不大,第三个线程也可以省略的。 2、tcp本身有丢包重传,其可靠性是不用应用层操心的。如果想知道send数据是否ok,可以看send的返回值,小于0就是有问题了。 3、可以更改keepalive心跳间隔,默认值是2小时,可以通过socket option修改,不过这个值是全局的哦,修改了整个服务器都会有影响。 4、注意接收和发送buf的大小问题,注意判断recv和send的返回值。 5、强烈建议把select换成epoll,而且,仅仅20个连接,虽然是长连接,个人经验,性能应该不会有什么瓶颈,一个线程,一个epoll,用于接收和发送,我觉得就可以搞定,实在搞不定在分成2线程也来得及,毕竟一个线程,出错的几率就会小很多。
简单的通信是没问题的。应用层完全依赖于tcp协议层对于复杂的、高可靠性应用是无法满足的,还是需要应用层协议辅助使用。
beginning1126 2014-02-13
  • 打赏
  • 举报
回复
1、如果只有20个连接,处理用户的listen和消息接收,用一个线程就够了,甚至如果发送数据量不大,第三个线程也可以省略的。 2、tcp本身有丢包重传,其可靠性是不用应用层操心的。如果想知道send数据是否ok,可以看send的返回值,小于0就是有问题了。 3、可以更改keepalive心跳间隔,默认值是2小时,可以通过socket option修改,不过这个值是全局的哦,修改了整个服务器都会有影响。 4、注意接收和发送buf的大小问题,注意判断recv和send的返回值。 5、强烈建议把select换成epoll,而且,仅仅20个连接,虽然是长连接,个人经验,性能应该不会有什么瓶颈,一个线程,一个epoll,用于接收和发送,我觉得就可以搞定,实在搞不定在分成2线程也来得及,毕竟一个线程,出错的几率就会小很多。
zx9786 2014-02-13
  • 打赏
  • 举报
回复
同样问题,过来看看
kacy16 2014-02-13
  • 打赏
  • 举报
回复
引用 5 楼 feiyinzilgd 的回复:
确保稳定性,我经常使用的方案是: 1.设计应用层握手协议,tcp的握手只是协议层的,为了确保数据稳定可靠,在tcp链接之后,应用层, 也就是server/client也需要建立自己的握手协议,只有握手成功之后,才可以发送数据。 2.心跳包是为了让server知道client还活着,所以,需要client向server发。需要有个watchDog之类的线程来维护,设定一个timer,如果在规定的时间内没收到client的心跳包,说明client有可能已经断掉了。然后做出相应的处理逻辑。
谢谢您的无私指点,谢谢! 继续欢迎各位发表高见,谢谢!
谭海燕 2014-02-12
  • 打赏
  • 举报
回复
链接断开,服务端是可以知道的。向一个断开的socket fd写数据,也会报错。
kacy16 2014-02-12
  • 打赏
  • 举报
回复
版主,谢谢您的回答。 1 对于每个client一个进程的确实对稳定性方面有帮助,但是我的应用服务器端有几个业务功能是在某时刻独占某个设备进行工作的,需要对所有客户端的请求进行排队,并在服务端进行依次处理。所以我设计的通信框架有一个独立的接收线程,把所有客户端的请求均放入到一任务队列中,另外有一个工作线程进行处理。如果是多进程的话,可能还需要协调各客户端进程的业务处理过程。
引用 1 楼 feiyinzilgd 的回复:
3.======>合理的设置TCP的KeepAlive,或者设计自己的心跳包。
关于这点,我觉得设置KeepAlive和心跳包是能在一定的时间间隔内检测出连接的中断。 我担心的是在这个情况,刚心跳包测试完,就在这时连接异常中断了,此时刚好服务端需在异常中断的连接上发送数据,还未到下一次心跳检测的时候,此时服务端是否能从发送数据的过程中知道连接已异常中断?
谭海燕 2014-02-12
  • 打赏
  • 举报
回复
简单的回答下: 1.=====>如果你对稳定性要求较高,建议使用多进程,每次来一个client,fork一个进程对其专门处理 3.======>合理的设置TCP的KeepAlive,或者设计自己的心跳包。

23,120

社区成员

发帖
与我相关
我的任务
社区描述
Linux/Unix社区 应用程序开发区
社区管理员
  • 应用程序开发区社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧