• Python中的socket网络编程(TCP/IP,UDP)讲解


    在网络编程中的一个基本组件就是套接字(socket)。套接字基本上是两个端点的程序之间的“信息通道”。程序可能分布在不同的计算机上,通过套接字互相发送信息。套接字包括两个:服务器套接字和客户机套接字。在创建一个服务器套接字后,让它等待连接。这样它就在某个网络地址处(ip地址和端口的组合)监听,直到有客户端套接字连接。连接完成后,二者就可以进行交互了。

    socket套接字
    一个套接字就是socket模块中socket类的一个实例。它的实例化需要3个参数:
    第1个参数是地址族,默认是socket.AF_INET(指定使用IPv4协议)
    第2个参数是流,默认是socket.SOCK_STREAM(指定面向流的TCP协议),或者数据报socket.SOCK_DGRAM套接字。
    第3个参数是使用的协议,默认是0

    import socket
    # s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # 这里我们全部使用默认值就行了
    s=socket.socket()

    服务器套接字使用bind方法后,再调用listen方法去监听某个特定的地址。

    s.bind(('127.0.0.1',9999))#绑定地址和端口
    s.listen(5)#服务器开始监听客户端连接,允许排队等待的连接数目是5,一般都设5

    端口是自己设的,ip我用的是本机的ip地址,当然如果服务器程序在其他主机上,就要换成其他的ip。端口号低于1024一般都被限制使用,它们被用于标准服务,例如端口80用于web服务。

    服务器套接字开始监听后,它就静静的等待客户端的连接了。这个步骤使用accept方法完成,这个方法会阻塞直到客户端的连接。该方法返回一个格式为(client,address)的元组,client是客户端套接字,address是格式为(host,port)的元组。服务器在处理完与该客户端的连接后,再次调用accept方法开始等待下个连接,通常这个过程用一个无限循环实现。

    while True:
        #接受一个客户端连接
        sock,addr=s.accept()#addr是个元组('127.0.0.1',端口)
        #接下来可以创建新线程来处理当前客户端的连接

    现在我们来看客户端怎么连接服务器,客户端套接字使用connect方法连接服务器,在connect方法中使用的地址必须和服务器在bind方法中绑定的地址相同。

    import socket
    c=socket.socket()#创建客户端套接字
    c.connect(('127.0.0.1',9999))#连接服务器

    套接字有两个方法:send和recv,分别用来发送和接收数据。send方法以字符串作为参数,recv用一个最大字节数来作为参数,如果不确定,使用1024比较好。

    c.send('hello,I am xxx!')
    c.recv(1024)

    基础内容介绍完后,我们现在来看个完整的例子,客户端连接服务器,客户端向服务器发送一组名字xxx,然后服务器回复hello,xxx!。

    #服务器
    import socket
    import threading
    #处理客户端请求
    def tcplink(sock,addr):
        print('Accept new connection from %s:%s...' % addr)
        sock.send('welcome!')
        while True:
            data=sock.recv(1024)
            if data=='exit' or not data:
                break;
            sock.send('hello %s' % data)
        sock.close()
        print('Connection from %s:%s closed' % addr)
    
    s=socket.socket()
    s.bind(('127.0.0.1',9999))#绑定地址和端口
    s.listen(5)
    print('serve is waiting connect.....')
    while True:
        sock,addr=s.accept()
        #创建新线程来处理每个客户端连接
        #target=tcplink代表新线程要运行哪个函数
        #args=(sock,addr)代表向这个方法传的参数
        t=threading.Thread(target=tcplink,args=(sock,addr))
        #启动这个线程
        t.start()
    #客户端
    import socket
    import threading
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #建立连接
    s.connect(('127.0.0.1',9999))
    data=s.recv(1024)
    print(data)
    for i in ['zhangkang','jack','tom']:
        s.send(i)
        data=s.recv(1024)
        print(data)
    s.send('exit')
    s.close()

    这里写图片描述

    TCP建立的是可靠的连接,而UDP是无连接,不可靠的。使用UDP的时候,只需要知道目标主机的ip和端口就行了,然后直接发送,能不能到达就不一定了。
    和TCP类型,服务器套接字需要先实例,然后绑定地址。

    #这里第二个参数选择数据报
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    # 绑定端口:
    s.bind(('127.0.0.1', 9999))

    但是服务器不需要调用listen()方法,而是直接接收客户端的数据

    while True:
        # 接收数据:
        data, addr = s.recvfrom(1024)
        print('Received from %s:%s' % addr)
        s.sendto('Hello, %s!' % data, addr)

    recvfrom()方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()就可以把数据用UDP发给客户端。

    客户端使用UDP时,首先仍然创建基于UDP的Socket,然后,不需要调用connect(),直接通过sendto()给服务器发数据:

    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    for data in ['zhangkang', 'jack', 'tom']:
        # 发送数据:
        s.sendto(data, ('127.0.0.1', 9999))
        # 接收数据:
        print s.recv(1024)
    s.close()

    从服务器接收数据仍然调用recv()方法。

    简单的聊天室程序

    这里我写了一个简易的类似聊天室的程序,没有使用图形界面,全部消息都显示在控制台。

    #服务器
    # -*- encoding: utf-8 -*-
    import socket
    import threading
    
    def tcplink(sock,addr):
        print('[%s:%s] is online...' % addr)
        while True:
            try:
                data=sock.recv(1024)
            except:
                socket_set.remove(sock)
                print('[%s:%s] is down!' % addr)
                break
            if data=='exit' or not data:
                socket_set.remove(sock)
                sock.close()
                print('[%s:%s] is down!' % addr)
                break
            else:
                list1=[]
                for i in socket_set:
                    if i!=sock:
                        list1.append(i)
                for i in list1:
                    i.send(data)
    
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    socket_set=set()#用来保存每个socket对象
    s.bind(('127.0.0.1',9999))#绑定地址和端口
    s.listen(5)
    print('serve is waiting connect.....')
    while True:
        #接受一个客户端连接
        sock,addr=s.accept()#addr是个元组('127.0.0.1',端口)
        socket_set.add(sock)#把socket对象添加到集合中
        #创建新线程来处理TCP连接
        t=threading.Thread(target=tcplink,args=(sock,addr))
        t.start()
    
    #客户端
    # -*- encoding: utf-8 -*-
    import socket
    import threading
    
    name=raw_input('input you name:')
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    #建立连接
    s.connect(('127.0.0.1',9999))
    def rec_message():
        while True:
            try:
                data=s.recv(1024)
                print('
    ['+data+']')
            except:
                break
    
    t=threading.Thread(target=rec_message)
    t.start()
    while True:
        st=raw_input()
        if st=='exit':
            s.send(st)
            s.close()
            break
        s.send(name+":"+st)
    

    这里写图片描述

    客户端逐个下线
    这里写图片描述

  • 相关阅读:
    前端一站式学习地址
    springboot注解开发
    java中的四种内部类使用(1)
    java内存回收机制
    TweenMax详解
    flash渲染机制
    通过字符串名访问变量
    总结调用Flash的几种方法
    flex buider2 的注册
    转:FlexChn.Cn Adobe Flex最佳学习路线
  • 原文地址:https://www.cnblogs.com/neuzk/p/9476427.html
Copyright © 2020-2023  润新知