百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

linux下C++ socket网络编程——即时通信系统(含源码)

off999 2025-03-11 19:46 14 浏览 0 评论

一:项目内容

本项目使用C++实现一个具备服务器端和客户端即时通信且具有私聊功能的聊天室。

目的是学习C++网络开发的基本概念,同时也可以熟悉下Linux下的C++程序编译和简单MakeFile编写

二:需求分析

这个聊天室主要有两个程序:

1.服务端:能够接受新的客户连接,并将每个客户端发来的信息,广播给对应的目标客户端。

2.客户端:能够连接服务器,并向服务器发送消息,同时可以接收服务器发来的消息。

即最简单的C/S模型。

三:抽象与细化

服务端类需要支持:

1.支持多个客户端接入,实现聊天室基本功能。

2.启动服务,建立监听端口等待客户端连接。

3.使用epoll机制实现并发,增加效率。

4.客户端连接时,发送欢迎消息,并存储连接记录。

5.客户端发送消息时,根据消息类型,广播给所有用户(群聊)或者指定用户(私聊)。

6.客户端请求退出时,对相应连接信息进行清理。

客户端类需要支持:

1.连接服务器。

2.支持用户输入消息,发送给服务端。

3.接受并显示服务端发来的消息。

4.退出连接。

涉及两个事情,一个写,一个读。所以客户端需要两个进程分别支持以下功能。

子进程:

1.等待用户输入信息。

2.将聊天信息写入管道(pipe),并发送给父进程。

父进程:

1.使用epoll机制接受服务端发来的消息,并显示给用户,使用户看到其他用户的信息。

2.将子进程发送的聊天信息从管道(pipe)中读取出来,并发送给客户端。

四:C/S模型

TCP服务端通信常规步骤:

1.socket()创建TCP套接字

2.bind()将创建的套接字绑定到一个本地地址和端口上

3.listen(),将套接字设为监听模式,准备接受客户请求

4.accept()等用户请求到来时接受,返回一个对应此连接新套接字

5.用accept()返回的套接字和客户端进行通信,recv()/send() 接受/发送信息。

6.返回,等待另一个客户请求。

7.关闭套接字

TCP客户端通信常规步骤:

1.socket()创建TCP套接字。

2.connect()建立到达服务器的连接。

3.与客户端进行通信,recv()/send()接受/发送信息,write()/read() 子进程写入管道,父进程从管道中读取信息然后send给客户端

5. close() 关闭客户连接。

五:相关技术介绍

1.socket 阻塞与非阻塞

阻塞与非阻塞关注的是程序在等待调用结果时(消息,返回值)的状态。

阻塞调用是指在调用结果返回前,当前线程会被挂起,调用线程只有在得到调用结果之后才会返回。

非阻塞调用是指在不能立刻得到结果之前,该调用不会阻塞当前线程。

eg. 你打电话问书店老板有没有《网络编程》这本书,老板去书架上找,如果是阻塞式调用,你就会把自己一直挂起,守在电话边上,直到得到这本书有或者没有的答案。如果是非阻塞式调用,你可以干别的事情去,隔一段时间来看一下老板有没有告诉你结果。

同步异步是对书店老板而言(同步老板不会提醒你找到结果了,异步老板会打电话告诉你),阻塞和非阻塞是对你而言。

socket()函数创建套接字时,默认的套接字都是阻塞的,非阻塞设置方式代码:

//将文件描述符设置为非阻塞方式(利用fcntl函数)

fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)| O_NONBLOCK);

2. epoll

当服务端的人数越来越多,会导致资源吃紧,I/O效率越来越低,这时就应该考虑epoll,epoll是Linux内核为处理大量句柄而改进的poll,是linux特有的I/O函数。其特点如下:

1)epoll是Linux下多路复用IO接口select/poll的增强版本,其实现和使用方式与select/poll大有不同,epoll通过一组函数来完成有关任务,而不是一个函数。

