问个问题吧

huxuanhui 2009-06-04 10:29:03
最近在搞一个c/s系统
目前做的模块是

通信模块

服务端 实现了一个windos服务
该服务可以侦听 服务器机器指定的端口

然后这里有了困惑

就是 处理多个用户要用多线程

我想知道

accept这里如何多线程
...全文
232 42 打赏 收藏 转发到动态 举报
写回复
用AI写文章
42 条回复
切换为时间正序
请发表友善的回复…
发表回复
huxuanhui 2009-06-05
  • 打赏
  • 举报
回复
[Quote=引用 41 楼 pathuang68 的回复:]
As promised, the VC project was already sent out you! enjoy it:)
[/Quote]

thank you
huxuanhui 2009-06-05
  • 打赏
  • 举报
回复
揭帖先

分不够我再开个贴
pathuang68 2009-06-05
  • 打赏
  • 举报
回复
As promised, the VC project was already sent out you! enjoy it:)
liubuweiright 2009-06-04
  • 打赏
  • 举报
回复
不懂,帮顶


能不能自定义一个自己的协议
zjw6861982 2009-06-04
  • 打赏
  • 举报
回复
顶,过儿
huxuanhui 2009-06-04
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 fetag 的回复:]
问的人和回的人都是满足跑火车...

你都说了,用accept()监听,啥叫监听?不阻塞你怎么监听?基本上百分之九十八的网络程序,server端都是被

动连接的,主动连接的只有在流媒体领域有偶尔的应用。

多线程的实现,是在accept()以后,创建一个已连接的描述符,然后再创一个子进程出来,这样子进程就从父进程

那里集成了这个已连接的描述符,然后父进程close()掉描述符,继续监听;而子进程就会继续通信了。

我借…
[/Quote]

我看到有人写了代码
并没有while
listen..
完了 就是 启动一个 accept 线程
让线程去accept
djh512 2009-06-04
  • 打赏
  • 举报
回复
问题 看不懂 关注
liuka 2009-06-04
  • 打赏
  • 举报
回复
大概这个样
while(1)
{
creatthread()
}
独孤过儿 2009-06-04
  • 打赏
  • 举报
回复
还有,十楼贴的,四个字总结“IO复用”,根本不是用在这个问题上的!
独孤过儿 2009-06-04
  • 打赏
  • 举报
回复
问的人和回的人都是满足跑火车...

你都说了,用accept()监听,啥叫监听?不阻塞你怎么监听?基本上百分之九十八的网络程序,server端都是被

动连接的,主动连接的只有在流媒体领域有偶尔的应用。

多线程的实现,是在accept()以后,创建一个已连接的描述符,然后再创一个子进程出来,这样子进程就从父进程

那里集成了这个已连接的描述符,然后父进程close()掉描述符,继续监听;而子进程就会继续通信了。

我借上面的代码改一下:

socket();
bind();
listen();
while(1)
{
fd=accept();
if ( fork()==0 ) // 子进程
{
recv();
send();
close(fd);
}
else // 父进程
close(fd);
}



huxuanhui 2009-06-04
  • 打赏
  • 举报
回复
10楼

晚上我试试

如果你的线程池能跑

我就把分全给你

我也想搞线程池

不过不想用类

和 mfc
fibbery 2009-06-04
  • 打赏
  • 举报
回复
其实你的问题很简单,不知道为什么搞得这么复杂。如果用并行模型来来解释的话,可以说是MTMD多线程多数据流。你喜欢线程池也没有问题。
每一个线程可以访问一个独立的队列,队列元素为连接描述符。队列为空时线程等待。

可以使用boss-worker模型。
boss线程accept客户端连接,将连接的描述符添加到线程队列。

zhy19870722 2009-06-04
  • 打赏
  • 举报
回复
汗,顶楼上,专业
pathuang68 2009-06-04
  • 打赏
  • 举报
回复
建议楼主用IOCP。说明和参考代码如下:
异步IO、APC、IO完成端口、线程池与高性能服务器之一 异步IO

背景:轮询 PIO DMA 中断
早期IO设备的速度与CPU相比,还不是太悬殊。CPU定时轮询一遍IO设备,看看有无处理要求,有则加以处理,完成后返回继续工作。至今,软盘驱动器还保留着这种轮询工作方式。

