c - 在 linux 上的多线程 c 服务器中创建新的文件描述符
问题描述
将我在学校学到的知识应用到一个爱好项目中,但我是网络编程的新手。我正在使用 Linux 上的 C 服务器设计一个简单的多用户文本游戏。我有一个简单的线程 C 回显服务器,它使用 NPTL(本机 POSIX 线程库)进行传入连接。我想通过在每个连接上打开一个新的文件描述符来避免竞争和死锁情况,这样他们就可以访问设备的数据库项目等等,而不必使用锁。我可以使用 select() 来轮询我的每个线程连接吗?这对最佳实践有意义吗?
我试图在 Beej 的网络编程指南中找到一个例子,但我做不到。
编辑:
这是我的回声服务器的基础知识:
// .. pick a port on startup
// initial socket creation
lsock = socket(AF_INET,SOCK_STREAM,0);
// ... error handling
sname.sin_family = AF_INET;
sname.sin_addr.s_addr = INADDR_ANY;
sname.sin_port = htons( port );
// binding the socket
while(client_sock=accept(lsock,(struct sockaddr*)&client,(socklen_t*)&c))
{
puts("New connection accepted");
pthread_t cthread;
new_sock = malloc(1);
*new_sock = client_sock;
if( pthread_create( &cthread , NULL , clientHandler , (void*) new_sock) < 0)
{
perror("Error: Thread creation");
return 1;
}
puts("Handler assigned");
}
void *clientHandler(void *socket_desc)
{
//Get the socket descriptor
int sock = *(int*)socket_desc;
int n;
char sendBuff[100], client_message[2000];
while((n=recv(sock,client_message,2000,0))>0)
{
send(sock,client_message,n,0);
}
close(sock);
if(n==0)
{
puts("Client disconnected");
}
else
{
perror("recv failed");
}
return 0;
}
这是我计划在下面的基本房间系统和播放器系统上在我的回声服务器上实施的计划:
struct pollfd pdata[MAXPLAY+1];
struct _player{
obj o;
int id,state,file_desc,roomNum;
void(*ip)(plr *p, char ch);
char name[16],op[MAXBUF],line[128];
struct pollfd *p;
int HP,ATK;
plr *target;
};
void init_player(player *p){
p->HP=10;
p->ATK=1;
p->target=NULL;
}
#define playerLloop(p) for(i=0,p=players;i<MAXPLAY;i++,p++)
if(p->state==0){
init_plr(p);
p->state=1;
p->name[0]=0;
p->file_desc=rsock; // file descriptor
p->roomNum=0;
// function to initialize names and add to linked list of players
p->ip=new_connect;
// function to send clients a message
sendp(p,"Welcome");
}
通过客户端处理程序初始化播放器后,我会将它们添加到带有文件描述符的链接列表中。我的问题会出现在这里;当我有两个单独的线程试图访问服务器上的对象时,避免任何类型的竞争或死锁情况的最佳方法是什么?我可以通过我的解析函数处理它以获取客户端输入来解决这个问题吗?
如果玩家一试图打开房间里的一扇门,玩家二会尝试打开同一扇门;当我通过线程连接读取两个客户端的文件描述符时,如何为这些套接字分配优先级?这可能会导致竞争或死锁情况吗?
我只是很难理解通过线程套接字读取信息的概念。
解决方案
我可以使用 select() 来轮询我的每个线程连接吗?
select()
用于通过单个线程有效地服务多个 I/O 通道。您在评论中澄清说您打算为每个客户提供一个单独的线程。在这种情况下,您不需要select
管理客户端连接:每个这样的连接都有一个专用的整个线程!
您似乎在提议,这些线程中的每一个都将另外获得它需要的所有其他资源,例如与底层数据库的连接。在这种情况下,是的,只要这些外部资源固有地支持您需要的那种并发访问,您就可以避免在自己的代码中使用锁来保护它们。
这对最佳实践有意义吗?
每个客户端线程模型是一种相当常见的模型。只要并发客户端的数量不太大,应该没问题。您将希望确保有一种方法可以清理在没有通知的情况下消失的客户端线程。“太大”的大小取决于每个线程实际执行的工作的细节,以及运行它的机器的特性。
如果并发客户端的数量增长到足够大,那么您将需要改变您的方法,但我认为您现在还不需要太担心这一点。保持代码干净和模块化并避免依赖线程本地数据将有助于在您达到需要时切换到不同的方法。
推荐阅读
- c# - WPF动态生成的TreeView,复选框不显示
- c# - EF Core 3.1 中的项目树
- google-apps-script - 谷歌表:导入库(CryptoJS)
- sql - Snowflake SQL 列出每小时的会话计数(按天和小时分组)
- excel - 生成唯一的双向数据对并用于在 Excel 中计数
- r - 如何应用 lm 或 glm 以外的模型来乘以插补数据?
- email - 使用 Applescript 接收规则条件电子邮件
- javascript - 下拉菜单中 SVG 的颜色变化
- python - 如何在第三维的中间创建一个全零和一个 1 的 PyTorch 张量?
- java - NullPointerException:尝试调用虚拟方法 'void android.widget.ListView.setAdapter 无法修复