2)epoll之所以高效,是因为epoll将用户关心的文件描述符放到内核里的一个事件列表中,而不是像select/poll每次调用都需要重复传入文件描述符集或事件集(大量拷贝开销),比如一个事件发生,epoll无需遍历整个被监听的描述符集,而只需要遍历哪些被内核IO事件异步唤醒而加入就绪队列的描述符集合即可。

3)epoll有两种工作方式,LT(Level triggered) 水平触发 、ET(Edge triggered)边沿触发。LT是select/poll的工作方式,比较低效,而ET是epoll具有的高速工作方式。

Epoll 用法(三步曲):

第一步:int epoll_create(int size)系统调用,创建一个epoll句柄,参数size用来告诉内核监听的数目,size为epoll支持的最大句柄数。

第二步:int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event) 事件注册函数

参数 epfd为epoll的句柄。参数op 表示动作 三个宏来表示:EPOLL_CTL_ADD注册新fd到epfd 、EPOLL_CTL_MOD 修改已经注册的fd的监听事件、EPOLL_CTL_DEL从epfd句柄中删除fd。参数fd为需要监听的标识符。参数结构体epoll_event告诉内核需要监听的事件。

第三步:int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout) 等待事件的产生,通过调用收集在epoll监控中已经发生的事件。参数struct epoll_event 是事件队列 把就绪的事件放进去。

eg. 服务端使用epoll的时候步骤如下:

1.调用epoll_create()在linux内核中创建一个事件表。

2.然后将文件描述符(监听套接字listener)添加到事件表中

3.在主循环中,调用epoll_wait()等待返回就绪的文件描述符集合。

4.分别处理就绪的事件集合,本项目中一共有两类事件:新用户连接事件和用户发来消息事件。

六:代码结构

每个文件的作用:

1.Common.h:公共头文件,包括所有需要的宏以及socket网络编程头文件,以及消息结构体(用来表示消息类别等)

2.Client.h Client.cpp :客户端类的实现

3.Server.h Server.cpp : 服务端类的实现

4.ClientMain.cpp ServerMain.cpp 客户端及服务端的主函数。

七:代码实现

Common.h

定义一些共用的宏定义,包括一些共用的网络编程相关头文件。

1)定义一个函数将文件描述符fd添加到epfd表示的内核事件表中供客户端和服务端两个类使用。

2)定义一个信息数据结构,用来表示传送的信息,结构体包括发送方fd, 接收方fd,用来表示消息类别的type,还有文字信息。

函数recv() send() write() read() 参数传递是字符串,所以在传送前/接受后要把结构体转换为字符串/字符串转换为结构体。

#ifndef CHATROOM_COMMON_H

#define CHATROOM_COMMON_H

#include

#include

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <sys/epoll.h>

#include

#include

#include

#include

#include

#include

// 默认服务器端IP地址

#define SERVER_IP "127.0.0.1"

// 服务器端口号

#define SERVER_PORT 8888

// int epoll_create(int size)中的size

// 为epoll支持的最大句柄数

#define EPOLL_SIZE 5000

// 缓冲区大小65535

#define BUF_SIZE 0xFFFF

// 新用户登录后的欢迎信息

#define SERVER_WELCOME "Welcome you join to the chat room! Your chat ID is: Client #%d"

// 其他用户收到消息的前缀

#define SERVER_MESSAGE "ClientID %d say >> %s"

#define SERVER_PRIVATE_MESSAGE "Client %d say to you privately >> %s"

#define SERVER_PRIVATE_ERROR_MESSAGE "Client %d is not in the chat room yet~"

// 退出系统

#define EXIT "EXIT"

// 提醒你是聊天室中唯一的客户

#define CAUTION "There is only one int the char room!"

// 注册新的fd到epollfd中

// 参数enable_et表示是否启用ET模式,如果为True则启用,否则使用LT模式

