1.UDP与TCP通信模型区别

UDP通信:

  udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似于生活中,”写信”

Pythontcpip通信 tcp通信程序python_python


 

TCP通信:

  tcp通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中,”打电话”

Pythontcpip通信 tcp通信程序python_Pythontcpip通信_02

2.TCP服务器

在程序中,如果想要完成一个tcp服务器的功能,需要的流程如下:
 1. socket创建一个套接字
 2. bind绑定ip和port
 3. listen使套接字变为可以被动链接
 4. accept等待客户端的链接
 5. recv/send接收发送数据

示例:

from socket import *

# 创建socket
tcpSerSocket = socket(AF_INET, SOCK_STREAM)

# 绑定本地信息
address = ('', 7788)
tcpSerSocket.bind(address)

# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcpSerSocket.listen(5)

# 如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务器
# newSocket用来为这个客户端服务
# tcpSerSocket就可以省下来专门等待其他新客户端的链接
newSocket, clientAddr = tcpSerSocket.accept()

# 接收对方发送过来的数据,最大接收1024个字节
recvData = newSocket.recv(1024)
print ('接收到的数据为:',recvData)

# 发送一些数据到客户端
newSocket.send("thank you !")

# 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,只能再次重新连接
newSocket.close()

# 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
tcpSerSocket.close()

3. TCP客户端

所谓的服务器端:就是提供服务的一方;
而客户端,就是需要被服务的一方

示例:

from socket import *

# 创建socket
tcpClientSocket = socket(AF_INET, SOCK_STREAM)

# 链接服务器
serAddr = ('192.168.1.102', 7788)
tcpClientSocket.connect(serAddr)

# 提示用户输入数据
sendData = input("请输入要发送的数据:")

tcpClientSocket.send(sendData)

# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcpClientSocket.recv(1024)
print ('接收到的数据为:',recvData

# 关闭套接字
tcpClientSocket.close()

4. 示例:TCP服务端接收与发送

import socket
import time

'''
serverSocket是用来接收新的客户端的
以后与这个连接的客户端的收发消息就不能用serverSocket了,
而是用返回来的新的newSocket


'''

serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serverSocket.bind(('', 7788))
serverSocket.listen(10)
newSocket, clientAddr = serverSocket.accept()

#发
sendData = input('>>')
newSocket.send(sendData.encode('gbk'))
#收
recvData = newSocket.recv(1024)
print(recvData.decode('gbk'))



newSocket.close()
serverSocket.close()

5.示例:TCP客户端接收与发送

import socket
import time

clientSocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientSocket.connect(('192.168.11.69',8899))

while True:

    sendMsg=input('请输入需要发送的信息:')
    clientSocket.send(sendMsg.encode('gbk'))

    recvMsg=clientSocket.recv(1024)
    print('请接收信息:%s'%recvMsg.decode('gbk'))

clientSocket.close()

6.示例:TCP服务端多线程连接多个客户端

import socket
import time
import threading


def socketState(newSocket,clientAddr):
    while True:
        recvData = newSocket.recv(1024)
        recvData = recvData.decode('gbk')
        if recvData == '':
            print('客户端%s退出了...'%clientAddr[0])
            newSocket.close()
            break
        else:
            print('来自于%s:%s的消息(%s):%s'%(clientAddr[0],clientAddr[1],time.strftime('%Y-%m-%d %H:%M:%S'),recvData))
            sendData = 'echo:%s'%recvData
            newSocket.send(sendData.encode('gbk'))


def main():
    #创建服务端socket对象
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSocket.bind(('', 7788))
    serverSocket.listen(10)
    #循环,等待多个客户端连接
    while True:
        #等待客户端的连接,阻塞。连接后,继续运行
        newSocket, clientAddr = serverSocket.accept()
        #创建新的线程,执行与新客户端的交互
        serverThread = threading.Thread(target=socketState, args=(newSocket,clientAddr))
        serverThread.start()
        #newSocket.close()


if __name__ == '__main__':
    main()

7. 示例:TCP服务端多进程连接多个客户端

import socket
import time
import multiprocessing

def socketState(newSocket,clientAddr):
    print(id(newSocket))

    while True:
        recvData = newSocket.recv(1024)
        recvData = recvData.decode('gbk')
        if recvData == '':
            print('客户端%s退出了...'%clientAddr[0])
            newSocket.close()
            break
        else:
            print('来自于%s:%s的消息(%s):%s'%(clientAddr[0],clientAddr[1],time.strftime('%Y-%m-%d %H:%M:%S'),recvData))
            sendData = 'echo:%s'%recvData
            newSocket.send(sendData.encode('gbk'))

def main():
    #创建服务端socket对象
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSocket.bind(('', 7788))
    serverSocket.listen(10)
    #循环,等待多个客户端连接
    while True:
        #等待客户端的连接,阻塞。连接后,继续运行
        newSocket, clientAddr = serverSocket.accept()

        print(id(newSocket))

        #创建新的进程,执行与新客户端的交互
        serverProcess = multiprocessing.Process(target=socketState, args=(newSocket,clientAddr))
        serverProcess.start()
        '''
            这里要关闭。
            子进程会单独分配与父进程相同的内容,地址不同(深拷贝)
       '''
        newSocket.close()


if __name__ == '__main__':
    main()

8. TCP三次握手

Pythontcpip通信 tcp通信程序python_socket_03

9. TCP四次挥手

Pythontcpip通信 tcp通信程序python_网络编程_04

10. TCP的十个状态

Pythontcpip通信 tcp通信程序python_服务器_05


注意:

 • 当一端收到一个FIN,内核让read返回0来通知应用层另一端已经终止了向本端的数据传送

 • 发送FIN通常是应用层对socket进行关闭的结果

11. TCP 长连接与短连接

TCP在真正的读写操作之前,server与client之间必须建立一个连接,
当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,
连接的建立通过三次握手,释放则需要四次握手,
所以说每个连接的建立都是需要资源消耗和时间消耗的。

TCP通信过程:

Pythontcpip通信 tcp通信程序python_服务器_06

11.1 TCP短连接

模拟一种TCP短连接的情况:
 1. client 向 server 发起连接请求
 2. server 接到请求,双方建立连接
 3. client 向 server 发送消息
 4. server 回应 client
 5. 一次读写完成,此时双方任何一个都可以发起 close 操作

在第 步骤5中,一般都是 client 先发起 close 操作。当然也不排除有特殊的情况。
从上面的描述看,短连接一般只会在 client/server 间传递一次读写操作

短连接操作过程:

建立连接——数据传输——关闭连接…建立连接——数据传输——关闭连接

Pythontcpip通信 tcp通信程序python_网络编程_07

11.2 TCP长连接

再模拟一种长连接的情况:
 1. client 向 server 发起连接
 2. server 接到请求,双方建立连接
 3. client 向 server 发送消息
 4. server 回应 client
 5. 一次读写完成,连接不关闭
 6. 后续读写操作…
 7. 长时间操作之后client发起关闭请求

长连接操作过程:

建立连接——数据传输…(保持连接)…数据传输——关闭连接

Pythontcpip通信 tcp通信程序python_python_08

11.3 两者优缺点对比

• 长连接可以省去较多的TCP建立和关闭的操作,减少浪费,节约时间。
对于频繁请求资源的客户来说,较适用长连接。

• client与server之间的连接如果一直不关闭的话,会存在一个问题,

随着客户端连接越来越多,server早晚有扛不住的时候,这时候server端需要采取一些策略,

如关闭一些长时间没有读写事件发生的连接,这样可以避免一些恶意连接导致server端服务受损;

如果条件再允许就可以以客户端机器为颗粒度,限制每个客户端的最大长连接数,
这样可以完全避免某个蛋疼的客户端连累后端服务。

• 短连接对于服务器来说管理较为简单,存在的连接都是有用的连接,不需要额外的控制手段。

• 但如果客户请求频繁,将在TCP的建立和关闭操作上浪费时间和带宽。

11.4 应用

• 长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况。

每个TCP连接都需要三次握手,这需要时间,如果每个操作都是先连接,

再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,

再次处理时直接发送数据包就OK了,不用建立TCP连接。
例如:数据库的连接用长连接,如果用短连接频繁的通信会造成socket错误,

而且频繁的socket 创建也是对资源的浪费。

• 而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,

而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,

如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,

那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。