Python中的socket(TCP,UDP)

  • baagee 发布于 2017-07-31 23:13:14
  • 分类:Python
  • 2689 人围观
  • 2 人喜欢

1,SOCKET的使用

网络中各个机器上进程之间如何通信,首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!

在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。

其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。

这样利用ip地址,协议,端口就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互

什么是socket

socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:

它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的

例如我们每天浏览网页、QQ 聊天、收发 email 等等

2,UDP

UDP --- 用户数据报协议,UDP是一种面向无连接的协议,每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

不需要建立相关的链接,只需要发送数据即可,类似于生活中,"写信"

udp通信的流程图

UDP特点:

UDP是面向无连接的通讯协议,UDP数据包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。 UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。 UDP是一个不可靠的协议,发送方所发送的数据报并不一定以相同的次序到达接收方。

【适用情况】

UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如

语音广播,视频,QQ,TFTP(简单文件传送),SNMP(简单网络管理协议),RIP(路由信息协议,如报告股票市场,航空信息),DNS(域名解释)

注重速度流畅

UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。

2.1,创建一个udp socket(udp套接字)发送接收数据

代码如下:

#coding=utf-8

from socket import *

#1. 创建套接字
udpSocket = socket(AF_INET, SOCK_DGRAM)

#2. 准备接收方的地址
sendAddr = ('192.168.137.1', 8080)

#3. 从键盘获取数据
sendData = "要发送的数据".encode('gbk')

#4. 发送数据到指定的电脑上
udpSocket.sendto(sendData, sendAddr)
print('send over')

#5. 等待接收对方发送的数据
recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数

#6. 显示对方发送的数据
print(recvData)

#7. 关闭套接字
udpSocket.close()

每重新运行一次上述程序,端口都会变,如果没有确定到底用哪个,系统默认会随机分配。

记住一点:这个网络程序在运行的过程中,这个就唯一标识这个程序,所以如果其他电脑上的网络程序如果想要向此程序发送数据,那么就需要向这个数字(即端口)标识的程序发送即可。

一般服务性的程序,往往需要一个固定的端口号,这就是所谓的端口绑定

#coding=utf-8

from socket import *

#1. 创建套接字
udpSocket = socket(AF_INET, SOCK_DGRAM)

#2. 绑定本地的相关信息,如果一个网络程序不绑定,则系统会随机分配
bindAddr = ('', 7788) # ip地址和端口号,ip一般不用写,表示本机的任何一个ip
udpSocket.bind(bindAddr)

#3. 等待接收对方发送的数据
recvData = udpSocket.recvfrom(1024) # 1024表示本次接收的最大字节数

#4. 显示接收到的数据
print(recvData)

#5. 关闭套接字
udpSocket.close()

2.2,UDP中的广播(只有UDP有广播)

#coding=utf-8

import socket, sys

dest = ('<broadcast>', 7788)

# 创建udp套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 对这个需要发送广播数据的套接字进行修改设置,否则不能发送广播数据
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST,1)

# 以广播的形式发送数据到本网络的所有电脑中
s.sendto("Hi".encode('utf-8'), dest)

print("等待对方回复(按ctrl+c退出)")

while True:
    (buf, address) = s.recvfrom(2048)
    print("Received from %s: %s" % (address, buf))

2.3:UDP完成多线程的聊天工具

#coding=utf-8

from socket import *
from threading import Thread

udpSocket = None
toIP = ''

def sendMsg(msg):
    sendData = msg.encode('utf-8')
    sendAddr=(toIP,8081)#her'192.168.137.1'
    udpSocket.sendto(sendData, sendAddr)

def send():
    while True:
        msg=input('输入你要发送的消息("q"为退出):\r\n')
        if msg=='q':
            msg='对方以下线...'
            sendMsg(msg)
            break
        sendMsg(msg)

def recive():
    while True:
        msg=udpSocket.recvfrom(1024)
        print('来自 '+msg[1][0]+':'+str(msg[1][1])+'的消息: '+msg[0].decode('utf-8')+'\r\n')
        if msg[0].decode('utf-8')=='对方以下线...':
            sendMsg('对方以下线...')
            break