static void addfd( int epollfd, int fd, bool enable_et )

{

struct epoll_event ev;

ev.data.fd = fd;

ev.events = EPOLLIN;

if( enable_et )

ev.events = EPOLLIN | EPOLLET;

epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev);

// 设置socket为非阻塞模式

// 套接字立刻返回,不管I/O是否完成,该函数所在的线程会继续运行

//eg. 在recv(fd...)时,该函数立刻返回,在返回时,内核数据还没准备好会返回WSAEWOULDBLOCK错误代码

fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0)| O_NONBLOCK);

printf("fd added to epoll!\n\n");

}

//定义信息结构,在服务端和客户端之间传送

struct Msg

{

int type;

int fromID;

int toID;

char content[BUF_SIZE];

};

#endif // CHATROOM_COMMON_H

服务端类 Server.h Server.cpp

服务端需要的接口:

1)init()初始化

2)Start()启动服务

3)Close()关闭服务

4)广播消息给所有客户端函数 SendBroadcastMessage()

服务端的主循环中每次都会检查并处理EPOLL中的就绪事件,而就绪事件列表主要是两种类型:新连接或新消息。服务器会依次从就绪事件列表里提取事件进行处理,如果是新连接则accept()然后addfd(),如果是新消息则SendBroadcastMessage()实现聊天功能。

Server.h

#ifndef CHATROOM_SERVER_H

#define CHATROOM_SERVER_H

#include

#include "Common.h"

using namespace std;

// 服务端类,用来处理客户端请求

class Server {

public:

// 无参数构造函数

Server();

// 初始化服务器端设置

void Init();

// 关闭服务

void Close();

// 启动服务端

void Start();

private:

// 广播消息给所有客户端

int SendBroadcastMessage(int clientfd);

// 服务器端serverAddr信息

struct sockaddr_in serverAddr;

//创建监听的socket

int listener;

// epoll_create创建后的返回值

int epfd;

// 客户端列表

list clients_list;

};

//Server.cpp

#include

#include "Server.h"

using namespace std;

// 服务端类成员函数

// 服务端类构造函数

Server::Server(){

// 初始化服务器地址和端口

serverAddr.sin_family = PF_INET;

serverAddr.sin_port = htons(SERVER_PORT);

serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

// 初始化socket

listener = 0;

// epool fd

epfd = 0;

}

// 初始化服务端并启动监听

