【python】TCP socket 实现局域网一对一聊天、另附TLS加密加密版

一、初始版

1.1 代码效果演示

先启动服务器端,再启动客户端。在聊天中输入’quit’,退出聊天。
在这里插入图片描述
大概的原理:就是服务器端和客户端都分别启动两个线程用来完成发送消息和接收消息。

1.2服务器端源码

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 26 22:39:40 2020
@author: kenwanmao
"""
import socket
from threading import Thread
import psutil
#获取网卡名称和其ip地址,不包括回环
def get_netcard():
    netcard_info = []
    info = psutil.net_if_addrs()
    for k,v in info.items():
        for item in v:
            if item[0] == 2 and not item[1]=='127.0.0.1':
                netcard_info.append((k,item[1]))
    return netcard_info

def recv():
    while True:
        recv_data= newSocket.recv(1024)
        recv_msg = recv_data.decode('utf-8')
        if recv_msg == 'quit' : 
            print('The client wants to close the connection')
            newSocket.close()
            break
        print('客户端>:', recv_msg)

def send_msg():
    while True:
        msg = input('Server>')
        if msg == 'quit' : 
            print('close the connection!')
            newSocket.close()
            break
        newSocket.send(msg.encode('utf-8'))

# 创建TCP socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
local_address_list = get_netcard()
wlan_ip = local_address_list[-1][1]
print('本机的局域网ip:{}'.format(wlan_ip))
address = input('请输入本机的局域网ip:(default 自动填写)') or wlan_ip
ip_port = (address, 23333)  # 绑定本地信息.
s.bind(ip_port)
s.listen(1)
print('chat server 已启动...,正在监听{}'.format(s.getsockname()))
newSocket, clientAddr = s.accept()
print('与客户端 {} 连接成功!'.format(newSocket.getpeername()))

thread_recv = Thread(target=recv)
thread_send = Thread(target=send_msg)
thread_recv.start()
thread_send.start()
thread_recv.join()
thread_send.join()

# 关闭这个客户端服务的套接字,只要关闭了,就意味着不能再为这个客户端服务器了,如果还需要服务,只能再次重新连接
newSocket.close()

# 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收到任何新的客户端的连接
s.close()

1.3客户端源码

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 26 22:39:41 2020

@author: kopstone
"""

import socket,sys
from threading import Thread

def recv():
    while True:
        data_recv = s.recv(1024)
        text_recv = data_recv.decode('utf-8')
        if text_recv == 'quit' : 
            print('The server wants to close the connection')
            s.close()
            break
        print('服务端>:', text_recv)

def send_msg():
    while True:
        msg = input(name+'>')
        if msg == 'quit' : 
            print('close the connection!')
            s.close()
            break
        s.send(msg.encode('utf-8'))

s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 1、创建套接字
dest_ip = input('请输入服务器端ip: (default localhost)')
if len(dest_ip)<=0 : 
    print('目的ip不能为空')
    sys.exit(0)
#dest_port = int(input('请输入服务端端口:'))
#dest_ip = '192.168.2.158'
dest_port = 23333
name = input('input your nickname (default kenwanmao) :') or 'kenwanmao'
s.connect((dest_ip,dest_port)) # 2、创建链接

thread_recv = Thread(target=recv)
thread_send = Thread(target=send_msg)
thread_recv.start()
thread_send.start()
thread_recv.join()
thread_send.join()
# 4、关闭
s.close()

1.4 用pyinstaller打包成可执行的.exe文件

第一步:安装 PyInstaller 模块与安装其他 Python 模块一样,使用 pip 命令安装即可。在命令行输入如下命令:

pip install pyinstaller

接下来使用命令行工具进入到目录下,执行如下命令:

pyinstaller -F D:\PythonWorkPlace\serverchat.py

最后在下面的位置找到。这种可执行的.exe文件可以在未安装python环境的电脑中直接运行。
在这里插入图片描述
打包结果:
在这里插入图片描述

