关于socket收发信息与多线程,多进程问题总结(后续补充)

上文地址:关于socket收发信息与多线程,多进程问题总结-CSDN博客

一  错误问题原因以及个人分析

在客户端申请关闭连接后,再次重新运行客户端发送信息会出现报错情况,分别为以下两种错误:

[WinError 10054]一个现有的连接被远程主机强制关闭

以及:

[WinError 10038] 在一个非套接字上尝试了一个操作

1.报错原因

因为服务端需要接收多个客户端发来的连接请求,所以这里采用的列表的方式存储客户端套接字。

代码如下:

 当客户端申请关闭后,服务器同意关闭,双方结束通讯,但套接字仍保留在list1中未被删除,当有人在发送消息的时候,服务器就会遍历列表,向列表中的每个客户端发送消息,当发送到已经断开连接的套接字时就会产生[WinError 10038] 在一个非套接字上尝试了一个操作这种错误。

2.解决方法

在关闭套接字后可以对list1中的套接字进行删除,同时因为对list1进行修改,对应的list_name中数据也需要进行修改(list_name中以字典形式存放了用户IP,name,和 list1中的对应的编号),这用就不会出现上述错误了。

代码展示:

 二  代码展示

1.服务端代码

# 导入模块
import socket
# 创建socket对象
import threading


# 通过服务端输入消息并向所有用户广播
def read_sock(list1):
    while True:
        str1 = input("请输入内容")
        for li in list1:
            li.send(f"{str1}".encode(encoding="utf-8"))
            print("发送成功")


# 接收到客户端发送来的消息,并向所有用户广播消息
def write_sock(listen_socket_1,list1,list_name,arr):
    while True:
        substance = listen_socket_1.recv(1024).decode(encoding="utf-8")
        print("接收成功")
        if substance == "exit":     # 检测用户是否要退出聊天室,如果是就执行if内操作,不是则执行else
            use = return_values(list_name, arr, 'seria')
            # 通过for循环向退出用户以外的其他用户发送该用户退出的消息
            # -------------------------------------------------------------
            for num1 in range(len(list1)):
                if num1 != use:
                    list1[num1].send(f"{return_values(list_name, arr, 'name')}退出聊天室".encode(encoding="utf-8"))
            # ----------------------------------------------------------------------

            list1[use].close()  # 关闭连接

            # 对已经退出的用户套接字进行删除(同过将退出用户后面的用户向前覆盖,并删除最后一个重复用户来实现)
            # ********************************************************************************
            for num2 in range(use, len(list1)-1):
                list1[num2] = list1[num2+1]
            del list1[len(list1)-1]
            # ********************************************************************************

            # 下方代码是对list_name的seria进行更新,seria为( list1中的对应的编号)
            for num3 in range(len(list_name)):
                if list_name[num3]['seria'] == use:
                    list_name[num3]['seria'] = None
                elif list_name[num3]['seria'] > use:
                    list_name[num3]['seria'] -= 1
            break
        else:  # 接收到客户端发送来的消息,并向所有用户广播消息
            for li in list1:
                print(list1)
                li.send(f"{return_values(list_name, arr, 'name')}说:{substance}".encode(encoding="utf-8"))
                print("发送成功")
                print(substance)


# 该函数是对list_name中的某个指定数据进行返回,condition为要返回的内容,有(name和seria)两种
def return_values(list_name, arr, condition):
    if condition == "name":
        for num1 in range(len(list_name)):
            if list_name[num1]["ip"] == arr:
                return list_name[num1]["name"]
    elif condition == "seria":
        for num1 in range(len(list_name)):
            if list_name[num1]["ip"] == arr:
                return list_name[num1]["seria"]


# 下方代码是开启服务端,
# --------------------------------------------------------
ip = '192.168.22.90'
port = 7777
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.bind((ip, port))
tcp_socket.listen(5)
list1 = []  # 用于存储连接成功的客户端套接字
list_name = []  # list_name中以字典形式存放了用户IP,name,和 list1中的对应的编号
# ---------------------------------------------------------

