socket 聊天程序实例

socket单线程的阻塞

单线程,即数据的串行发送,会导致阻塞,如果要解决这个问题,当然在Server端就需要支持多线程,即数据折并发。

# 本机实现的单线程非交互式的socket程序
# #### server ###########################################################################################
import socket                #导入socket类
 
HOST =''                     #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有,也可以写成0.0.0.0
PORT = 50007                 #Server端开放的服务端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #选择Socket类型和Socket数据包类型
s.bind((HOST, PORT))         #绑定IP地址和端口
s.listen(1)                  #定义侦听数开始侦听(实际上并没有效果)
conn, addr = s.accept()      #定义实例,accept()函数的返回值可以看上面的socket函数说明
 
print 'Connected by', addr
while 1:
    data = conn.recv(1024)    #接受套接字的数据
    if not data:break         #如果没有数据接收,则断开连接
    print 'revc:',data        #发送接收到的数据
    conn.sendall(data)        #发送接收到的数据
conn.close()                      #关闭套接字


# #### client ###########################################################################################
import socket
 
HOST = '192.168.1.13'        #定义目标主机名
PORT = 50007                 #定义目标主机开放服务端口号
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  #选择Socket类型和Socket数据包类型 
s.connect((HOST, PORT))      #连接到目标主机的socket(套接字)中
 
s.sendall('Hello, world!')   #发送数据
data = s.recv(1024)          #接收数据
s.close()                    #关闭socket
print 'Received', repr(data)
# 单线程+阻塞+交互式socket程序
# #### server:内容同上个例子#######################################################################33######
import socket                #导入socket类
  
HOST =''                     #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有,也可以写成0.0.0.0
PORT = 50007                 #Server端开放的服务端口
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)    #选择Socket类型和Socket数据包类型
s.bind((HOST, PORT))         #绑定IP地址和端口
s.listen(1)                  #定义侦听数开始侦听(实际上并没有效果)
conn, addr = s.accept()      #定义实例,accept()函数的返回值可以看上面的socket函数说明
  
print 'Connected by', addr
while 1:
    data = conn.recv(1024)    #接受套接字的数据
    if not data:break         #如果没有数据接收,则断开连接
    print 'revc:',data        #发送接收到的数据
    conn.sendall(data)        #发送接收到的数据
conn.close()                      #关闭套接字


# #### client ##########################################################################################
import socket
 
HOST = '192.168.1.13'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
 
while True:
    user_input = raw_input('msg to send:').strip()    #由User输入要发送的数据
    s.sendall(user_input)
    data = s.recv(1024)
    print 'Received', repr(data)
 
s.close()

 

 

socket(多线程聊天程序实例)

  该程序实现的是一个相对比较简单的聊天程序,由于是基于控制台实现的,所只设计容许两个人聊天,另外消息的编码的分割符为|。


 

# 首先建立一个User的数据结构
import socket 

class User:
    def __init__(self,skt,username='none'):
        self.skt=skt
        self.username=username
    def send_msg(self,msg):
        self.skt.send(msg)
    def logout(self):
        self.skt.close()
# server
import sys 
import socket
import threading,time
import User

#global variable
userlist=[] 
   
def hand_user_con(usr):
    try:
        isNormar=True
        while isNormar:
            data=usr.skt.recv(1024)
            time.sleep(1)
            msg=data.split('|')#分析消息
            if msg[0]=='login':
                print 'user [%s] login' % msg[1]
                usr.username=msg[1]
                notice_other_usr(usr)
            if msg[0]=='talk':
                print 'user[%s]to[%s]:%s' % (usr.username,msg[1],msg[2])
                send_msg(msg[1],msg[2])#发送消息给目标用户,参数1:目标用户,参数2:消息内容
            if msg[0]=='exit':
                print 'user [%s] exit' % msg[0]
                isNormar=False
                usr.close()
                userlist.remove(usr)
    except:
        isNormar=False
        
#通知其他用户以上的好友       
def notice_other_usr(usr):
    if(len(userlist)>1):
        print 'The two users'
        userlist[0].skt.send(("login|%s" % userlist[1].username))
        userlist[1].skt.send(("login|%s" % userlist[0].username))
    else:
        print 'The one users'
         
def send_msg(username,msg):
    for usr in userlist:
        if(usr.username==username):
            usr.skt.send(msg)
            

def main():
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind(('0.0.0.0',9999))
    s.listen(5)
    print u'waiting for connection...'
    while True:
        sock,addr=s.accept()#等待用户连接
        user=User.User(sock)
        userlist.append(user)
        t=threading.Thread(target=hand_user_con,args=(user,));
        t.start()  
    s.close()
    

if(__name__=="__main__"):
    main()
import sys 
import socket
import threading,time

#global variable    
isNormar=True
other_usr=''


def recieve_msg(username,s):
    global isNormar,other_usr
    print 'Please waiting other user login...'
    s.send('login|%s' %username)
    while(isNormar):
        data= s.recv(1024)#阻塞线程,接受消息
        msg=data.split('|')
        if msg[0]=='login':
            print u'%s user has already logged in, start to chat' % msg[1]
            other_usr=msg[1]
        else:
            print msg[0]
            
#程序入口
def main(): 
    global isNormar,other_usr  
    try:
        print 'Please input your name:'
        usrname=raw_input()
        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        s.connect(("127.0.0.1",9999))
        t=threading.Thread(target=recieve_msg,args=(usrname,s))
        t.start()
    except:
        print 'connection exception'
        isNormar=False
    finally:
        pass
    while isNormar:
        msg=raw_input()#接受用户输入
        if msg=="exit":
            isNormar=False
        else:
            if(other_usr!=''):
                s.send("talk|%s|%s" % (other_usr,msg))#编码消息并发送
    s.close()
    