二、将初始版的服务器端和客户端整合成一个文件

2.1 代码效果演示

启动方式:python localchat.py,进入后选s
python localchat.py,进入后选c
在这里插入图片描述

2.2 源代码

# -*- coding: utf-8 -*-
"""
Created on 2020/9/20
@author: kenwanmao
"""
import socket, argparse
from threading import Thread
import psutil
#获取网卡名称和其ip地址,不包括回环
def get_netcard():
    netcard_info = []
    info = psutil.net_if_addrs()
    for k,v in info.items():
        for item in v:
            if item[0] == 2 and not item[1]=='127.0.0.1':
                netcard_info.append((k,item[1]))
    return netcard_info

def server_recv(newSocket):
    while True:
        recv_data= newSocket.recv(1024)
        recv_msg = recv_data.decode('utf-8')
        if recv_msg == 'quit' : 
            print('The client wants to close the connection')
            newSocket.close()
            break
        print('客户端>:', recv_msg)

def server_send_msg(newSocket):
    while True:
        msg = input('Server>')
        if msg == 'quit' : 
            print('close the connection!')
            newSocket.close()
            break
        newSocket.send(msg.encode('utf-8'))

def client_recv(s):
    while True:
        data_recv = s.recv(1024)
        text_recv = data_recv.decode('utf-8')
        if text_recv == 'quit' : 
            print('The server wants to close the connection')
            s.close()
            break
        print('服务端>:', text_recv)

def client_send_msg(s,name):
    while True:
        msg = input(name+'>')
        if msg=='quit' : 
            print('close the connection!')
            s.close()
            break
        s.send(msg.encode('utf-8'))

def server(address, port):
    # 创建TCP socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ip_port = (address, port)  
    s.bind(ip_port)
    s.listen(1)
    print('chat server 已启动...,正在监听{}'.format(s.getsockname()))
    newSocket, clientAddr = s.accept()
    print('与客户端 {} 连接成功!'.format(newSocket.getpeername()))
    
    thread_recv = Thread(target=server_recv, args=(newSocket,))
    thread_send = Thread(target=server_send_msg, args=(newSocket,))
    thread_recv.start()
    thread_send.start()
    thread_recv.join()
    thread_send.join()

    # 关闭这个客户端服务的套接字,只要关闭了,就意味着不能再为这个客户端服务器了,如果还需要服务,只能再次重新连接
    newSocket.close()
    # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收到任何新的客户端的连接
    s.close()

def client(address, port):
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 1、创建套接字
    name = input('input your nickname (default kenwanmao) :') or 'kenwanmao'
    s.connect((address, port)) # 2、创建链接

    thread_recv = Thread(target=client_recv, args=(s,))
    thread_send = Thread(target=client_send_msg, args=(s,name))
    thread_recv.start()
    thread_send.start()
    thread_recv.join()
    thread_send.join()
    # 4、关闭
    s.close()

if __name__ == '__main__':
    local_address_list = get_netcard() #获取本机上的ip地址,包括虚拟机的
    wlan_ip = local_address_list[-1][1] #获取本机上wlan网卡的ip地址,这才是局域网的地址。
    print('本机的局域网ip:{}'.format(wlan_ip))
    while True:
        getstart = input('请选择启动端,s代表服务器端、c代表客户端,输入其他无效会重复,q代表退出:')
        if getstart == 's':
            server(wlan_ip,23333) #启动服务器端
        elif getstart == 'c':
            dest_ip = input('请输入服务器端ip (必填):')
            if len(dest_ip)<=0 : 
                print('警告,服务器端ip不能为空!')
                continue
            client(dest_ip,23333) #启动客户端
        elif getstart == 'q':
            break
        else :
            continue
   

三、TLS加密加密版

尚未完成,仍存在一些问题。但留代码在这里,可能将来再解决。

# -*- coding: utf-8 -*-
"""
Created on 2020/9/20
@author: kenwanmao
"""
import socket, argparse, ssl
from threading import Thread

