前情提要

    一:套接字  socket() 

      1:三次握手

   1:客户端像服务端链接,   (第一次握手)

   2:服务端收到请求,告诉客户端服务端收到了内容    (第二次握手1)

   3:服务端像客户端连接,(第二次握手2)                             这俩可以合在一起

   4:客户端收到服务端请求,并告诉客户端服务端已经收到了内容 (第三次握手)

  

TCP是因特网中的传输层协议,使用三次握手协议建立连接。当主动方发出SYN连接请求后,等待对方回答SYN+ACK[1],并最终对对方的 SYN 执行 ACK 确认。这种建立连接的方法可以防止产生错误的连接。[1]
TCP三次握手的过程如下:
客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。
三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。 tcp的三次握手

      2:四次挥手

    1:客户端告诉服务器说客户端要断开了,( 第一次挥手)

   2:服务端告诉客户端说服务端收到了断开信息(第二次挥手)  客户端和服务端通信关闭

   3:服务端像客户端通信,说服务端要断开了连接(第三次挥手)

   4:客户端收到通知后,告诉客户端已经收到了断开连接的信息. 服务端与客户端通信关闭

建立一个连接需要三次握手,而终止一个连接要经过四次握手,这是由TCP的半关闭(half-close)造成的。
(1) 某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。
(2) 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。
注意:FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
(3) 一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN。
(4) 接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。[1]
既然每个方向都需要一个FIN和一个ACK,因此通常需要4个分节。
注意:
(1) “通常”是指,某些情况下,步骤1的FIN随数据一起发送,另外,步骤2和步骤3发送的分节都出自执行被动关闭那一端,有可能被合并成一个分节。[2]
(2) 在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。
(3) 当一个Unix进程无论自愿地(调用exit或从main函数返回)还是非自愿地(收到一个终止本进程的信号)终止时,所有打开的描述符都被关闭,这也导致仍然打开的任何TCP连接上也发出一个FIN。
无论是客户还是服务器,任何一端都可以执行主动关闭。通常情况是,客户执行主动关闭,但是某些协议,例如,HTTP/1.0却由服务器执行主动关闭。[2] tcp的四次挥手

      3:基本模型

    二:简单的socket例子

服务端:

# sk =socket.socket()
# sk.bind(('127.0.0.1',8888)) #服务器建立ip 和端口
# sk.listen() # 创建监听
# conn,addr =sk.accept() #阻塞,直到有一个客户端来连接我,三次握手
# print(addr)
# while True:
# send_msg =input('msg: ')
# conn.send(send_msg.encode()) #转化成2进制
# msg =conn.recv(1024).decode() #最大接收1024,解码
# print(msg)
# conn.close()
# sk.close()
import socket
# sk =socket.socket() #实例化对象
# # sk.connect(('127.0.0.1',8888)) #选择要连接的服务器,端口
# # sk.send(b'12313123') #像服务器传输你要发送的东西
# # ret =sk.recv(1024) #设置接收,和发送的大小 字节
# # print(ret) #打印接收到的内容
# # sk.close() #关闭客户端链接

    三:带退出的socket例子

服务端:

# 带双方退出的版本
import time
sk =socket.socket()
sk.bind(('127.0.0.1',8887))
sk.listen() #建立监听
time1 =time.strftime("%Y-%m-%d %H:%M:%S")
while 1:
conn,addr =sk.accept() #建立阻塞
# conn.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)#实现端口的复用 while 1:
ret = conn.recv(1024).decode()
print(ret)
if ret == 'Q':
break
else:
send_msg = input('msg :>>>'+time1).encode()
# time1 =
# conn.send(time1)
conn.send(send_msg)
if send_msg == 'Q':
break
conn.close()
sk.close
客户端:

import time
time1 =time.strftime("%Y-%m-%d %H:%M:%S") sk =socket.socket()
sk.connect(("127.0.0.1",8887))
while 1 :
send_msg = input('msg:>>>'+str(time1)).encode() sk.send(send_msg)
if send_msg == 'Q'.encode():
break else:
ret = sk.recv(1024).decode()
print(ret)
if ret == 'Q':
break sk.close()

    四:带时间的socket例子

 服务端:
# import time
# sk = socket.socket()
# sk.bind(('127.0.0.1',9000))
# sk.listen()
# while True:
# conn,addr = sk.accept()
# fmt = conn.recv(1024)
# str_time = time.strftime(fmt.decode())
# conn.send(str_time.encode())
# conn.close()
# sk.close()
客户端:
# import socket
# sk = socket.socket()
#
# sk.connect(('127.0.0.1',9000))
#
# sk.send(b'%m/%d %H:%M:%S')
# msg = sk.recv(1024).decode()
# print(msg)
# sk.close()

    五:粘包