void Server::Init() {

cout << "Init Server..." << endl;

//创建监听socket

listener = socket(PF_INET, SOCK_STREAM, 0);

if(listener < 0) { perror("listener"); exit(-1);}

//绑定地址

if( bind(listener, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {

perror("bind error");

exit(-1);

}

//监听

int ret = listen(listener, 5);

if(ret < 0) {

perror("listen error");

exit(-1);

}

cout << "Start to listen: " << SERVER_IP << endl;

//在内核中创建事件表 epfd是一个句柄

epfd = epoll_create (EPOLL_SIZE);

if(epfd < 0) {

perror("epfd error");

exit(-1);

}

//往事件表里添加监听事件

addfd(epfd, listener, true);

}

// 关闭服务,清理并关闭文件描述符

void Server::Close() {

//关闭socket

close(listener);

//关闭epoll监听

close(epfd);

}

// 发送广播消息给所有客户端

int Server::SendBroadcastMessage(int clientfd)

{

// buf[BUF_SIZE] 接收新消息

// message[BUF_SIZE] 保存格式化的消息

char recv_buf[BUF_SIZE];

char send_buf[BUF_SIZE];

Msg msg;

bzero(recv_buf, BUF_SIZE);

// 接收新消息

cout << "read from client(clientID = " << clientfd << ")" << endl;

int len = recv(clientfd, recv_buf, BUF_SIZE, 0);

//清空结构体,把接受到的字符串转换为结构体

memset(&msg,0,sizeof(msg));

memcpy(&msg,recv_buf,sizeof(msg));

//判断接受到的信息是私聊还是群聊

msg.fromID=clientfd;

if(msg.content[0]=='\\'&&isdigit(msg.content[1])){

msg.type=1;

msg.toID=msg.content[1]-'0';

memcpy(msg.content,msg.content+2,sizeof(msg.content));

}

else

msg.type=0;

// 如果客户端关闭了连接

if(len == 0)

{

close(clientfd);

// 在客户端列表中删除该客户端

clients_list.remove(clientfd);

cout << clientid='<< clientfd

<< clients_list.size()

<< " client in the char room"

<< endl;

}

// 发送广播消息给所有客户端

else

{

// 判断是否聊天室还有其他客户端

if(clients_list.size() == 1){

// 发送提示消息

memcpy(&msg.content,CAUTION,sizeof(msg.content));

bzero(send_buf, BUF_SIZE);

memcpy(send_buf,&msg,sizeof(msg));

send(clientfd, send_buf, sizeof(send_buf), 0);

return len;

}

//存放格式化后的信息

char format_message[BUF_SIZE];

//群聊

if(msg.type==0){

// 格式化发送的消息内容 #define SERVER_MESSAGE "ClientID %d say >> %s"

sprintf(format_message, SERVER_MESSAGE, clientfd, msg.content);

memcpy(msg.content,format_message,BUF_SIZE);

// 遍历客户端列表依次发送消息,需要判断不要给来源客户端发

list::iterator it;

for(it = clients_list.begin(); it != clients_list.end(); ++it) {

if(*it != clientfd){

//把发送的结构体转换为字符串

bzero(send_buf, BUF_SIZE);

memcpy(send_buf,&msg,sizeof(msg));

if( send(*it,send_buf, sizeof(send_buf), 0) < 0 ) {

return -1;

}

}

}

}

//私聊

if(msg.type==1){

bool private_offline=true;

sprintf(format_message, SERVER_PRIVATE_MESSAGE, clientfd, msg.content);

memcpy(msg.content,format_message,BUF_SIZE);

// 遍历客户端列表依次发送消息,需要判断不要给来源客户端发

list::iterator it;

for(it = clients_list.begin(); it != clients_list.end(); ++it) {

if(*it == msg.toID){

private_offline=false;

//把发送的结构体转换为字符串

bzero(send_buf, BUF_SIZE);

memcpy(send_buf,&msg,sizeof(msg));

if( send(*it,send_buf, sizeof(send_buf), 0) < 0 ) {

return -1;

}

}

}

//如果私聊对象不在线

if(private_offline){

sprintf(format_message,SERVER_PRIVATE_ERROR_MESSAGE,msg.toID);

memcpy(msg.content,format_message,BUF_SIZE);

bzero(send_buf,BUF_SIZE);

memcpy(send_buf,&msg,sizeof(msg));

if(send(msg.fromID,send_buf,sizeof(send_buf),0)<0)

return -1;

}

}

}

return len;

}

// 启动服务端

void Server::Start() {

// epoll 事件队列

static struct epoll_event events[EPOLL_SIZE];

// 初始化服务端

Init();

//主循环

while(1)

{

//epoll_events_count表示就绪事件的数目

int epoll_events_count = epoll_wait(epfd, events, EPOLL_SIZE, -1);

if(epoll_events_count < 0) {

perror("epoll failure");

break;

}

cout << "epoll_events_count =\n" << epoll_events_count << endl;

//处理这epoll_events_count个就绪事件

for(int i = 0; i < epoll_events_count; ++i)

{

int sockfd = events[i].data.fd;

//新用户连接

if(sockfd == listener)

{

struct sockaddr_in client_address;

socklen_t client_addrLength = sizeof(struct sockaddr_in);

int clientfd = accept( listener, ( struct sockaddr* )&client_address, &client_addrLength );

cout << "client connection from: "

<< inet_ntoa(client_address.sin_addr) << ":"

<< ntohs(client_address.sin_port) << clientfd='

addfd(epfd, clientfd, true);

// 服务端用list保存用户连接

clients_list.push_back(clientfd);

cout << "Add new clientfd = " << clientfd << " to epoll" << endl;

cout << "Now there are " << clients_list.size() << " clients int the chat room" << endl;

// 服务端发送欢迎信息

cout << "welcome message" << endl;

char message[BUF_SIZE];

bzero(message, BUF_SIZE);

sprintf(message, SERVER_WELCOME, clientfd);

int ret = send(clientfd, message, BUF_SIZE, 0);

if(ret < 0) {

perror("send error");

Close();

exit(-1);

}

}

//处理用户发来的消息,并广播,使其他用户收到信息

else {

int ret = SendBroadcastMessage(sockfd);

if(ret < 0) {

perror("error");

Close();

exit(-1);

}

}

}

}

// 关闭服务

Close();

}

客户端类实现

需要的接口:

1)连接服务端connect()

2)退出连接close()