随着CPU性能的迅速提高,这种效率低下的工作方式浪费了大量的CPU时间。因此,中断工作方式开始成为普遍采用的技术。这种技术使得IO设备在需要得到服务时,能够产生一个硬件中断,迫使CPU放弃目前的处理任务,进入特定的中断服务过程,中断服务完成后,再继续原先的处理。这样一来,IO设备和CPU可以同时进行处理,从而避免了CPU等待IO完成。

早期数据的传输方式主要是PIO(程控IO)方式。通过对IO地址编程方式的方式来传输数据。比如串行口,软件每次往串行口上写一个字节数据,串口设备完成传输任务后,将会产生一个中断,然后软件再次重复直到全部数据发送完成。性能更好的硬件设备提供一个FIFO(先进先出缓冲部件),可以让软件一次传输更多的字节。

显然,使用PIO方式对于高速IO设备来说,还是占用了太多的CPU时间(因为需要通过CPU编程控制传输)。而DMA(直接内存访问)方式能够极大地减少CPU处理时间。CPU仅需告诉DMA控制器数据块的起始地址和大小,以后DMA控制器就可以自行在内存和设备之间传输数据,其间CPU可以处理其他任务。数据传输完毕后将会产生一个中断。

同步文件IO和异步文件IO
有两种类型的文件IO同步:同步文件IO和异步文件IO。异步文件IO也就是重叠IO。
在同步文件IO中,线程启动一个IO操作然后就立即进入等待状态,直到IO操作完成后才醒来继续执行。而异步文件IO方式中,线程发送一个IO请求到内核,然后继续处理其他的事情,内核完成IO请求后,将会通知线程IO操作完成了。

如果IO请求需要大量时间执行的话,异步文件IO方式可以显著提高效率,因为在线程等待的这段时间内,CPU将会调度其他线程进行执行,如果没有其他线程需要执行的话,这段时间将会浪费掉(可能会调度操作系统的零页线程)。如果IO请求操作很快,用异步IO方式反而还低效,还不如用同步IO方式。

同步IO在同一时刻只允许一个IO操作,也就是说对于同一个文件句柄的IO操作是序列化的,即使使用两个线程也不能同时对同一个文件句柄同时发出读写操作。重叠IO允许一个或多个线程同时发出IO请求。

异步IO在请求完成时,通过将文件句柄设为有信号状态来通知应用程序,或者应用程序通过GetOverlappedResult察看IO请求是否完成,也可以通过一个事件对象来通知应用程序。

异步IO、APC、IO完成端口、线程池与高性能服务器之二 APC
Alertable IO(告警IO)提供了更有效的异步通知形式。ReadFileEx / WriteFileEx在发出IO请求的同时,提供一个回调函数(APC过程),当IO请求完成后,一旦线程进入可告警状态,回调函数将会执行。
以下五个函数能够使线程进入告警状态:
SleepEx
WaitForSingleObjectEx
WaitForMultipleObjectsEx
SignalObjectAndWait
MsgWaitForMultipleObjectsEx
线程进入告警状态时,内核将会检查线程的APC队列,如果队列中有APC,将会按FIFO方式依次执行。如果队列为空,线程将会挂起等待事件对象。以后的某个时刻,一旦APC进入队列,线程将会被唤醒执行APC,同时等待函数返回WAIT_IO_COMPLETION。

QueueUserAPC可以用来人为投递APC,只要目标线程处于告警状态时,APC就能够得到执行。

使用告警IO的主要缺点是发出IO请求的线程也必须是处理结果的线程,如果一个线程退出时还有未完成的IO请求,那么应用程序将永远丢失IO完成通知。然而以后我们将会看到IO完成端口没有这个限制。
下面的代码演示了QueueUserAPC的用法。


/************************************************************************/
/* APC Test. */
/************************************************************************/

DWORD WINAPI WorkThread(PVOID pParam)
{
HANDLE Event = (HANDLE)pParam;

for(;;)
{
DWORD dwRet = WaitForSingleObjectEx(Event, INFINITE, TRUE);
if(dwRet == WAIT_OBJECT_0)
break;
else if(dwRet == WAIT_IO_COMPLETION)
printf("WAIT_IO_COMPLETION\n");
}

return 0;
}

VOID CALLBACK APCProc(DWORD dwParam)
{
printf("%s", (PVOID)dwParam);
}