服务端
import struct
import socket sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen() conn,addr = sk.accept()
send_msg = input('>>>').encode()
bytes_len = struct.pack('i',len(send_msg))
conn.send(bytes_len)
conn.send(send_msg) # 粘包现象
conn.send(b'world')
conn.close()
sk.close() # 1.发送端的粘包 合包机制 + 缓存区
# 2.接收端的粘包 延迟接受 + 缓存区
# 3.流式传输
# 电流 高低电压
# 所以我们说 tcp协议是无边界的流式传输
# 4.拆包机制 # 粘包现象
# 接收端不知道发送端给我发送了多长的数据
import struct
import socket sk = socket.socket()
sk.connect(('127.0.0.1',9000))
bytes_len = sk.recv(4)
msg_len = struct.unpack('i',bytes_len)[0]
msg = sk.recv(msg_len)
print(msg.decode())
msg2 = sk.recv(5)
print(msg2)
sk.close()

    六: 如何解决粘包 sturck包

服务端:
# import socket
# import struct
# sk =socket.socket()
# sk.bind(('127.0.0.1',8888))
# sk.listen()
# conn,addr =sk.accept() #创建阻塞
# send_msg =input('>>>').encode()
# bete_len =struct.pack('i',len(send_msg)) #文件长度标志
# conn.send(bete_len)
# conn.send(send_msg)
# conn.close()
# sk.close()
客户端
# import socket
# sk =socket.socket()
# sk.connect(('127.0.0.1',8888))
# ret =sk.recv(1024).decode()
# print(ret)
# sk.close()
# import struct
# import socket
# sk =socket.socket()
# sk.connect(('127.0.0.1',8888))
# bete_len =sk.recv(4) #只是读取前4个值
# # print(bete_len)
# msg_len =struct.unpack('i',bete_len)[0]
# msg =sk.recv(msg_len)
# print(msg.decode())
# # msg2 =sk.recv(5)
# sk.close()

    七 :struct 包的使用

      struct.pack('i',len(bytes))  #i 是固定的 len() 里面放 存的内容

        他会返回4个字节 , 客户端将这4个字节读取就可以得到真实字符串长度

import struct

ret = struct.pack('i',560000)
print(ret,len(ret))
ret1 = struct.pack('i',123)
print(ret1,len(ret1))
ret2 = struct.pack('i',902730757)
print(ret2,len(ret2)) res = struct.unpack('i',ret)
print(res[0])
res = struct.unpack('i',ret1)
print(res[0])
res = struct.unpack('i',ret2)
print(res[0]) >>>>>>>>>>>
b'\x80\x8b\x08\x00' 4 字节
b'{\x00\x00\x00' 4 字节
b'\x05\x94\xce5' 4  字节
560000
123
902730757

使用struct解决黏包

借助struct模块,我们知道长度数字可以被转换成一个标准大小的4字节数字。因此可以利用这个特点来预先发送数据长度。