if __name__=="__main__":
    main()
# SockteServer例子说明
# #### server ####################################
import SocketServer            #导入SocketServer,多线程并发由此类实现
 
class MySockServer(SocketServer.BaseRequestHandler):    #定义一个类
 
    def handle(self):      #handle(self)方法是必须要定义的,可以看上面的说明
        print 'Got a new connection from', self.client_address
        while True:
            data = self.request.recv(1024)    #需要通过self的方法调用数据接收函数
            if not data:break
            print 'recv:', data
 
            self.request.send(data.upper())   #需要通过self的方法调用数据接收函数
 
if __name__ == '__main__':    #并非一定要用这样的方式,只是建议这样使用
    HOST = ''             #定义侦听本地地址口(多个IP地址情况下),这里表示侦听所有
    PORT = 50007          #Server端开放的服务端口
    s = SocketServer.ThreadingTCPServer((HOST, PORT), MySockServer)
                              #调用SocketServer模块的多线程并发函数
    s.serve_forever()     #持续接受客户端的连接


# #### client #####################################
import socket
 
HOST = '192.168.1.13'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
 
while True:
    user_input = raw_input('msg to send:').strip()
    s.sendall(user_input)
    data = s.recv(1024)
    print 'Received', repr(data)
 
s.close()

 

基于SocketServer多线程的简化SSH程序

# #### server ############################################
import SocketServer
import commands    #使用其中的getstatusoutput()函数,让Server端可以识别Client端发送过来的命令并执行
import time        #主要使用其中的time.sleep()函数,用来解决Server端发送数据的“连块”问题
 
class MySockServer(SocketServer.BaseRequestHandler):
 
    def handle(self):
        print 'Got a new connection from', self.client_address
        while True:
            cmd = self.request.recv(1024)
            if not cmd:
                print 'Last connection with:',self.client_address
                break
             
            cmd_result = commands.getstatusoutput(cmd)    #获取Client端的指令并执行,返回结果是一个存储两个元素的元组,第一个元素为0表示成功执行,第二个元素则是命令的执行结果
 
            self.request.send(str(len(cmd_result[1])))    #发送命令执行结果的大小长度,Client端要想接收任意大小的执行结果,就需要根据命令执行结果的大小来选择策略,这里需要注意的是,发送的数据是字符串,所以需要作类型转换
 
            time.sleep(0.2)        #睡眠0.2s,是为了解决“连块”的问题
 
            self.request.sendall(cmd_result[1]) #发送命令执行结果
 
if __name__ == '__main__':
    HOST = ''
    PORT = 50007
    s = SocketServer.ThreadingTCPServer((HOST, PORT), MySockServer)
 
    s.serve_forever()


# #### client #######################################
import socket
 
HOST = '192.168.1.13'
PORT = 50007
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
 
def data_all(obj, lenth_size):
    data = ''                #用来存储每一次循环时socket接收的数据,解决socket大概两万多字节的缓冲瓶颈限制
    while lenth_size != 0:   #如果接收的数据长度不为0,开始执行接收数据处理策略
        if lenth_size <= 4096:    #这里以4096为单位块,作为每次的数据处理量大小
            data_recv = obj.recv(lenth_size)    #通过recv()接收数据
            lenth_size = 0    #通过这一步的处理,数据全部接收完毕,置lenth_size为0,结束循环,完成数据的接收处理工作
        else:
            data_recv = obj.recv(4096)    #以4096为单位块,一次接收4096的数据量大小
            lenth_size -= 4096            #处理完一次4096字节的数据后,将lenth_size减去4096
        data += data_recv                     #判断外层,用本地的data来存储接收到的数据,因为本地的data大小没有限制,所以不存在data饱和无法继续存储数据的问题,但前面socket的recv()函数一次最多只能接收的数据量大小是有限制的,这取决于socket的缓冲区大小,因此data_all函数的作用就是通过使用多次recv()函数,并且每次接收一定量的数据后就进行本地存储,直到把所有的数据都接收完毕
    return data
 
while True:
    user_input = raw_input('cmd to send:').strip()
    if len(user_input) == 0:continue
    s.sendall(user_input)
 
    data_size = int(s.recv(1024))      #得到命令执行结果的大小长度,因为发送过来的数据是字符串,所以这里要作类型转换
    print '\033[32;1mdata size:\033[0m',data_size    #打印命令执行结果的大小
    result = data_all(s, data_size)    #通过data_all函数来执行相应的数据接收处理策略
    print result                       #打印命令执行结果
 
s.close()                                  #关闭套接字

 

PS:

  1.Server端发送数据的“连块”问题,即发送两次数据时,如果发送间隔比较短,socket会把两次发送的数据放在一起来发送,这里通过time.sleep()函数来解决。

  2.socket的缓冲区大小问题,即当执行top -bn 3这样执行结果长度大的命令时,socket缓冲区一次是无法存储这么多数据的,所以只能分多次来接收数据,这样就会在Client端带来一定的问题,比如命令执行的不同步等,解决的方法是通过用循环接收的方法来进行本地存储Server端发送的数据。

  当然,如果要执行man等查询方面的命令,上面的程序也是无法做到的,所以这里说,这只是一个简版的SSH程序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值