python学习21

网络编程-socket编程

 1.什么是c/s架构?

 顾名思义,就是客户端端/服务器架构。不同的人可能回答不一,但是有一点是相同的:服务器是一个软件或硬件,用于向一个或多个客户端提供所需要的服务,服务器存在的唯一目的就是等待客户的请求,给这些客户服务,然后等待其他的请求

2.客户端与服务端如何通信?

其实说白了就是互联网中两个主机该如何通信,首先我们用ip地址可以标示一台主机,这样就可以通信了么?当然也不行,我们还得标示主机中的进程,当然协议也是不可或缺的,是udp报文协议还是tcp数据流协议,这的看你个人的需求。用套介子来作为一个标示符,唯一标识网络进程。

下面图示:


服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen)调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

 服务端:

          采用socket()函数 定义socket描述字

          bind()函数来把地址族中一个特定地址赋给socket,简单来说就是把一个ipv4/ipv6地址+端口号组合赋给socket

          listen()函数把socket变成被动类型的,等待客户连接请求。

          当客户端发来消息时用accept()函数来接受请求,同时给服务端返回客户端的socket描述字,接下来服务端以接收到的描述字来向客户端发送信息以及接受信息。

下面我们模拟发送消息和接收消息的过程:

服务器:

#usr/bin/python
#-*-coding:utf-8-*-
import socket
#1先创建一个服务端
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP和端口
server.bind(('127.0.0.1.',8009))
#设置监听
server.listen(5)
print('服务端启动')
#等待客户端链接 如果有链接 将返回一个链接对象和对方的地址
conn,address=server.accept()
#接受客户端发过来 多大子节点的数据
msg=conn.recv(1024)
#给客户端回复数据
print('客户端发来:',msg)   
conn.send(msg.upper()) #发消息
#结束了,连接关闭,服务关闭
conn.close()
server.close()

客户端:

#usr/bin/python
#-*-coding:utf-8-*-

import socket
#创建客户端
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.connect(('192.168.0.143',8011))
print('客户端成功连接服务器 n结束')
server.send('hello'.encode('utf-8'))#发消息
data=server.recv(1024)  
print('服务器: ',data)

server.close()

上述结果服务端会打印:客户端发来:b'HELLO'。

上述C/S构架只能发送一条消息,且不能发送中文。我们稍作更改,使其可以聊天且可以发送中文:

服务端:

#usr/bin/python
#-*-coding:utf-8-*-
import socket
#1先创建一个服务端
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP和端口
server.bind(('127.0.0.1.',8022))
#设置监听
server.listen(5)
print('服务端启动')
#等待客户端链接 如果有链接 将返回一个链接对象和对方的地址
conn,address=server.accept()
#接受客户端发过来 多大子节点的数据
while True:
    msg=conn.recv(1024)
    #给客户端回复数据
    print('客户端发来:',msg.decode())   #decode解码  encode编码
    conn.sendall(input('服务端:').encode('utf-8'))
#结束了,连接关闭,服务关闭
conn.close()
server.close() 

注:

socket.send(string[, flags])  发送TCP数据,返回发送的字节大小。这个字节长度可能少于实际要发送的数据的长度。换句话说,这个函数执行一次,并不一定能发送完给定的数据,可能需要重复多次才能发送完成。

socket.sendall(string[, flags])   看懂了上面那个,这个函数就容易明白了。发送完整的TCP数据,成功返回None,失败抛出异常

在接受客户端发来的数据后做了一个while无限循环,先将接受到的信息做解码decode,然后发送信息并做转码encode,使可以书写中文。

客户端:

#usr/bin/python
#-*-coding:utf-8-*-

import socket
#创建客户端
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.connect(('192.168.0.143',8022))
print('客户端成功连接服务器 n结束')
while True:
    data=input('客户端:')
    if data.__eq__('n'):break
    server.send(data.encode('utf-8'))
    data=server.recv(1024)
    print('服务器: ',data.decode())

server.close()

与服务端一样,做一个while无循环,停止条件是输入n,将输入的信息转码发送过去,将接受到的信息解码输出。

我们已经粗略了解了C/S结构,下面我们做一个可以多人本地连接的C/S结构。

服务端:

#usr/bin/python
#-*-coding:utf-8-*-
#群聊客户端
import socket,threading
#创建一个服务端
server=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#绑定IP和端口
server.bind(('127.0.0.1',8025))
#设置最大挂起连接数5
server.listen(5)
print('服务端 启动,等待客户端连接...')
#中间转换数据的变量
message=''
#创建一把锁
lock=threading.Lock()
cond=threading.Condition(lock=lock)
#不停的收
def recv_msg(conn,address):
    while True:
        msg=conn.recv(1024)
        global message
        cond.acquire()
        message=str(address)+':'+msg.decode()
        print('收到:'+message)
        cond.notify_all() #唤醒其他发消息的线程
        cond.release()
#不停的发
def send_msg(conn,address):
    while True:
        cond.acquire()
        cond.wait()
        cond.release()
        conn.sendall(message.encode('utf-8'))
        print('发送'+message)

while True:
    conn,address=server.accept()
    threading._start_new_thread(recv_msg,(conn,address))
    threading._start_new_thread(send_msg,(conn,address))

这个服务器与上面的区别在于,它最后执行了一个while无限循环,一直执行了收发两个线程,同时两个线程之间有锁,通过不停的等待和唤醒交替进行。我们定义了两个函数,一个用来接收消息,一个用来发送消息。

接收消息:首先用msg存储接收到的数据,然后引入全局变量message,message的值为接收到信息的地址加上msg的解码,然后将信息打印,同时唤醒发送消息的锁,对应的在唤醒锁前后写上加锁与解锁。

发送消息:一开始如果服务端没有接到到信息,发送消息线程不会启动,所以我们设置一个锁,使其等待,同时在前后写上加锁与解锁。在唤醒之后,将接受到的信息编码并发送给所有客户端,同时打印message。

客户端:

#usr/bin/python
#-*-coding:utf-8-*-
import socket,threading
#创建客户端
client=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#建立连接
client.connect(('127.0.0.1',8026))
#收取其他用户的数据
def recv_msg():
    while True:
        msg=client.recv(1024)
        print('收到:',msg.decode())

threading._start_new_thread(recv_msg,())

#不停的发
while True:
    msg=input('请数据消息:')
    client.send(msg.encode('utf-8'))
在客户端中,我们需要定义一个线程用于接受其他用户的消息即recv_msg,就是将接受到的信息解码打印,然后创建一个while循环,用于不停的发送消息,即将我们input的值编码发送到服务器,然后由服务器发送给连接到的所有客户端。

以上服务端与客户端的IP与端口要保持一致!!!!!!!!


猜你喜欢

转载自blog.csdn.net/gy_334/article/details/80545017
今日推荐