基本Socket例子

  一、基本Socket例子
  
  Server端:
  
  # Echo server program
  
  import socket
  
  HOST = ''                 # Symbolic name meaning all available interfaces
  
  PORT = 50007              # Arbitrary non-privileged port
  
  sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  sock_server.bind((HOST, PORT))
  
  sock_server.listen(1) #开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
  
  conn, addr = sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
  
  with conn:
  
  print('Connected by', addr)
  
  while True:
  
  data = conn.recv(1024) #接收1024个字节
  
  if not data: break #收不到数据,就break
  
  conn.sendall(data) #把收到的数据再全部返回给客户端
  
  Client端:
  
  # Echo client program
  
  import socket
  
  HOST = 'localhost'    # The remote host
  
  PORT = 50007              # The same port as used by the server
  
  client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  client.connect((HOST, PORT))
  
  client.sendall(b'Hello, world')
  
  data = client.recv(1024)
  
  print('Received',data)
  
  先启动Server端,再启动Client端,结果如下:
  
  二、循环收发数据
  
  第一次接触就这么交待了,之说了一句话,感觉不够过瘾,如何实现更多的交互呢?简单,只需要让客户端不断的发,服务端不断的收就可以了,写个循环搞定。
  
  Server端:
  
  # Echo server program
  
  import socket
  
  HOST = ''                 # Symbolic name meaning all available interfaces
  
  PORT = 50007              # Arbitrary non-privileged port
  
  sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  sock_server.bind((HOST, PORT))
  
  sock_server.listen(1) #开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
  
  conn, addr = sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
  
  with conn:
  
  print('Connected by', addr)
  
  while True:
  
  data = conn.recv(1024) #接收1024个字节
  
  print("server recv:",conn.getpeername(), data.decode())
  
  if not data: break #收不到数据,就break
  
  conn.sendall(data) #把收到的数据再全部返回给客户端
  
  Client端:
  
  # Echo client program
  
  import socket
  
  HOST = 'localhost'    # The remote host
  
  PORT = 50007              # The same port as used by the server
  
  client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  client.connect((HOST, PORT))
  
  while True:
  
  msg = input(">>>:").strip()
  
  if len(msg) == 0:continue
  
  client.sendall(msg.encode()) #发送用户输入的数据,必须是bytes模式
  
  data = client.recv(1024)
  
  print('Received',data.decode()) #收到服务器的响应后,decode一下
  
  三、简单聊天软件
  
  上面的例子,服务端只是将客户端发来的再发送给客户端,这哪叫聊天啊,这种事需要双方配合,得让服务端也能说话。
  
  Server端:
  
  import socket
  
  HOST = ''                 # Symbolic name meaning all available interfaces
  
  PORT = 50007              # Arbitrary non-privileged port
  
  sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  sock_server.bind((HOST, PORT))
  
  sock_server.listen(1) #开始监听,1代表在允许有一个连接排队,更多的新连接连进来时就会被拒绝
  
  conn, addr www.jintianxuesha.com= sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
  
  with conn:
  
  print('Connected by', addr)
  
  while True:
  
  data = conn.recv(1024) #接收1024个字节
  
  print("recv from Alex:",conn.getpeername(), data.decode())
  
  if not data: break #收不到数据,就break
  
  response = input(">>>").strip()
  
  conn.send(response.encode())
  
  print("send to alex:",response)
  
  Client不需要做更改,直接看结果:
  
  以上的例子还是有bug,双方只能一来一往的说话,如果你想来纳许发2句话是不行的,会卡住。这是因为你发了一条消息后,就去调用recv方法接收服务器的响应了,再服务器端返回消息之前,这个recv(1024)方法是阻塞的,如果想允许此时还能再发消息给服务器端,就需要再单独启动一个线程,只负责发消息。
  
  四、聊天软件升级版
  
  刚才在聊天的时候,服务端在服务客户端的时候,其它人如果也想跟服务端连接是处于排队状态,然后等正在被服务的客户端完事并断开后,下一个人就跟上,但实际情况是客户端一断开,服务端也跟着断了。
  
  为什么会断呢?引文服务端以下代码的意思是,如果收不到数据,就跳出循环,就断开了。
  
  conn, addr = sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
  
  with conn:
  
  print('Connected by', addr)
  
  while True:
  
  data = conn.recv(1024) #接收1024个字节
  
  print("recv from Alex:",conn.getpeername(www.hongyangpt.cn), data.decode())
  
  if not data: break #收不到数据,就break , 就是它干的
  
  response = input(">>>").strip()
  
  conn.send(response.encode())
  
  print("send to alex:",response)
  
  想实现一个客户端断开后,可以立刻接入另外一个客户端的话,怎么办呢?只需要再在外层加个循环。
  
  while True: #最外层loop
  
  conn, addr = sock_server.accept() #阻塞直到有连接为止,有了一个新连接进来后,就会为这个请求生成一个连接对象
  
  #为何把上面这句话也包含在循环里?
  
  print("来了个新客人",conn.getpeername() )
  
  with conn:
  
  print('Connected by'www.hongyaoyul.cn, addr)
  
  while True:
  
  data = conn.recv(1024) #接收1024个字节
  
  print("recv from :",conn.getpeername(), data.decode())
  
  if not data: break #收不到数据,就break
  
  conn.send(data.upper())
  
  print("send to alex:",data)
  
  break 跳出后就回到大while那层:
  
  但是,有的人在重启服务端时可能会遇到:
  
  这是由于你的服务端仍然存在4次挥手的time_wait状态,在占用地址(如果不懂,请深入研究:1、tcp三次握手,四次挥手。2、sun洪水攻击。3、服务器高并发情况下会有大量的time_wait状态的优化方法)
  
  解决方法1:
  
  sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  
  sock_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #一行代码搞定,写在bind之前
  
  sock_server.bind((HOST,www.qiaoheibpt.com PORT))
  
  解决方法2(用于Linux系统):
  
  发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
  
  vi /etc/sysctl.conf
  
  编辑文件,加入以下内容:
  
  net.ipv4.tcp_syncookies = 1
  
  net.ipv4.tcp_tw_reuse = 1
  
  net.ipv4.tcp_tw_recycle = 1
  
  net.ipv4.tcp_fin_timeout = 30
  
  然后执行 /sbin/sysctl -p 让参数生效。
  
  net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;
  
  net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
  
  net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
  
  net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
  
  五、UDP实例
  
  UDP不需要经过3次握手和4次挥手,不需要提前建立连接,直接发数据就行。
  
  Server端:
  
  import socket
  
  ip_port=('127.0.0.1',9000)
  
  BUFSIZE=1024
  
  udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #udp类型
  
  udp_server_client.bind(ip_port)
  
  while True:
  
  msg,addr=udp_server_client.recvfrom(BUFSIZE)
  
  print("recv ",msg,addr)
  
  udp_server_client.sendto(msg.upper(),addr)
  
  Client端:
  
  import socket
  
  ip_port = ('127.0.0.1'www.luqintang.com,9000)
  
  BUFSIZE = 1024
  
  udp_server_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  
  while True:
  
  msg=input(www.hnyxysy.com'>>: ').strip(xingtuylgw.com)
  
  if not msg:continue
  
  udp_server_client.sendto(msg.encode('utf-8'),ip_port)
  
  back_msg,addr = udp_server_client.recvfrom(BUFSIZE)
  
  print(back_msg.decode('utf-8'),addr)
  
  结果:
  
  六、TCP  VS  UDP
  
  1、TCP基于链接通信
  
  基于链接,则需要listen(backlog),指定连接池的大小。
  
  基于链接,必须先运行服务端,然后再由客户端发起链接请求。
  
  对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋,recv将不会阻塞,接收到的是空(解决方法:服务端通信循环内加异常处理,捕捉到异常后就break通讯循环)
  
  对于windows/Linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋,recv将不会阻塞,收到的是空(解决方法:服务端通信循环内加异常处理,捕捉到异常后就break通讯循环)
  
  2、UDP无链接
  
  无链接,因而无需listen(backlog),更加没有什么连接池之说了。
  
  无链接,UDP的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲地发消息,只不过数据会丢失。
  
  recvfrom收的数据小于sendinto发送的数据时,在mac和Linux系统上数据直接丢失,在windows系统则会直接报错。
  
  只有sendinto发送数据没有recvfrom收数据,则数据丢失。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值