3)启动客户端Start()

Client.h

#ifndef CHATROOM_CLIENT_H

#define CHATROOM_CLIENT_H

#include

#include "Common.h"

using namespace std;

// 客户端类,用来连接服务器发送和接收消息

class Client {

public:

// 无参数构造函数

Client();

// 连接服务器

void Connect();

// 断开连接

void Close();

// 启动客户端

void Start();

private:

// 当前连接服务器端创建的socket

int sock;

// 当前进程ID

int pid;

// epoll_create创建后的返回值

int epfd;

// 创建管道,其中fd[0]用于父进程读,fd[1]用于子进程写

int pipe_fd[2];

// 表示客户端是否正常工作

bool isClientwork;

// 聊天信息

Msg msg;

//结构体要转换为字符串

char send_buf[BUF_SIZE];

char recv_buf[BUF_SIZE];

//用户连接的服务器 IP + port

struct sockaddr_in serverAddr;

};

Client.cpp

#include

#include "Client.h"

using namespace std;

// 客户端类成员函数

// 客户端类构造函数

Client::Client(){

// 初始化要连接的服务器地址和端口

serverAddr.sin_family = PF_INET;

serverAddr.sin_port = htons(SERVER_PORT);

serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);

// 初始化socket

sock = 0;

// 初始化进程号

pid = 0;

// 客户端状态

isClientwork = true;

// epool fd

epfd = 0;

}

// 连接服务器

