Socket编程和实现聊天室

一、HTTP协议的socket通信

1、server.py

# 服务端
import sys
import socket

ip_point = ('127.0.0.1',9999)
sk = socket.socket()
sk.bind(ip_point)
sk.listen(5)
print("进入监听状态...")
conn,addr = sk.accept()
print(addr)
client_data = conn.recv(1024)
print(client_data.decode("utf-8"))
conn.send("服务端回复内容:我很好。".encode("utf-8"))
conn.close()

2、client.py

# 客户端
import socket
ip_port = ("127.0.0.1",9999)
sk = socket.socket()
sk.connect(ip_port)
sk.send("客户端发送数据:你好吗?".encode("utf-8"))
server_replay = sk.recv(1024)
print("get data:",server_replay.decode("utf-8"))
sk.close()

3、执行结果

 4、流程说明

  1. 建立socket对象
  2. socket绑定ip和端口
  3. listen
  4. 有连接,accept
  5. 读写数据
  6. 关闭

二、UDP协议的socket通信

1、server.py

# 服务端
from socket import *
from time import ctime

HOST = ''
PORT = 1200
BUFSIZ = 128
ADDR = (HOST, PORT)

# 创建一个服务器端UDP套接字
udpServer = socket(AF_INET, SOCK_DGRAM)
# 绑定服务器套接字
udpServer.bind(ADDR)
print('已经进入监听状态...')
# 接收来自客户端的数据
data, addr = udpServer.recvfrom(BUFSIZ)
print(u"得到客户端数据:",data.decode("utf-8"))
# 向客户端发送数据
udpServer.sendto(b'%s %s[%s]' % ("服务器发送消息:".encode("utf-8"),ctime().encode("utf-8"),data),addr)
print('向客户端发送数据:', data)
udpServer.close()

2、client.py

# 客户端
#encoding=utf-8
from socket import *

HOST = 'localhost'
PORT = 1200
BUFSIZ = 128
ADDR = (HOST, PORT)

# 创建客户端UDP套接字
udpClient = socket(AF_INET, SOCK_DGRAM)
data = input('>')
# 向服务器端发送数据
udpClient.sendto(data.encode("utf-8"), ADDR)
# 接收来自服务器端的数据
data, ADDR = udpClient.recvfrom(BUFSIZ)
print(data.decode("utf-8"))
udpClient.close()

3、结果

三、实现一个调用系统命令的例子

1、server.py

import os
if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.bind(('localhost', 8001))
    sock.listen(5)
    while True:
        connection,address = sock.accept()
        try:
            connection.settimeout(5)
            command = connection.recv(1024)
            print(command)
            result=os.popen(command.decode("utf-8")) #拿到执行后管道命令后的结果
            connection.send(command)
            connection.send(result.read().encode("utf-8"))
        except socket.timeout:
            print('time out')
        connection.close()

2、client.py

if __name__ == '__main__':
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(('localhost', 8001))
    import time
    time.sleep(2)
    sock.send('ipconfig'.encode("utf-8"))
    print("command:",sock.recv(10).decode("utf-8"))
    print ("command result:",sock.recv(1024).decode("utf-8"))
    sock.close()

3、执行结果

客户端向服务端发送一个dos命令
服务服务端返回命令响应结果

四、实现聊天室

1、实现的效果:

2、服务器端代码

# encoding=utf-8
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver  # 事件处理器
from twisted.internet import reactor

