美文网首页Java 杂谈
通过线程编写一个简单的并发服务器

通过线程编写一个简单的并发服务器

作者: Java架构_师 | 来源:发表于2018-09-30 14:55 被阅读23次

概述

之前在上一节通过使用fork来实现了一个并发程序,它很经典但是效率不高主要是太消耗资源因为fork一个进程的开销很大,假如100客户端连接就需要100个进程,这样不是不可以只是这种方式不太高级,下面我们通过使用线程来实现并发,因为产生一个线程的开销要小的多,当然对于大规模并发的场景使用线程也不是最好的选择,但是学习socket编程的过程中这些东西是需要了解的。

代码段

服务器端

1 #!/usr/bin/env python

2 # -*- coding: utf-8 -*-

3 # Author: rex.cheny

4 # E-mail: rex.cheny@outlook.com

5

6 import socket

7 import time

8 import threading

9

10

11 def echoStr(connFd):

12 print("新连接:", connFd.getpeername())

13 while True:

14 bytesData = connFd.recv(1024)

15 data = bytesData.decode(encoding="utf-8")

16 print("收到客户端消息:", data)

17 if data == "Bye":

18 """

19 这里需要关闭连接,在之前的fork模式中这里是直接返回的

20 不过使用线程则需要先关闭再返回。

21 """

22 connFd.close()

23 return

24 else:

25 time.sleep(1)

26 connFd.send(data.encode(encoding="utf-8"))

27

28

29 def main():

30 sockFd = socket.socket()

31 sockFd.bind(("", 5555))

32 sockFd.listen(5)

33

34 print("等待客户端连接......")

35 while True:

36 connFd, remAddr = sockFd.accept()

37 try:

38 """

39 这里产生一个线程来处理连接,我们在这里启动线程后不能像fork模式那样关闭连接套接字

40 因为线程是共享进程资源的所以你这里如果关闭那么这个TCP连接也就断了。而之前在fork

41 模式中需要关闭是因为进程的资源是隔离的父子进程对同一个文件描述符的两次引用,而在

42 线程里对这个一个文件描述符只引用了一次,所以这里不能关闭。

43 """

44 t = threading.Thread(target=echoStr, args=(connFd,))

45 t.start()

46 except Exception as err:

47 print(err)

48

49

50 if __name__ == '__main__':

51 main()

客户端代码

与之前的相同,不过这里还是放进来

1 #!/usr/bin/env python

2 # -*- coding: utf-8 -*-

3 # Author: rex.cheny

4 # E-mail: rex.cheny@outlook.com

5

6 import socket

7

8

9 def echoStr(sockFd, data):

10 sockFd.send(data)

11 bytesData = sockFd.recv(1024)

12 data = bytesData.decode(encoding="utf-8")

13 print(data)

14

15

16 def main():

17 sockFd = socket.socket()

18 sockFd.connect(("127.0.0.1", 5555))

19

20 for i in range(1, 11):

21 data = "第:" + str(i) + " 条消息。"

22 echoStr(sockFd, data.encode(encoding="utf-8"))

23 加群:874811168即可免费领取java资料一份。

24 echoStr(sockFd, "Bye".encode(encoding="utf-8"))

25 sockFd.close()

26

27

28 if __name__ == '__main__':

29 main()

结果演示

效果一样。不过这里有一个问题其实包括之前的fork版本的程序也有问题就是如果处理客户端请求的进程崩溃,那么服务器端TCP协议栈会发送FIN,这时候客户端肯定可以收到并回复ACK,但是如果客户端这时候刚好要发送下一条消息,这时候客户端就是在一个已收到FIN的套接字里写数据,那么它会收到一个RST,它写完了马上会调用读(从代码里可以看出来)那么读一个收到RST的套接字将会报错。而如果它收到FIN的时候刚好是读则会得到一个EOF。这也就是上一节里末尾的图所说明的内容。其实如果客户端能够及时对收到的FIN做出反应那么将避免对收到FIN的套接字进行操作,其实在我们的例子中效果不明显因为我的客户端程序是自动发送消息的,如果你改成输入消息则会非常明显。因为会阻塞在用户输入的地方,因为这种阻塞模式的套接字程序,收到FIN后它无法处理,换句话说对于客户端来说它在处理两个文件描述符一个是套接字的一个是标准输入的,要想解决这个问题我们就需要用到多路复用。

出处:https://www.cnblogs.com/yunxizhujing/p/9720966.html

相关文章

  • 通过线程编写一个简单的并发服务器

    概述 之前在上一节通过使用fork来实现了一个并发程序,它很经典但是效率不高主要是太消耗资源因为fork一个进程的...

  • golang分层测试之http压测脚本编写(2)

    前言 前一篇文已经简单讲解怎么通过goroutines的能力编写并发http压测脚本,但前文有提到过,主线程为了等...

  • 用C编写一个简单服务器

    前言 本文使用C语言编写一个简单服务器,旨在更好的理解服务端/客户端程序,迭代服务器,并发服务器等概念,仅供学习参...

  • java-threadpool

    线程池的好处 利用线程池管理多线程可以控制最大并发的问题,同时可以只能加服务器资源的利用率 线程池的简单使用 1,...

  • 线程池原理详解及如何用C语言实现线程池

    线程池是一种多线程处理形式,大多用于高并发服务器上,它能合理有效的利用高并发服务器上的线程资源;线程与进程用于处理...

  • golang的线程模型——GMP模型

    常见的线程模型 单线程服务器编程模型:Redis、Node.、JavaScript 多线程服务器编程模型:并发多个...

  • Puma: 为并发而生的Ruby Web服务器

    描述 Puma是一个简单,快速,线程式,并且支持HTTP1.1高并发的Ruby/Rack服务器。在开发环境和生产环...

  • Hystrix实现请求合并/请求缓存

    Hystrix请求合并用于应对服务器的高并发场景,通过合并请求,减少线程的创建和使用,降低服务器请求压力,提高在高...

  • PHP 协程实现

    多进程/线程 最早的服务器端程序都是通过多进程、多线程来解决并发IO的问题。进程模型出现的最早,从Unix 系统诞...

  • 是什么原因让并发编程如此困难?

    编写正确的程序难,编写正确的并发程序则是难上加难。既然这么难为什么还要并发,单线程执行不好吗?为了快呀,点个链接你...

网友评论

    本文标题:通过线程编写一个简单的并发服务器

    本文链接:https://www.haomeiwen.com/subject/lvewoftx.html