# 下方代码为循环接收客户端连接请求
while True:
    listen_socket_1, arr = tcp_socket.accept()
    list1.append(listen_socket_1)
    print(f"接收到一个连接{arr}")
    print(listen_socket_1)

    # 该for循环为检测用户基础信息是否存在,如果存在则执行for循环内if语句,给用户的seria更新新的list1中的位置,并执行break
    # 如果执行break则不会执行else中的语句,如果for循环执行完毕,
    # if仍未执行,则表示该用户信息不在list_name中,需要用出手动出入名称
    # ********************************************************************************
    for num in range(len(list_name)):
        if list_name[num]["ip"] == arr[0]:
            list_name[num]["seria"] = len(list1)-1
            for li in list1:
                li.send(f"欢迎{list_name[num]['name']}进入聊天室".encode(encoding="utf-8"))
            break
    else:
        listen_socket_1.send(f"IP{arr[0]}请输入姓名".encode(encoding="utf-8"))
        substance = listen_socket_1.recv(1024).decode(encoding="utf-8")
        list_name.append({"ip": arr[0], "name": substance, "seria": len(list1)-1})
        for li in list1:
            li.send(f"欢迎{substance}进入聊天室".encode(encoding="utf-8"))
    # ********************************************************************************

    print(f"现有人员{list_name}")

    # 下方代码通过多线程方式接收客户端消息或向客户端发送消息
    if __name__ == '__main__':
        p1 = threading.Thread(target=read_sock, args=(list1,))
        p2 = threading.Thread(target=write_sock, args=(listen_socket_1, list1, list_name, arr[0]))
        p1.setDaemon(True)
        p1.start()
        p2.start()

2.客户端代码

# 导入模块
import socket
# 创建socket对象
import threading


# 客户端向服务端发送来的消息,并向所有用户广播消息
def read_sock(tcp_socket):
    while True:
        global str1
        str1 = input()
        tcp_socket.send(f"{str1}".encode(encoding="utf-8"))
        print("发送成功")
        if str1 == "exit":
            break
    return 1


# 接收到服务端发送来的消息,并进行显示
def write_sock(tcp_socket):
    while True:
        substance = tcp_socket.recv(1024).decode(encoding="utf-8")
        print(substance)
        if str1 == "exit":
            break


# 下方代码为客户端向服务端发送连接申请
# --------------------------------------------------------
str1 = ""  # 创建公共字符用于接收客户端向服务端发送的消息,如果是exit,则同时退出收和发,这里采用的多线程可以共享全局的方法
ip = '192.168.22.90'
port = 7777
tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket.connect((ip, port))
# ---------------------------------------------------------

# 下方代码通过多线程方式接收服务器消息或向服务端发送消息
if __name__ == '__main__':
    p1 = threading.Thread(target=read_sock, args=(tcp_socket,))
    p2 = threading.Thread(target=write_sock, args=(tcp_socket,))
    p2.setDaemon(True)
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    tcp_socket.close()

三  其他问题及个人思路

1.其他问题

在客服端不采用exit退出系统,而是强制关闭系统时,则服务器会产生错误,同时失效的套接字仍存在在list1列表中,继续发送信息时就会导致系统崩溃。

2.个人思路

在发送时使用try:监测发送语句,如果报错则删除掉报错的套接字并更新list_name,然后继续执行程序,(因为报错为失效套接字,所以跳过本次发送也是没有问题的,并向列表内的下一个套接字发送消息)

(待实现)

  • 19
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
在C++中使用socket发送数据,可以通过以下步骤进行: 1. 包含头文件:首先需要包含相关的头文件,包括<sys/socket.h>和<netinet/in.h>。 2. 创建socket:使用socket()函数创建一个socket对象,该函数返回一个整数值作为socket的文件描述符。 3. 设置服务器地址:创建一个sockaddr_in结构体对象,并设置服务器的IP地址和端口号。 4. 连接服务器:使用connect()函数将socket连接到服务器。该函数需要传入socket文件描述符和服务器地址。 5. 发送数据:使用send()函数发送数据。该函数需要传入socket文件描述符、发送缓冲区的指针和发送数据的长度。 下面是一个简单的示例代码,演示了如何使用C++ socket发送数据: ```cpp #include <iostream> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> int main() { // 创建socket int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1) { std::cerr << "Failed to create socket." << std::endl; return 1; } // 设置服务器地址 sockaddr_in serverAddr{}; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(8080); // 设置服务器端口号 serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 设置服务器IP地址 // 连接服务器 if (connect(sockfd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) { std::cerr << "Failed to connect to server." << std::endl; close(sockfd); return 1; } // 发送数据 const char* message = "Hello, server!"; if (send(sockfd, message, strlen(message), 0) == -1) { std::cerr << "Failed to send data." << std::endl; close(sockfd); return 1; } // 关闭socket close(sockfd); return 0; } ``` 请注意,上述代码仅演示了发送数据的基本过程,并未处理错误情况和异常情况。在实际使用中,应该根据具体需求进行错误处理和异常处理。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值