发送时 接收时
先发送struct转换好的数据长度4字节 先接受4个字节使用struct转换成数字来获取要接收的数据长度
再发送数据 再按照长度接收数据
import socket,struct,json
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080)) phone.listen(5) while True:
conn,addr=phone.accept()
while True:
cmd=conn.recv(1024)
if not cmd:break
print('cmd: %s' %cmd) res=subprocess.Popen(cmd.decode('utf-8'),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
err=res.stderr.read()
print(err)
if err:
back_msg=err
else:
back_msg=res.stdout.read() conn.send(struct.pack('i',len(back_msg))) #先发back_msg的长度
conn.sendall(back_msg) #在发真实的内容 conn.close() 服务端(自定制报头)
#_*_coding:utf-8_*_
import socket,time,struct s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
res=s.connect_ex(('127.0.0.1',8080)) while True:
msg=input('>>: ').strip()
if len(msg) == 0:continue
if msg == 'quit':break s.send(msg.encode('utf-8')) l=s.recv(4)
x=struct.unpack('i',l)[0]
print(type(x),x)
# print(struct.unpack('I',l))
r_s=0
data=b''
while r_s < x:
r_d=s.recv(1024)
data+=r_d
r_s+=len(r_d) # print(data.decode('utf-8'))
print(data.decode('gbk')) #windows默认gbk编码 客户端(自定制报头)

总结day24 ---- socket ,struct 的学习的更多相关文章

  1. struct ifreq学习和实例

    一.struct ifreq结构体 这个结构定义在/usr/include/net/if.h,用来配置和获取ip地址,掩码,MTU等接口信息的. /* Interface request struct ...

  2. [Java]Socket和ServerSocket学习笔记

    对于即时类应用或者即时类的游戏,HTTP协议很多时候无法满足于我们的需求.这会,Socket对于我们来说就非常实用了.下面是本次学习的笔记.主要分异常类型.交互原理.Socket.ServerSock ...

  3. Socket和ServerSocket学习笔记

    对于即时类应用或者即时类的游戏,HTTP协议很多时候无法满足于我们的需求.这会,Socket对于我们来说就非常实用了.下面是本次学习的笔记.主要分异常类型.交互原理.Socket.ServerSock ...

  4. VC SOCKET 压缩通信学习

    Server................// Server.cpp : Defines the entry point for the console application. // #inclu ...

  5. java中有关socket通信的学习笔记

    最近做的项目中使用到了一些基于java的socket长连接的一些功能,用来穿透有关行业的网闸.用到了也就学习了一下,下面是对学习内容的一个笔记,记录一下也希望有兴趣的同学可以参考一下,加深对javas ...

  6. Java Socket网络编程学习笔记(一)

    0.前言 其实大概半年前就已经看过网络编程Socket的知识了(传统IO),但是因为长时间的不使用导致忘的一干二净,最近正好准备校招,又重新看了网络编程这一章, 是传统IO(BIO)相关的内容,故在此 ...

  7. Socket通讯简易学习

    Socket打开通信通道,告诉本地机器,愿意在该通道上接受客户请求——监听,等待客户请求——接受请求,创建专用链接进行读写——处理完毕,关闭专用链接——关闭通信通道(当然其中监听到关闭专用链接可以重复 ...

  8. Linux socket编程应用学习笔记

    参考这个系列吧 http://www.cnblogs.com/wunaozai/tag/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/default.html?page=2 ...

  9. 快速学习C语言三: 开发环境, VIM配置, TCP基础,Linux开发基础,Socket开发基础

    上次学了一些C开发相关的工具,这次再配置一下VIM,让开发过程更爽一些. 另外再学一些linux下网络开发的基础,好多人学C也是为了做网络开发. 开发环境 首先得有个Linux环境,有时候家里机器是W ...

随机推荐

  1. IP地址工具类

    /// <summary> /// 获取客户端IP地址 /// </summary> /// <returns></returns> public st ...

  2. python动态捕获异常-乾颐堂

    在讨论动态捕获异常时让我大吃一惊的是,可以让我找到隐藏的Bug和乐趣... 有问题的代码 下面的代码来自一个产品中看起来是好的抽象代码 - slightly(!) .这是调用一些统计数据的函数,然后进 ...

  3. Web Api 中返回JSON的正确做法(转)

    出处:http://www.cnblogs.com/acles/archive/2013/06/21/3147667.html 在使用Web Api的时候,有时候只想返回JSON:实现这一功能有多种方 ...

  4. srping boot thymeleaf 学习总结 (2) - thymeleaf properties 国际化 mesaage

    thymeleaf获取配置properties中的数据与thymeleaf国际化(摘录) 使用thymeleaf提供的国际化 有时候会有直接在模板中获取配置文件properties中的配置信息,比如: ...

  5. Linux中逻辑卷(LVM)管理基本操作

    1.创建逻辑卷 原文:https://linux.cn/article-3965-1.html

  6. Oracle 11g PL/SQL Developer登入时候报ORA-12638: 身份证明检索失败的解决办法(安装了6遍,吐血之作)

    1.报这个错的时候会弹出一个对话框,先点击终止 2.然后汇报出这个是错误的窗口,然后点击确认,但是不要关这个安装窗口也不要其他不必要操作,窗口最小化 3.找到product文件夹,一般在app文件里 ...

  7. sublime填坑之旅: 格式代码, 缩进缩进

    前言:sublime是一款编程神器,轻巧又强大,适用于各种语言.这里介绍下如何快速缩进混乱代码,方便代码阅读. 原料:sublime text 3 1 混乱代码如下: 2  格式菜单选择: 英文: 菜 ...

  8. BZOJ 1503 郁闷的出纳员 (treap)

    1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 13370  Solved: 4808[Submit][Stat ...

  9. swift学习之-- UIAlertViewController -alert

    // //  ViewController.swift //  alertView // //  Created by su on 15/12/7. //  Copyright © 2015年 tia ...

  10. .NET基础 (17)反射

    反射1 请解释反射的基本原理和其实现的基石2 .NET提供了哪些类型来实现反射3 如何实现动态地发射程序集4 如何利用反射来实现工厂模式 反射1 请解释反射的基本原理和其实现的基石 反射是一种动态分析 ...