def server_recv(newSocket):
    while True:
        recv_data= newSocket.recv(1024)
        recv_msg = recv_data.decode('utf-8')
        if recv_msg == 'quit' : 
            print('The client wants to close the connection')
            newSocket.close()
            break
        print('客户端>:', recv_msg)

def server_send_msg(newSocket):
    while True:
        msg = input('Server>')
        if 'quit' in msg : 
            print('close the connection!')
            newSocket.close()
            break
        newSocket.send(msg.encode('utf-8'))

def client_recv(ssl_sock):
    while True:
        data_recv = ssl_sock.recv(1024)
        text_recv = data_recv.decode('utf-8')
        if text_recv == 'quit' : 
            print('The server wants to close the connection')
            ssl_sock.close()
            break
        print('服务端>:', text_recv)

def client_send_msg(ssl_sock,name):
    while True:
        msg = input(name+'>')
        if 'quit' in msg : 
            print('close the connection!')
            ssl_sock.close()
            break
        ssl_sock.send(msg.encode('utf-8'))

def server(address, port, cafile=None):
    purpose = ssl.Purpose.CLIENT_AUTH
    context = ssl.create_default_context(purpose, cafile=cafile)
    #context.load_cert_chain(certfile)

    # 创建TCP socket
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ip_port = (address, port)  
    s.bind(ip_port)
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.listen(1)
    print('chat server 已启动...,正在监听{}'.format(s.getsockname()))
    newSocket, clientAddr = s.accept()
    print('与客户端 {} 连接成功!'.format(newSocket.getpeername()))

    ssl_sock = context.wrap_socket(newSocket,server_side=True)

    thread_recv = Thread(target=server_recv, args=(ssl_sock,))
    thread_send = Thread(target=server_send_msg, args=(ssl_sock,))
    thread_recv.start()
    thread_send.start()
    thread_recv.join()
    thread_send.join()

    # 关闭这个客户端服务的套接字,只要关闭了,就意味着不能再为这个客户端服务器了,如果还需要服务,只能再次重新连接
    newSocket.close()
    # 关闭监听套接字,只要这个套接字关闭了,就意味着整个程序不能再接收到任何新的客户端的连接
    s.close()

def client(address, port, cafile = None):
    purpose = ssl.Purpose.SERVER_AUTH
    context = ssl.create_default_context(purpose, cafile=cafile)
    s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# 1、创建套接字
    name = input('input your nickname (default kenwanmao) :') or 'kenwanmao'
    s.connect((address, port)) # 2、创建链接
    ssl_sock = context.wrap_socket(s,server_hostname=address)

    thread_recv = Thread(target=client_recv, args=(ssl_sock,))
    thread_send = Thread(target=client_send_msg, args=(ssl_sock,name))
    thread_recv.start()
    thread_send.start()
    thread_recv.join()
    thread_send.join()
    # 4、关闭
    s.close()

if __name__ == '__main__':
    choices = {'client': client, 'server': server}
    parser = argparse.ArgumentParser(description='局域网一对一聊天')
    parser.add_argument('role', choices=choices, help='选择服务器(聊天发起者)或客户端启动')
    parser.add_argument('-host',default='127.0.0.1',help='the server listens at (default 127.0.0.1);'
                        ' host the client sends to ')
    parser.add_argument('-p', metavar='PORT', type=int, default=6666,help='UDP port (default 6666)')
    #parser.add_argument('-s',metavar='certfile',default=None)
    args = parser.parse_args()
    function = choices[args.role]
    function(args.host, args.p)

参考博客

Python3 socket编程,利用多线程完成最简单的聊天室 TCP和UDP
【Python3之socket编程】
Python3+ssl实现加密通信
argument after * must be an iterable解决方法
Python PyInstaller安装和使用教程(详解版)
python 获取网卡名称及其IP地址

源码以及可执行文件的下载

【python】TCP socket 实现局域网一对一聊天.rar 下载

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值