zoukankan      html  css  js  c++  java
  • 线程间使用socket通信的计算器

    该程序是处理平时的算数运算,程序也没有什么特别之处,只是将所有运算分开运算,每个函数(线程)处理不同的运算符号里面的运算,然后将所有结果都汇总到main函数中进行最后汇总(相加减)运算,
    每个函数内都处理相加减法运算并返回结果给main函数中,然后在main函数将最后的结果返回给用户。可能我是因为不会线程间的通信,所以才想到了这个办法来处理线程间的通信问题。
    但是处理效率问题还没有考虑,也没有对比过,所以只能交给大家去测试啦。

    程序中主要的代码就是线程中使用socket进行通信,每个函数都启用了socket来作为服务器和客户端,每一个函数都处理不同的部分,如处理的{}里面的运算是使用Curlybraces函数处理,
    每个函数都是进行单运算符的运算,如乘法运算函数Multiplication,但只能处理两个数值的乘法运算,如Curlybraces函数只能处理{},如果{}里面有其他的括号([]、())
    就不能正常处理里面的括号的运算。

    程序的设想图:

    图虽然有点简陋,但是还是可以说明问题的。图中对应的符号就是处理相应的运算符的函数,图中的每个函数都不是独立的,而都是通过socket来相连着,每条线都好比是一条网线,
    只要是相连着都是可以进行通信的,所以当处理大括号'{}'函数中如果存在除加法和减法外的其他运算符都是可以通过socket通信来获取相应表达式的结果,其他的也亦然。

    具体的程序代码如下:

    首先,初始化用到的变量和数据,导入相关的库文件。portn的端口是不是感觉与下面的funcPort字典重复了?其实我只是将其分得更细而已,这个portn是各线程创建socket对象和用于监听的端口,
    而funcPort字典则是各个线程作为客户端来连接相应处理函数的端口,看下去你就会明白了。
    import socket
    import threading
    import time
    import re
    
    # 将所有的端口和函数绑定
    port1 = 8001    # to deal with {}  Curlybraces()
    port2 = 8002    # to deal with []  Brackets()
    port3 = 8003    # to deal with ()  Parentheses()
    port4 = 8004    # to deal with *   Multiplication()
    port5 = 8005    # to deal with /   Division()
    port6 = 8006    # to deal with %   TakeMoreThan()
    
    host = '127.0.0.1'    # 本地IP
    
    # 存放所有函数的字典,这样在调用函数时可以通过字典的key来获取相应的函数
    
    funcPort = {'{':'8001',
               '[':'8002',
               '(':'8003',
               '*':'8004',
               '/':'8005','%':'8006'}

    既然要将所有的加法和减法运算都在函数的本身处理,那得想办法将减法和加法融合。刚开始的时候我是再创建一个处理减法的函数,但是如果这样处理的话就导致线程函数内部

    更加复杂啦,但是想起最近看过了正则表达式有替换字符的功能,所以想到使用正则表达式去处理这个问题,只需要将表达式的减法转换成加法就可以了,然后在函数本身处理中,

    只需以'+'将表达式字符串分成好几块,这样就可以保证函数本身可以处理加法和减法运算了。

    def SearchRemoveSub(string):
        string_b = re.sub('-','+-',string)
        return string_b

    但是括号的处理确实有些难度,因为你要判断该括号有没有出现,还要判断其位置,最后将其分成独立的一块,然后将其交给相应的处理函数处理,这个问题也是困扰了我很久,

    测试过好多方法,最后还是用上了正则表达式来处理相应括号的搜索并返回一个值来判断该括号是否存在

    def SearchParentheses(string):
    
        num1=num2=num3=0
        if re.search('.*{.*',string):
            if re.search('.*}.*',string):
                num1 = 1
            else:
                # 上面的if语句不能判断输入的 } 在 { 的前面的情况
                pass
        if re.search('.*[.*',string):
            if re.search('.*].*',string):
                num2 = 2
            else:
                pass
        if re.search('.*(.*',string):
            if re.search('.*).*',string):
                num3 = 3
            else:
                pass
        else:
            pass
        return num1,num2,num3

    这里没有对一些情况进行处理,如:括号只出现一个呢,那应该要返回一个值来判断该括号是不完整的;还不能处理当这些括号在一个表达式中存在多对时也是不行的;

    还有就是上面注释的,当一对括号的位置颠倒了也是不能判断出来的。以上的情况中都是在用户输入中可能出现的,我们也是要将其处理的。

    既然将所有的括号都处理好了,然后现在就要进行函数的socket通信,来处理相应块的表达式了。在连接函数中用到了我们上面定义的字典,在代码中科院看到这里只是简单的

    进行socket通信,主要是将传入的块表达式发送相应函数处理服务器上,然后获取结果。请大家注意的是:要将没有括号的块表达式返回给调用函数中,我之前就是没有这句

    return block,然后测试了好久才发现有些值是没有加上去的,导致结果不正确,相信大家应该没有我这么不小心。

    def ConnectThread(block):
    
        for i in block:
            if i in ['{','[','(','*','/','%']:
                s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                s.connect((host,int(funcPort[i])))  # 通过字典来获取相应的端口
                s.sendall(block)   # 发送相应结果
                result = s.recv(1024)
                s.sendall('Done')    # 用于告诉服务器该请求连接已结束,以免被连接的服务器线程不知道何时断开连接
                s.close()
                return result
            else:
                pass
        else:
            return block    

    接下来就是在main函数中调用相应处理的函数了,在这个函数中首先使用SearchRemoveSub函数将传入的表达式进行去除'-',然后使用SearchParentheses函数查找

    有可能出现的各种括号,然后通过返回值来判断这些括号有没有出现,一旦出现相应的括号就使用ConnectThread函数连接相应的函数处理,并将结果直接通过正则表达式

    替换原来的块表达式,这样保证了最终的string_b处理的都是加法而没有其他运算符的出现。当然这里没有解决一个括号包含或被包含的情况或一个括号包含多种括号或多个

    括号的情况,如果有需要的可以改写一下,可以判断每个括号的位置如果有的话,然后比较括号的位置大小,如果有些括号被包含的话,只要稍作判断就可以了,别的就不

    多说了。在这段代码的最后使用字符串函数split将整个表达式以'+'分开,然后进行累加,得到整个表达式的和,最后返回给调用函数,即main函数。

    def AddAndSub(string):
    
        string_b = SearchRemoveSub(string)   
        num1,num2,num3 = SearchParentheses(string_b)
        # 只能用于各种括号不互相包含的情况下
        if num1 == 1:
            try:
                # {}存在,查找{}的位置
                startCu = string_b.index('{')
                endCu = string_b.index('}')
                blockCu = string_b[startCu:endCu + 1]  # {}数据块 当 startCa > endCa 时会导致捕获不了字符串
                # 调用相应处理的函数并捕获返回值
                result = ConnectThread(blockCu)
                string_b = string_b.replace(blockCu,result)
            except ValueError:
                # {}这对符号可能只存在一个
                print('Error expressions in {}')
        else:
            pass
    
        if num2 == 2:
            # 找到[]
            try:
                startBr = string_b.index('[')
                endBr = string_b.index(']')
                blockBr = string_b[startBr:endBr + 1]
                # 调用相应处理的函数并捕获返回值
                result = ConnectThread(blockBr)
                string_b = string_b.replace(blockBr,result)
            except ValueError:
                # []这对符号可能只存在一个
                print('Error expressions in []')
        else:
            pass
    
        if num3 == 3:
            try:
                # ()存在
                startPr = string_b.index('(')
                endPr = string_b.index(')')
                blockPr = string_b[startPr:endPr + 1]   # 当 startCa > endCa 时会导致捕获不了字符串
                # 调用相应处理的函数并捕获返回值
                result = ConnectThread(blockPr)
                string_b = string_b.replace(blockPr,result)
            except ValueError:
                # ()这对符号可能只存在一个
                print('Error expressions in ()')
        else:
            pass
    
        data = string_b.split('+')    # string_b已将'-'变为'+-'
        print('all data :'),
        print(data)   # 将每个块的值都列举出来,以防出错
        print('
    all data sum is :')
        Sum = 0
        for d in data:
            # 接收返回来的值,并将其相加
            Sum += float(ConnectThread(d))
            print('Sum : ' + str(Sum))
    
        return Sum

    然后就是编写相应的处理函数部分了,主要通过socket创建socket对象,并监听相应端口,处理来自客户端的数据,在这些函数中并没有使用多线程去处理,当出现多个客户端

    同时连接该服务器时可能会出现socket.error异常。

    首先的是单运算符的处理函数,乘法运算函数,从函数可以看出只可以处理两个数值的乘法运算,还有就是不能处理当表达式出现像5*6/3等这种类似的情况,

    如果要处理多个值的乘法运算等其他情况那就要改写一下代码啦,在这里我就不多说什么啦,这需要大家去思考一下啦。

    def Multiplication():
    
        q =  True
        s4 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s4.bind((host,port4))    # 绑定IP和端口
        s4.listen(5)      # 启动监听
        while q:
            conn,addr = s4.accept()    # 重复监听连接请求
            # 进入和处理连接请求
            while True:
                try:
                    data = conn.recv(1024)
                    if data == 'quit':
                        q = False
                        conn.close()
                        break    # 退出当前循环和退出当前连接
                    elif data == 'Done':
                        conn.close()
                        break
                    else:
                        dataValue = data.split('*')
                        result = float(dataValue[0].strip()) * float(dataValue[1].strip())   # 只处理两个值相乘?
                        conn.sendall(str(result))
                except socket.error,e:
                    print('Multiplication is Error',e)
                    q = False
                    break
        s4.close()

    至于除法运算和取余等其他的运算也是和乘法运算大同小异,只需要稍微修改一下乘法的代码就行拉,这里就不一一列举出来啦。

    然后就是各个括号的处理函数了,首先是大括号{}处理的函数,在这个函数中主要是监听相应的端口,接收来自客户端的数据,通过'+'将传入的表达式分成几个块,

    然后通过ConnectThread来调用处理块表达式获取到返回的结果,再在本地进行累加,然后将累加的结果再发送回客户端中。这个过程就像是上述的AddAndSub一样。

    def Curlybraces():
    
        q =  True
        s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s1.bind((host,port1))    # 绑定IP和端口
        s1.listen(5)      # 启动监听
        start = 0         # '{' 字符的位置
        end = 0           # '}' 字符的位置
        conn,addr = s1.accept()
        while q:
            try:
                data = conn.recv(1024)   # 接收数据
                if data == 'quit':
                    q = False
                else:
                    try:
                        start = data.index('{')
                        end = data.index('}')
                        exist = True     # 查找符号存在
                    except ValueError:
                        #print('Ca va error')
                        exist = False    # 查找符号不存在或个别不存在
                    if exist:
                        # 如果存在,就将该字符去掉,在计算其里面的算数
                        print('get in Curlybraces')
                        Sum = 0    #
                        block = data[start + 1:end]
                        blockData = block.split('+')
                        for d in blockData:
                            Sum += float(ConnectThread(d))
                            print('Sum : ' + str(Sum))
                        conn.sendall(str(Sum))   # 发送结果
                        print('quit Curlybraces')
            except socket.error,e:
                #print('Ca error')
                print(str(e).decode('gb2312'))
                q = False    # 关闭该线程
        s1.close()

    然后就是[]和()这两个括号的处理了,当然大家参照上述处理{}括号的函数就可以简单的写出来了,也没有什么特别的难的地方,都是类似的,所以就不将其一一粘贴出来了,

    最后就是main函数的编写了,在该函数中主要是创建函数线程和启动所有的函数线程,大家可以发现,这个函数中只能计算一次的表达式后就不会再有输入了,不过大家可以

    循环AddAndSub函数就可以计算多次输入的表达式了,如果出错也是需要大家自己去寻找解决方案的。本人在测试过程中也是出现很多的错误,但是还是一个个的解决了,

    所以当出现错误时,不要惊慌,使用print来排查是那一块出现错误,哪一块不能执行导致出现错误。当然由于能力有限,在测试中我尝试使用for循环来创建和启动所有线程时

    就出现了错误,但是不是因为其他函数出现错误,所以正如大家所见的代码都是一句句代码将一个个的线程启动起来的。

    def main(string_all):
    
        start = time.time()   # 启动时间
        print('start all threading')
    
        # 创建并启动所有函数线程
        # 将函数与编号关联,可以使用循环来创建和启动线程
        thread1 = threading.Thread(target=Curlybraces)
        thread1.start()
        thread2 = threading.Thread(target=Brackets)
        thread2.start()
        thread3 = threading.Thread(target=Parentheses)
        thread3.start()
        thread4 = threading.Thread(target=Multiplication)
        thread4.start()
        thread5 = threading.Thread(target=Division)
        thread5.start()
    
        result = AddAndSub(string_all)
    
        print('result : ' + str(result))
    
        print('use time : ' + str(time.time() - start))    # 整个过程所需的时间
    
        # 等待线程的终止
        # 应该放到程序的后面,不然会导致程序运行停止在
        # 监听线程结束的信号中,不会执行下去
        thread1.join()
        thread2.join()
        thread3.join()
        thread4.join()
        thread5.join()

    接下来就到了激奋人心的时刻了,测试代码的运行,测试代码很简单,只需要调用main函数就可以啦:

    if __name__ == '__main__':
        main('1+2+3+4+5+{1+2+3+4+4*4+4/2+10-5}+[44+342+3423]+(3424+234-213)')

    测试结果如下:

    start all threading
    get in Curlybraces
    Sum : 1.0
    Sum : 3.0
    Sum : 6.0
    Sum : 10.0
    Sum : 26.0
    Sum : 28.0
    Sum : 38.0
    Sum : 33.0
    quit Curlybraces
    get in Brackets
    Sum : 44.0
    Sum : 386.0
    Sum : 3809.0
    quit Brackets
    get in Parentheses
    Sum : 3424.0
    Sum : 3658.0
    Sum : 3445.0
    quit Parentheses
    all data : ['1', '2', '3', '4', '5', '33.0', '3809.0', '3445.0']

    all data sum is :
    Sum : 1.0
    Sum : 3.0
    Sum : 6.0
    Sum : 10.0
    Sum : 15.0
    Sum : 48.0
    Sum : 3857.0
    Sum : 7302.0
    result : 7302.0
    use time : 0.0269999504089

    不相信这结果的博友们可以认证一下喔,反正我信啦。整个程序大致就是这样啦,虽然问题挺多且没有解决的,但也算是一个完整的程序啊,这个程序的意义不大,但是由于是

    自己的突发奇想,然后就想分享一下,但是每个人的看法和理解都不一样,也不知道大家能不能明白,也不知道各路大神有什么看法,但请不要喷        谢谢。

    这个程序完全是个人的一些想法,由于看到了太多不一样的东西,自己也就想写一个不一样的东西,仅此而已。但是大家可以尝试一下将所有的处理函数,运行到不一样的主机

    上,然后只需要修改一下连接的IP地址即可,或者可以将该想法应用到不一样的程序中,来达到自己想要的效果,至于可以应用到什么样的程序中,那有待大家去开发,但是我

    觉得可以应用到运算方面的程序和处理数据的程序上,由于个人的知识有限,也不知道有哪一项技术是与这些相关的,the end ...

    <div style="border:silver 1px dashed;background-color: rgba(0, 0, 0, 0);600px;padding:12px;font-size: 12px;font-family: 微软雅黑; ">
      作者:<a href="http://www.cnblogs.com/GHost-Ma/" target="_blank">sdjnzqr</a><br>
      出处:<a href="http://www.cnblogs.com/GHost-Ma/">http://www.cnblogs.com/GHost-Ma/</a><br>
      版权:本文版权归作者和博客园共有<br>
      转载:欢迎转载,但未经作者同意,必须保留此段声明;必须在文章中给出原文连接;否则必究法律责任
    </div>
    http://www.cnblogs.com/GHost-Ma/
  • 相关阅读:
    vim编辑器入门
    线程概念
    forkJoin
    join()方法跟踪
    mybatis 注解和xml 优缺点
    sql 索引 sql_safe_updates
    spirngcloud文件
    springCloud com.sun.jersey.api.client.ClientHandlerException: java.net.ConnectException: Connection refused: connect
    创建线程池的四种方式
    ThreadLocal
  • 原文地址:https://www.cnblogs.com/GHost-Ma/p/5367458.html
Copyright © 2011-2022 走看看