class Chat(LineReceiver):#本质就是一个Protocol类的子类,真正管理server端和每个客户端通讯逻辑的,每一个客户端的连接,都会自动生成一个Chat类的实例。
    message_dict={}      #类变量,存储所有用户发送的消息,key:收消息的用户名,value:列表存所有发送来的消息
    def __init__(self, users):
        self.users = users   #把ChatFactory类中的实例变量user字典,用self.users 变量引用这个字典
        self.name = None     #当前连接的用户名,你起的名字会存到这个变量里面
        self.state = "GETNAME" #状态位,确认当前连接的客户端起名名字没有,没起名状态是GETNAME,起了名状态是CHAT

    def connectionMade(self):  # 连接开始,且开始做处理
        if self.name is None:  #判断客户端有没有名字
            self.sendLine(u"你叫什么名字?".encode("utf-8"))  #给客户端发送指定消息!

    def connectionLost(self, reason): #连接断开的时候做什么处理
        print("断开时的用户名:",self.name)
        if self.name in self.users:
            del self.users[self.name]  #user字典是一个全局变量,value存的是连接对象本身(chat的实例),已经断开不是连接状态了,删除掉。
            try:
                if self.name in Chat.message_dict: #把别人给你发的消息,从消息字典中删除掉
                    del Chat.message_dict[self.name]
            except:
                print("删除用户的聊天记录失败")


    def lineReceived(self, line):  # 收到客户端发来的消息做什么处理,会自动调用
                                   #客户端发送的消息,会自动传到line这个变量里面    
        if self.state == "GETNAME":  # 根据状态开始选择不同得内容处理
            print("line:",line)
            self.handle_GETNAME(line.decode("utf-8")) #调用起名字的处理方法handle_GETNAME
        else:
            self.handle_CHAT(line.decode("utf-8")) #调用聊天的处理方法handle_CHAT

    def handle_GETNAME(self, name): #处理用户起名字的逻辑
        if name in self.users:
            self.sendLine(u"名字冲突了,请做另外起个用户名.
".encode("utf-8") )  # 每个用户只能有一个聊天通信存在
            return
        self.sendLine(("欢迎, %s!
"  %name).encode("utf-8")) #给客户端发送一个消息,告诉你欢迎xxx
        self.name = name #用户发来的名字,赋值给self.name变量,连接的客户端的名字
        self.users[name] = self #把self,自己,放到users字典中(存所有用户的连接对象---Chat的实例--self)
        self.state = "CHAT"  #把用户从GETNAME状态改为CHAT状态


    def handle_CHAT(self, message):  # 处理用户聊天的逻辑
            #客户端输入:zhangsan:你吃了吗?
            if "getmessage" in message:  #发的命令是getmessage,读别人给你发的消息,取消息的用户名:getmessage
                username = message.split(":")[0]#取到需要取消息的用户名
                print("*"*2,username,Chat.message_dict)
                #判断取消息的用户名在不在消息字典中,或者是不是消息为空
                if (username  not in Chat.message_dict) or  Chat.message_dict[username] == []:
                    self.users[username].sendLine("没有别人给你发送的数据".encode("utf-8"))
                    print(message,"---->","没有别人给你发送的数据","
")
                    return
                message_list=Chat.message_dict[username]#把消息的列表取出来
                print(message_list)
                if username in self.users:#把所有的链接对象遍历一遍
                    print(username,self.users[username])
                    #self.users[username]--》username对应的链接对象取出来,然后把列表以字符串的形式发给客户端
                    #客户端,需要用eval(列表的字符串)---》转换为一个列表
                    #用这个连接对象发给用户这个列表字符串
                    self.users[username].sendLine(("%s" % message_list).encode("utf-8"))
                    del Chat.message_dict[username]#删除刚才收走的消息
                return

            elif ":" in message:    
                '''
          消息格式:
huqiqi:zhangsan:你吃了么? ''' if message.count(":")!=2: self.sendLine("您发送的消息格式不对,请输入send命令后,按照格式‘用户名:消息’来进行聊天信息发送。".encode("utf8")) print("您发送的消息格式不对,请输入send命令后,按照格式‘用户名:消息’来进行聊天信息发送。") return from_username = message.split(":")[0] to_username = message.split(":")[1] chat_message = message.split(":")[2] if to_username not in Chat.message_dict:#判断接受者是否在消息字典中,不在,声明一个空列表作为value Chat.message_dict[to_username] = [] Chat.message_dict[to_username].append((from_username,chat_message))#向列表中追加这个消息:(发消息人名字,消息体) print(message, "---->", "增加了用户%s发送的消息:%s" %(to_username,(from_username,chat_message)) , " ") return #查看哪些用户处于登录状态 elif message.strip() =="list": #查看哪些用户是处于登录状态 print("list response") self.sendLine((str([username for username in self.users]) + " ").encode("utf-8")) print(message, "---->", (str([username for username in self.users]) + " ")," ") return #如果没有识别用户命令,提示用户可以输入使用的命令 else: #非上面的消息,就返回命令提示语 send_message= ("""请指定用户名,输入send后,按照格式‘用户名:消息’来进行聊天信息发送。 或者输入list查看当前登录用户 输入getmessage获取其他用户发给你的聊天信息 """) #print (type(send_message)) self.sendLine(send_message.encode("utf-8")) print(message, "---->",send_message," ") return class ChatFactory(Factory): #实现的工厂类,必须定义buildProtocol方法,必须返回一个Protocol子类的实例对象 def __init__(self): self.users = {} #将所有与服务器端连接的对象存放到此字典中,所有的实例均可以使用此字典获取所有的连接对象,字典来存储所有的连接,key:和server连接的客户端名字(第一次连接的时候取的);value:连接对象也就是Chat的实例 def buildProtocol(self, addr): return Chat(self.users) #每次一个新的客户端建立了连接,那么会自动生成Chat类的实例,这个实例负责管理服务端和客户端的所有通讯细节,会根据网络事件,触发不同内置的事件方法! if __name__ == '__main__': reactor.listenTCP(1200, ChatFactory()) #监听端口,指定通信协议的工厂类实例 print ("开始进入监听状态...") reactor.run() #开始监听

3、客户端代码

# socket client end
from socket import *
import time
s = socket(AF_INET, SOCK_STREAM)
remote_host = gethostname()print ('remote_host:', remote_host)
port = 1200
s.connect((remote_host, port))     # 发起连接
print (u"连接从", s.getsockname())  # 返回套接字自己的地址。通常是一个元组(ipaddr,port)
print (u"连接到", s.getpeername())  # 返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)

print (u'从服务器返回消息:')
print (s.recv(1200).decode("utf-8").strip())

while 1:
    username = input("请输入你要使用的英文用户名:
")
    s.send(('%s
' %username.strip()).encode("utf-8"))  # 发送一行字符串(以
 结束)到服务器端
    print (u'从服务器返回消息:')
    response = s.recv(1200).decode("utf-8").strip()
    print (response)    
    if "冲突" not in response:
        break

print("*"*50)
print("""查看当前登录用户列表的命令:list
查看别人给你发送的消息命令要求:getmessage 
给别人发送消息,请先输入send,然后按照如下格式发送聊天信息:
username:要发送的消息  
断开连接请输入bye""")
print("*"*50)


while 1:
    send_message=input("请输入发送的信息:
")    
    if send_message=="getmessage" :
        #当前用户名:getmessage --->发给服务器
        s.send(('%s:%s
' %(username,send_message)).encode("utf-8"))
        print (u'从服务器返回消息:')
        content = s.recv(1200).decode("utf-8").strip()
        #返回的消息内容是个列表[(发消息人名1,消息1),(发消息人名2,消息2),(发消息人名3,消息3)....]
        try:
            if "[" in content and ']' in content:
                #服务器返回的是个字符串,用eval转换为列表对象
                info_list = eval(content)
                for index,info in enumerate(info_list):#遍历
                    #index是序号,info[0]发消息的人名,info[1]是发送的消息体
                    print("%s>用户%s信息:%s" %(index,info[0],info[1]))
            else:
                    print(content)            
        except:
            print("从服务器收到的数据是无效数据!数据为%s" %content)
    elif send_message=="list":
        s.send(('%s
' %send_message).encode("utf-8"))
        print (u'从服务器返回消息:')
        print (s.recv(1200).decode("utf-8").strip())
    elif send_message=="bye":
        s.close()
        break
    elif send_message=="send":
        print("请输入你要给用户发送的消息,消息格式:
用户名:您要发的消息内容
")
        info = input(">")
        s.send(('%s:%s
' %(username,info.strip())).encode("utf-8"))#发给server端
        print("发送的消息:",('%s:%s
' %(username,info.strip()))) #打印发送的消息
    else:
        print("输入的消息无效:请使用getmessage、list、bye或者send之一")
        time.sleep(1)
        continue
原文地址:https://www.cnblogs.com/hqq2019-10/p/13931264.html