void TestAPC(BOOL bFast)
{
HANDLE QuitEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

HANDLE hThread = CreateThread(NULL,
0,
WorkThread,
(PVOID)QuitEvent,
0,
NULL);

Sleep(100); // Wait for WorkThread initialized.

for(int i=5; i>0; i--)
{
QueueUserAPC(APCProc, hThread, (DWORD)(PVOID)"APC here\n");

if(!bFast)
Sleep(1000);
}

SetEvent(QuitEvent);
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
}


异步IO、APC、IO完成端口、线程池与高性能服务器之三 IO完成端口
IO完成端口
下面摘抄于MSDN《I/O Completion Ports》,smallfool翻译,原文请参考CSDN文档中心文章《I/O Completion Ports》, http://dev.csdn.net/Develop/article/29%5C29240.shtm 。
I/O完成端口是一种机制,通过这个机制,应用程序在启动时会首先创建一个线程池,然后该应用程序使用线程池处理异步I/O请求。这些线程被创建的唯一目的就是用于处理I/O请求。对于处理大量并发异步I/O请求的应用程序来说,相比于在I/O请求发生时创建线程来说,使用完成端口(s)它就可以做的更快且更有效率。

CreateIoCompletionPort函数会使一个I/O完成端口与一个或多个文件句柄发生关联。当与一个完成端口相关的文件句柄上启动的异步I/O操作完成时,一个I/O完成包就会进入到该完成端口的队列中。对于多个文件句柄来说,这种机制可以用来把多文件句柄的同步点放在单个对象中。(言下之意,如果我们需要对每个句柄文件进行同步,一般而言我们需要多个对象(如:Event来同步),而我们使用IO Complete Port 来实现异步操作,我们可以同多个文件相关联,每当一个文件中的异步操作完成,就会把一个complete package放到队列中,这样我们就可以使用这个来完成所有文件句柄的同步)

调用GetQueuedCompletionStatus函数,某个线程就会等待一个完成包进入到完成端口的队列中,而不是直接等待异步I/O请求完成。线程(们)就会阻塞于它们的运行在完成端口(按照后进先出队列顺序的被释放)。这就意味着当一个完成包进入到完成端口的队列中时,系统会释放最近被阻塞在该完成端口的线程。
调用GetQueuedCompletionStatus,线程就会将会与某个指定的完成端口建立联系,一直延续其该线程的存在周期,或被指定了不同的完成端口,或者释放了与完成端口的联系。一个线程只能与最多不超过一个的完成端口发生联系。

完成端口最重要的特性就是并发量。完成端口的并发量可以在创建该完成端口时指定。该并发量限制了与该完成端口相关联的可运行线程的数目。当与该完成端口相关联的可运行线程的总数目达到了该并发量,系统就会阻塞任何与该完成端口相关联的后续线程的执行,直到与该完成端口相关联的可运行线程数目下降到小于该并发量为止。最有效的假想是发生在有完成包在队列中等待,而没有等待被满足,因为此时完成端口达到了其并发量的极限。此时,一个正在运行中的线程调用GetQueuedCompletionStatus时,它就会立刻从队列中取走该完成包。这样就不存在着环境的切换,因为该处于运行中的线程就会连续不断地从队列中取走完成包,而其他的线程就不能运行了。

对于并发量最好的挑选值就是您计算机中CPU的数目。如果您的事务处理需要一个漫长的计算时间,一个比较大的并发量可以允许更多线程来运行。虽然完成每个事务处理需要花费更长的时间,但更多的事务可以同时被处理。对于应用程序来说,很容易通过测试并发量来获得最好的效果。

PostQueuedCompletionStatus函数允许应用程序可以针对自定义的专用I/O完成包进行排队,而无需启动一个异步I/O操作。这点对于通知外部事件的工作者线程来说很有用。

在没有更多的引用针对某个完成端口时,需要释放该完成端口。该完成端口句柄以及与该完成端口相关联的所有文件句柄都需要被释放。调用CloseHandle可以释放完成端口的句柄。

下面的代码利用IO完成端口做了一个简单的线程池。

/************************************************************************/
/* Test IOCompletePort. */
/************************************************************************/

DWORD WINAPI IOCPWorkThread(PVOID pParam)
{
HANDLE CompletePort = (HANDLE)pParam;
PVOID UserParam;
WORK_ITEM_PROC UserProc;
LPOVERLAPPED pOverlapped;

for(;;)
{
BOOL bRet = GetQueuedCompletionStatus(
CompletePort,
(LPDWORD)&UserParam,
(LPDWORD)&UserProc,
&pOverlapped,
INFINITE);

_ASSERT(bRet);

if(UserProc == NULL) // Quit signal.
break;

// execute user's proc.
UserProc(UserParam);
}

return 0;
}

void TestIOCompletePort(BOOL bWaitMode, LONG ThreadNum)
{
HANDLE CompletePort;
OVERLAPPED Overlapped = {0, 0, 0, 0, NULL};

CompletePort = CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
NULL,
NULL,
0);

// Create threads.
for(int i=0; i<ThreadNum; i++)
{
HANDLE hThread = CreateThread(NULL,
0,
IOCPWorkThread,
CompletePort,
0,
NULL);

CloseHandle(hThread);
}


CompleteEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
BeginTime = GetTickCount();
ItemCount = 20;

for(i=0; i<20; i++)
{
PostQueuedCompletionStatus(
CompletePort,
(DWORD)bWaitMode,
(DWORD)UserProc1,
&Overlapped);
}

WaitForSingleObject(CompleteEvent, INFINITE);
CloseHandle(CompleteEvent);


// Destroy all threads.
for(i=0; i<ThreadNum; i++)
{
PostQueuedCompletionStatus(
CompletePort,
NULL,
NULL,
&Overlapped);
}

Sleep(1000); // wait all thread exit.

CloseHandle(CompletePort);
}
fibbery 2009-06-04
  • 打赏
  • 举报
回复
accept也要非阻塞?

你创建完了工作线程,工作线程就已经开始并发工作了,你的监听线程返回继续accept等待新的连接。
简单的代码模式:
while(true)
{
fd=accept();
create_thread(func,fd);//线程开始执行
}
coverallwangp 2009-06-04
  • 打赏
  • 举报
回复

1.使用select实现非阻塞socket


# -*- coding: cp936 -*-
"""
非阻塞socket的使用(此程序在ubuntu linux和windows xp上测试,Windows可以支持select.select)
监控socket的三个list:in/out/err
程序以5000ms的时间长度为间隔,如果有客户端的请求,接收连接并进行显示;如果没有的话,
每隔5000ms显示一次"no data coming"
"""
import socket,select
host = ""
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((host,port))
s.listen(5)
while 1:
infds,outfds,errfds = select.select([s,],[],[],5)
# 如果infds状态改变,进行处理,否则不予理会
if len(infds) != 0:
clientsock,clientaddr = s.accept()
buf = clientsock.recv(8196)
if len(buf) != 0:
print (buf)
clientsock.close()
print "no data coming"
2.客户端程序与poll客户端基本一致,仅用来发送一字符串
# -*- coding: cp936 -*-
"""
向服务器端发送字符串
"""
import socket,select
host = "localhost"
port = 50000
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((host,port))
s.send("coming from select client")
s.close()
zhan1094 2009-06-04
  • 打赏
  • 举报
回复
关注······
coverallwangp 2009-06-04
  • 打赏
  • 举报
回复
非阻塞的不用accept,用
WSAAsyncSelect
The Windows Sockets WSAAsyncSelect function requests Windows message-based notification of network events for a socket.

int WSAAsyncSelect (
SOCKET s,
HWND hWnd,
unsigned int wMsg,
long lEvent
);


huxuanhui 2009-06-04
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 zhkjoy 的回复:]
socket();
bind();
listen();
while(1)
{
fd=accept();
recv();
send();
close(fd);
}


accept之后才真正建立连接
多个请求时,服务器socket是并行建立连接,还是串行建立连接 取决于你写的程序
你的代码是串行建立连接
[/Quote]

我就是想知道 如何并行建立链接?
zhkjoy 2009-06-04
  • 打赏
  • 举报
回复
socket();
bind();
listen();
while(1)
{
fd=accept();
recv();
send();
close(fd);
}


accept之后才真正建立连接
多个请求时,服务器socket是并行建立连接,还是串行建立连接 取决于你写的程序
你的代码是串行建立连接
加载更多回复(22)

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