void Client::Connect() {

cout << "Connect Server: " << SERVER_IP << " : " << SERVER_PORT << endl;

// 创建socket

sock = socket(PF_INET, SOCK_STREAM, 0);

if(sock < 0) {

perror("sock error");

exit(-1);

}

// 连接服务端

if(connect(sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {

perror("connect error");

exit(-1);

}

// 创建管道,其中fd[0]用于父进程读,fd[1]用于子进程写

if(pipe(pipe_fd) < 0) {

perror("pipe error");

exit(-1);

}

// 创建epoll

epfd = epoll_create(EPOLL_SIZE);

if(epfd < 0) {

perror("epfd error");

exit(-1);

}

//将sock和管道读端描述符都添加到内核事件表中

addfd(epfd, sock, true);

addfd(epfd, pipe_fd[0], true);

}

// 断开连接,清理并关闭文件描述符

void Client::Close() {

if(pid){

//关闭父进程的管道和sock

close(pipe_fd[0]);

close(sock);

}else{

//关闭子进程的管道

close(pipe_fd[1]);

}

}

// 启动客户端

void Client::Start() {

// epoll 事件队列

static struct epoll_event events[2];

// 连接服务器

Connect();

// 创建子进程

pid = fork();

// 如果创建子进程失败则退出

if(pid < 0) {

perror("fork error");

close(sock);

exit(-1);

} else if(pid == 0) {

// 进入子进程执行流程

//子进程负责写入管道,因此先关闭读端

close(pipe_fd[0]);

// 输入exit可以退出聊天室

cout << "Please input 'exit' to exit the chat room" << endl;

cout<<"\\ + ClientID to private chat "<<endl;

// 如果客户端运行正常则不断读取输入发送给服务端

while(isClientwork){

//清空结构体

memset(msg.content,0,sizeof(msg.content));

fgets(msg.content, BUF_SIZE, stdin);

// 客户输出exit,退出

if(strncasecmp(msg.content, EXIT, strlen(EXIT)) == 0){

isClientwork = 0;

}

// 子进程将信息写入管道

else {

//清空发送缓存

memset(send_buf,0,BUF_SIZE);

//结构体转换为字符串

memcpy(send_buf,&msg,sizeof(msg));

if( write(pipe_fd[1], send_buf, sizeof(send_buf)) < 0 ) {

perror("fork error");

exit(-1);

}

}

}

} else {

//pid > 0 父进程

//父进程负责读管道数据,因此先关闭写端

close(pipe_fd[1]);

// 主循环(epoll_wait)

while(isClientwork) {

int epoll_events_count = epoll_wait( epfd, events, 2, -1 );

//处理就绪事件

for(int i = 0; i < epoll_events_count ; ++i)

{

memset(recv_buf,0,sizeof(recv_buf));

//服务端发来消息

if(events[i].data.fd == sock)

{

//接受服务端广播消息

int ret = recv(sock, recv_buf, BUF_SIZE, 0);

//清空结构体

memset(&msg,0,sizeof(msg));

//将发来的消息转换为结构体

memcpy(&msg,recv_buf,sizeof(msg));

// ret= 0 服务端关闭

if(ret == 0) {

cout << "Server closed connection: " << sock << endl;

close(sock);

isClientwork = 0;

} else {

cout << msg.content << endl;

}

}

//子进程写入事件发生,父进程处理并发送服务端

else {

//父进程从管道中读取数据

int ret = read(events[i].data.fd, recv_buf, BUF_SIZE);

// ret = 0

if(ret == 0)

isClientwork = 0;

else {

// 将从管道中读取的字符串信息发送给服务端

send(sock, recv_buf, sizeof(recv_buf), 0);

}

}

}//for

}//while

}

// 退出进程

Close();

}

ClientMain.cpp

#include "Client.h"

// 客户端主函数

// 创建客户端对象后启动客户端

int main(int argc, char *argv[]) {

Client client;

client.Start();

return 0;

}

ServerMain.cpp

#include "Server.h"

// 服务端主函数

// 创建服务端对象后启动服务端

int main(int argc, char *argv[]) {

Server server;

server.Start();

return 0;

}

最后是Makefile 文件 对上面的文件进行编译

CC = g++

CFLAGS = -std=c++11

all: ClientMain.cpp ServerMain.cpp Server.o Client.o

$(CC) $(CFLAGS) ServerMain.cpp Server.o -o chatroom_server

$(CC) $(CFLAGS) ClientMain.cpp Client.o -o chatroom_client

Server.o: Server.cpp Server.h Common.h

$(CC) $(CFLAGS) -c Server.cpp

Client.o: Client.cpp Client.h Common.h

$(CC) $(CFLAGS) -c Client.cpp

clean:

rm -f *.o chatroom_server chatroom_client

注:需要C/C++ Linux服务器开发学习资料私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享

相关推荐

Python钩子函数实现事件驱动系统(created钩子函数)

钩子函数(HookFunction)是现代软件开发中一个重要的设计模式,它允许开发者在特定事件发生时自动执行预定义的代码。在Python生态系统中,钩子函数广泛应用于框架开发、插件系统、事件处理和中...

Python函数(python函数题库及答案)

定义和基本内容def函数名(传入参数):函数体return返回值注意:参数、返回值如果不需要,可以省略。函数必须先定义后使用。参数之间使用逗号进行分割,传入的时候,按照顺序传入...

Python技能:Pathlib面向对象操作路径,比os.path更现代!

在Python编程中,文件和目录的操作是日常中不可或缺的一部分。虽然,这么久以来,钢铁老豆也还是习惯性地使用os、shutil模块的函数式API,这两个模块虽然功能强大,但在某些情况下还是显得笨重,不...

使用Python实现智能物流系统优化与路径规划

阅读文章前辛苦您点下“关注”,方便讨论和分享,为了回馈您的支持,我将每日更新优质内容。在现代物流系统中,优化运输路径和提高配送效率是至关重要的。本文将介绍如何使用Python实现智能物流系统的优化与路...

Python if 语句的系统化学习路径(python里的if语句案例)

以下是针对Pythonif语句的系统化学习路径,从零基础到灵活应用分为4个阶段,包含具体练习项目和避坑指南:一、基础认知阶段(1-2天)目标:理解条件判断的逻辑本质核心语法结构if条件:...

[Python] FastAPI基础:Path路径参数用法解析与实例

查询query参数(上一篇)路径path参数(本篇)请求体body参数(下一篇)请求头header参数本篇项目目录结构:1.路径参数路径参数是URL地址的一部分,是必填的。路径参...

Python小案例55- os模块执行文件路径

在Python中,我们可以使用os模块来执行文件路径操作。os模块提供了许多函数,用于处理文件和目录路径。获取当前工作目录(CurrentWorkingDirectory,CWD):使用os....

python:os.path - 常用路径操作模块

应该是所有程序都需要用到的路径操作,不废话,直接开始以下是常用总结,当你想做路径相关时,首先应该想到的是这个模块,并知道这个模块有哪些主要功能,获取、分割、拼接、判断、获取文件属性。1、路径获取2、路...

原来如此:Python居然有6种模块路径搜索方式

点赞、收藏、加关注,下次找我不迷路当我们使用import语句导入模块时,Python是怎么找到这些模块的呢?今天我就带大家深入了解Python的6种模块路径搜索方式。一、Python模块...

每天10分钟,python进阶(25)(python进阶视频)

首先明确学习目标,今天的目标是继续python中实例开发项目--飞机大战今天任务进行面向对象版的飞机大战开发--游戏代码整编目标:完善整串代码,提供完整游戏代码历时25天,首先要看成品,坚持才有收获i...

python 打地鼠小游戏(打地鼠python程序设计说明)

给大家分享一段AI自动生成的代码(在这个游戏中,玩家需要在有限时间内打中尽可能多的出现在地图上的地鼠),由于我现在用的这个电脑没有安装sublime或pycharm等工具,所以还没有测试,有兴趣的朋友...

python线程之十:线程 threading 最终总结

小伙伴们,到今天threading模块彻底讲完。现在全面总结threading模块1、threading模块有自己的方法详细点击【threading模块的方法】threading模块:较低级...

Python信号处理实战:使用signal模块响应系统事件

信号是操作系统用来通知进程发生了某个事件的一种异步通信方式。在Python中,标准库的signal模块提供了处理这些系统信号的机制。信号通常由外部事件触发,例如用户按下Ctrl+C、子进程终止或系统资...

Python多线程:让程序 “多线作战” 的秘密武器

一、什么是多线程?在日常生活中,我们可以一边听音乐一边浏览新闻,这就是“多任务处理”。在Python编程里,多线程同样允许程序同时执行多个任务,从而提升程序的执行效率和响应速度。不过,Python...

用python写游戏之200行代码写个数字华容道

今天来分析一个益智游戏,数字华容道。当初对这个游戏颇有印象还是在最强大脑节目上面,何猷君以几十秒就完成了这个游戏。前几天写2048的时候,又想起了这个游戏,想着来研究一下。游戏玩法用尽量少的步数,尽量...

取消回复欢迎 发表评论: