socket实现局域网聊天室笔记

是笔记。socket实现聊天室,需要有服务端和客户端,此博客用于记录此程序所用所有方法的功能,便于理解。

先服务端:

import socket
import threading

from Tools.scripts.treesync import raw_input

con = threading.Condition() #判断条件  锁
# 线程A需要等某个条件成立才能继续往下执行,现在这个条件不成立,线程A就阻塞等待,而线程B在执行过程中使这个条件成立了,就唤醒线程A继续执行。
# 在pthread库中通过条件变量(Condition Variable)来阻塞等待一个条件,或者唤醒等待这个条件的线程。
# thread内部Condition条件变量有两个关键函数, await和signal方法,对应python threading Condition是wait和notify方法。
#
#  一个Condition实例的内部实际上维护了两个队列,一个是等待锁队列,mutex内部其实就是维护了一个队列。 另一个队列可以叫等待条件队列,在这队列中的节点都是由于(某些条件不满足而)线程自身调用wait方法阻塞的线程,记住是自身阻塞。最重要的Condition方法是wait和 notify方法。另外condition还需要lock的支持, 如果你构造函数没有指定lock,condition会默认给你配一个rlock。
# 下面是这两个方法的执行流程。
#           await方法:
#                             1. 入列到条件队列(注意这里不是等待锁的队列)
#                             2. 释放锁
#                             3. 阻塞自身线程
#                              ————被唤醒后执行————-
#                             4. 尝试去获取锁(执行到这里时线程已不在条件队列中,而是位于等待(锁的)队列中,参见signal方法)
#                                 4.1 成功,从await方法中返回,执行线程后面的代码
#                                 4.2 失败,阻塞自己(等待前一个节点释放锁时将它唤醒)
#          注意: 调用wait可以让当前线程休眠,等待其他线程的唤醒,也就是等待signal,这个过程是阻塞的。 当队列首线程被唤醒后,会继续执行await方法中后面的代码。
#          signal (notify)方法:
#
#                            1. 将条件队列的队首节点取出,放入等待锁队列的队尾
#                            2. 唤醒节点对应的线程.
# 注: signal ( notify ) 可以把wait队列的那些线程给唤醒起来。
host = raw_input('input the server ip address:')
port = 8888
data = ''  #文本

# TCP编程的服务器端一般步骤是:
#   1、创建一个socket,用函数socket();
#   2、设置socket属性,用函数setsockopt();
#   3、绑定IP地址、端口等信息到socket上,用函数bind();
#   4、开启监听,用函数listen();
#   5、接收客户端上来的连接,用函数accept();
#   6、收发数据,用函数send()
# 和recv(),或者read()
# 和write();
#   7、关闭网络连接;
#   8、关闭监听;

s = socket.socket()  #创建了一个服务
print('Socket created')
s.bind((host,port)) #将IP绑定到固定端口上
s.listen(5) #监听连接,listen(n)传入的值, n表示的是服务器拒绝(超过限制数量的)连接之前,操作系统可以挂起的最大连接数量。
# n也可以看作是"排队的数量",即除了正在处理(已接入)的线程数外还可等待进入线程中的数量

#self.Bind(wx.EVT_BUTTON, self.OnButtonClick, self.button)
# self.Bind(事件类型, 事件名, 绑定的控件名)
# self.button.Bind(wx.EVT_LEAVE_WINDOW, self.OnLeaveWindow)
# self.button.Bind(事件类型, 事件名)

# Bind是用来将控件事件与控件做绑定的,用self.Bind时,在后面的参数中你可以看到加入了控件,self.button.Bind没有,因为是直接使用了控件的Bind。

print('Socket new listening')

def NotifyAll(sss):
    global data
    if con.acquire(): #获取锁 使得当前只能进行对data赋值的操作,其他操作不能进行
        data = sss
        con.notifyAll() #表示当前线程放弃对资源的占用 通知其他线程从wait方法后面执行
        con.release()  #释放锁

def threadOut(conn,nick): #发送消息
    global data
    while True:
        if con.acquire():
            con.wait()   #会阻塞在这里,放弃对当前资源的占用,等消息通知
            if data:
                try:
                    conn.send(data)   #发送
                    con.release()
                except:
                    con.release()
                    return

def ThreadIn(conn,nick):   #接收消息
    while True:
        try:
            temp = conn.recv(1024)
            if not temp:
                conn.close()
                return
            NotifyAll(temp)
            print(data)
        except:
            NotifyAll(nick+'error')




while True:
    conn,addr = s.accept()  #接收到连接了,返回的是一个元组,conn参数是服务端与客户端的链接,addr返回的是客户端的IP和端口
    print('Connected with'+addr[0]+':'+str(addr[1]))
    nick = conn.recv(1024).decode(encoding='utf-8')
    #在服务器端,socket()返回的套接字用于监听(listen)和接受(accept),这个套接字不能用于与客户端之间发送和接收数据。
    NotifyAll('welcome'+nick+'to the room!!!')
    print(data)
    conn.send(data.encode())
    threading.Thread(target=threadOut,args=(conn,nick)).start()
    threading.Thread(target=threadOut,args=(conn,nick)).start()

 然后是客户端:

#客户端
import socket #自带的模块
import threading

from Tools.scripts.treesync import raw_input
#设置全局变量,全部定义为空,若函数出错,仍可发送空消息
outString = ''
nick = ''
inString = ''

def client_send(sock):  #在函数内部改变全局变量,sock为参数
    global outString
    while True:          #使客户端一直处于能够发送的循环状态
        #死循环 一直监听输入 若有输入 就会发送至服务端
        outString = raw_input() #接收输入,此处有bug,暂无法解决,后续有解决方法再修改
        outString = nick+':'+outString
        sock.send(outString)  #发送outString的数据,发送TCP数据,返回发送的字节大小。这个字节长度可能少于实际要发送的数据的长度。
        # 换句话说,这个函数执行一次,并不一定能发送完给定的数据,可能需要重复多次才能发送完成。

        # TCP编程的客户端一般步骤是:
        #   1、创建一个socket,用函数socket();
        #   2、设置socket属性,用函数setsockopt();
        #   3、绑定IP地址、端口等信息到socket上,用函数bind();
        #   4、设置要连接的对方的IP地址和端口等属性;
        #   5、连接服务器,用函数connect();
        #   6、收发数据,用函数send()
        # 和recv(),或者read()
        # 和write();
        #   7、关闭网络连接;

def client_accept(sock):
    global inString  #在生产环境不推荐使用
    while True:
        try:
            inString = sock.recv(1024)  #接收数据,不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。
            # recv函数是阻塞的,也就是会一直等待服务端发送来的数据包。如果没有数据包到来,就一直会等待。
            if not inString:
                break
            if outString != inString:
                print(inString)
        except:
            break

#创建套接字
nick = raw_input('input your nickname:')  # raw_input() 用来获取控制台的输入,将所有输入作为字符串看待,返回字符串类型。
ip = raw_input('input the server ip address:')  #输入ip地址
port = 8888  #端口
sock = socket.socket()  #创建文明的套接字,调用socket包里面的socket方法,AF_INET表示是IPv4协议
# socket通常也称作"套接字",用于描述IP地址和端口,应用程序通常通过"套接字"向网络发出请求或者应答网络请求,可以认为是一种计算机网络的数据结构,接口。
sock.connect((ip,port)) #连接
sock.send(nick.encode())  #把用户名发送给服务端

th_send = threading.Thread(target=client_send,args=(sock,))  #创建发送信息的线程
# target  : 指定这个线程去哪个函数里面去执行代码
#
# args:     指定将来调用函数的时候传递什么数据过去
#
#                   args参数指定的一定是一个元组类型,元组即需用括号括起来的集合,当只有一个参数时,需在此参数后加逗号来表明是元组类型
th_send.start()  #启动发送线程
th_accept = threading.Thread(target=client_accept,args=(sock,))  #创建接收信息的线程
th_accept.start()  #启动接收线程

outString = raw_input() 此处有bug,暂无法解决,后续有解决方法再修改。错误提示:TypeError: raw_input() missing 1 required positional argument: 'self'

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值