def main():
    global udpSocket
    global toIP
    udpSocket = socket(AF_INET, SOCK_DGRAM)
    toIP=input('請輸入對方IP:')
    bindAddr=('',7788)#my
    udpSocket.bind(bindAddr)

    sendT=Thread(target=send)
    reciveT=Thread(target=recive)
    sendT.start()
    reciveT.start()

    sendT.join()
    reciveT.join()

    udpSocket.close()
    print('over')

if __name__ == '__main__':
    main()

2.4,udp服务器、客户端

udp的服务器和客户端的区分:往往是通过请求服务和提供服务来进行区分

请求服务的一方称为:客户端

提供服务的一方称为:服务器

2.5,udp绑定问题

一般情况下,服务器端,需要绑定端口,目的是为了让其他的客户端能够正确发送到此进程

客户端,一般不需要绑定,而是让操作系统随机分配,这样就不会因为需要绑定的端口被占用而导致程序无法运行的情况

3,创建一个tcp socket(tcp套接字)

tcp方式的通信流程图

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

3.1,tcp服务器

如果想要完成一个tcp服务器的功能,需要的流程如下:

socket创建一个套接字

bind绑定ip和port

listen使套接字变为可以被动链接

accept等待客户端的链接

recv/send接收发送数据

一个很简单的tcp服务器如下:

#coding=utf-8
from socket import *

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

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

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

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

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

# 发送一些数据到客户端
newSocket.send("发送一些数据到客户端".encode('utf-8'))

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

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

3.2,tcp客户端

from socket import *
# 创建socket
clientSocket=socket(AF_INET,SOCK_STREAM)

# 链接服务器
clientSocket.connect(('192.168.117.133',7788))

#send
clientSocket.send('sdfds'.encode('utf-8'))

# 接收对方发送过来的数据,最大接收1024个字节
recvData=clientSocket.recv(1024)
print(recvData.decode('utf-8'))

clientSocket.close()

3.3,基于TCP的多线程聊天程序

客户端:

#coding=utf-8
from socket import *

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

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

while True:

    # 提示用户输入数据
    sendData = input("send:")

    if len(sendData)>0:
        if sendData=='q':
            break
        tcpClientSocket.send(sendData.encode('utf-8'))
    else:
        break

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

# 关闭套接字
tcpClientSocket.close()
print('已关闭')

服务器端:

#coding=utf-8
from socket import *
from threading import Thread

# 创建socket
tcpSerSocket = socket(AF_INET, SOCK_STREAM)
tcpSerSocket.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 绑定本地信息
address = ('', 7788)
try:
    tcpSerSocket.bind(address)
except Exception as e:
    print(e)
    exit()
# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcpSerSocket.listen(5)

def clientDetail(cSocket,clientAddr):
    while True:
        # 接收对方发送过来的数据,最大接收1024个字节
        recvData = cSocket.recv(1024)

        # 如果接收的数据的长度为0,则意味着客户端关闭了链接
        if len(recvData)>0:
            print('\rrecv('+str(clientAddr)+'):'+recvData.decode('utf-8'))
        else:
            break
        # 发送一些数据到客户端
        sendData = input("send"+str(clientAddr)+":")
        cSocket.send(sendData.encode('utf-8'))
    cSocket.close()
    print(str(clientAddr)+'对方已离线...')

try:
    while True:
        # 如果有新的客户端来链接服务器,那么就产生一个信心的套接字专门为这个客户端服务器
        # newSocket用来为这个客户端服务
        # tcpSerSocket就可以省下来专门等待其他新客户端的链接
        print('master thread等待中...')
        newSocket, clientAddr = tcpSerSocket.accept()
        print('来了新的人('+str(clientAddr)+')...')
        t=Thread(target=clientDetail,args=(newSocket,clientAddr))
        t.start()
        print(t.name)
except Exception as e:
    print('over')
finally:
    # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收任何新的客户端的连接
    tcpSerSocket.close()


标签: socket tcp udp

评论

点击图片切换
还没有评论,快来抢沙发吧!