//server端
#include<iostream.h>
#include<winsock2.h>
#define DEFAULT_PORT 4000
void WINAPI SendOut();
char info[255];
bool isConnect;
SOCKET sServSock= -1;
fd_set fds;
void main()
{
WORD wVersionRequested=MAKEWORD(2,2); //初始化winsock
WSADATA wsaData;
WSAStartup(wVersionRequested,&wsaData); SOCKET sServSock= socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

sockaddr_in addr;
int nSockErr;
int nNumConns=0;
SOCKET sClient;
sockaddr ConnAddr;
int nAddrLen=sizeof(sockaddr);
addr.sin_family=AF_INET;
addr.sin_port =htons(DEFAULT_PORT);
addr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(sServSock,(LPSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR)       //SOCKET_ERROR=-1
{
nSockErr=WSAGetLastError();
} if(listen(sServSock,5)==SOCKET_ERROR)
{
nSockErr=WSAGetLastError();
} //fd_set fds;
FD_ZERO(&fds);
FD_SET(sServSock,&fds); CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)SendOut,NULL,0,NULL); //发送数据 bool bRunning=1;
do{
fd_set readfds;
CopyMemory(&readfds,&fds,sizeof(fd_set));
if(select(0,&readfds,NULL,NULL,NULL)>0)
{
for(int i=readfds.fd_count-1;i>=0;i--)//顺序处理每一个有事件发生的套接字
{
if(readfds.fd_array[i]=sServSock)
{
sClient=accept(sServSock,&ConnAddr,&nAddrLen);
if(sClient==INVALID_SOCKET){
bRunning=false;
break;
}
cout<<"Client Accept Successfull."<<endl;
FD_SET(sClient,&fds); //将与新客户端对应的套接字加入select函数监视的集合
}
else//与客户端对应的套接字有事件发生说明有数据需要接收
{
char in[255];
sClient=readfds.fd_array[i];
if(recv(sClient,in,255,0)!=SOCKET_ERROR)
cout<<"\b<"<<in<<endl<<">";
else{
cout<<"\b<"<<"client exit"<<endl<<">";
FD_CLR(sClient,&fds); //将该套接字移出select集合
closesocket(sClient);
}
}
}
}
}while(bRunning);
WSACleanup();
}void WINAPI SendOut()
{
do
{
cin.getline(info,255);
if(!strcmp(info,"bye"))break;
cout<<">";
for(int i=fds.fd_count-1;i>=0;i--)
{
if(fds.fd_array[i]!=sServSock)
{
fd_set writefds;
FD_ZERO (&writefds);
FD_SET(fds.fd_array[i],&writefds); if(select(0,NULL,&writefds,NULL,NULL)>0)
send(fds.fd_array[i],info,255,0);
}
}
}while(true);

closesocket(sServSock);
}select模型实现一个服务器和多个客户端的简单winsock编程,客户端输入本机ip127.0.0.1,连接到服务器端,客户端在控制台输入字符,server端进行接受,并回显在控制台上。出现的问题是,可以连接,但是server无法回显从client接受的字符。

解决方案 »

  1.   

    //client端代码
    #include<windows.h>
    #include<iostream.h>
    #include<winsock.h>
    DWORD WINAPI ThreadFunc(LPVOID pParam);char info[255];
    #define DEFAULT_PORT 4000void main()
    {
    WORD wVersionRequested=MAKEWORD(2,2);
    WSADATA wsaData;
    WSAStartup(wVersionRequested,&wsaData);
    SOCKET sClntSock;
    sockaddr_in addr;
    sockaddr_in ServAddr;
    int nSockErr;
    sClntSock=socket(AF_INET,SOCK_STREAM,0); u_long arg=1; addr.sin_family=AF_INET;
    addr.sin_port=0;
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    if(bind(sClntSock,(LPSOCKADDR)&addr,sizeof(addr))==SOCKET_ERROR)
    {
    nSockErr=WSAGetLastError();
    }
    ServAddr.sin_family=AF_INET;
    ServAddr.sin_port=htons(DEFAULT_PORT);
    cout<<"Please input the ip of server:";
    char add_ip[50];
    cin>>add_ip;
    char dummy;
    cin.get(dummy); ServAddr.sin_addr.s_addr=inet_addr(add_ip);
    if(connect(sClntSock,(const sockaddr*)(&ServAddr),
    sizeof(ServAddr))==SOCKET_ERROR)
    {
    nSockErr=WSAGetLastError();
    }
    else cout<<"**********Connect successful.**********"<<endl;
    CreateThread(NULL,0,ThreadFunc,&sClntSock,0,NULL);
    cout<<">";
    cin.getline(info,255);
    while(strcmp(info,"bye"))
    {
    send(sClntSock,info,255,0);
    cout<<">";
    cin.getline(info,255);
    }
    }DWORD WINAPI ThreadFunc(LPVOID pParam)
    {
    SOCKET *s=(SOCKET *)pParam;
    char in[255];
    while(strcmp(info,"bye"))
    {
    if(recv(*s,in,255,0)!=SOCKET_ERROR)
    {
    cout<<"\b<"<<in<<endl<<">";
    cout.flush();
    }
    else
    {
    cout<<"**********Server Down**********"<<endl;
    exit(0);
    }
    }
    return 0;
    }
      

  2.   

    应该是server端的问题,下面这段应该是用来回显的,不知道为啥回显不出来,而其server只能向client发送一次
    else//与客户端对应的套接字有事件发生说明有数据需要接收
    {
             char in[255];
             sClient=readfds.fd_array[i];
    if(recv(sClient,in,255,0)!=SOCKET_ERROR)
    cout<<"\b<"<<in<<endl<<">";
    else{
    cout<<"\b<"<<"client exit"<<endl<<">";
    FD_CLR(sClient,&fds); //将该套接字移出select集合
    closesocket(sClient);
    }
    }
      

  3.   

    if(readfds.fd_array[i]=sServSock)
    --
    这里错误了,应该是if(readfds.fd_array[i]==sServSock)
      

  4.   

    用等于的话
    else//与客户端对应的套接字有事件发生说明有数据需要接收
    {
             char in[255];
             sClient=readfds.fd_array[i];
    if(recv(sClient,in,255,0)!=SOCKET_ERROR)
    cout<<"\b<"<<in<<endl<<">";
    else{
    cout<<"\b<"<<"client exit"<<endl<<">";
    FD_CLR(sClient,&fds); //将该套接字移出select集合
    closesocket(sClient);
    }
    }永远不会被执行哦.
      

  5.   

    竟然犯了这种低级错误,回显的问题是解决了,但为什么,server端只能发送一次,就再也不能输入了呢?
      

  6.   

    sever端发字符的代码在SendOut()函数里,为什么就是只能发一次呢?
      

  7.   

    不懂,是cin.getline()的问题么?
      

  8.   

    server端只能发送一次,就再也不能输入了呢?
    ---------
    在我这边都可以阿?!!!
    你不要看>符合,你直接输入就是了?我这里都是可以的.
      

  9.   

    ?我这边在server端输了一次以后,再敲字的话,server端的控制台根本没反应,也就是说按键盘,server端根本没响应,而client端可以一直发。
      

  10.   

    没有反应的时候,你在server端的sendout的do while 循环中加个断点试试.调试一下看问题在那里,我这里确实不会有问题.
      

  11.   

    设了断点以后,发现循环了一次i值就变成0了,所以就不能再输入了,我也不是很清楚select的含义,所以也不知道fds.fd_count的值是多少?
      

  12.   

    这就对了,那程序有没有在cin.getline()上阻塞?
      

  13.   

    因为fds.fd_count肯定是2,一个sServerSock,一个是你连接的socket.
    正常应该是在cin.getline()上阻塞的
      

  14.   

    那键盘应该可以使用的阿?那在main()的do while 循环中中断看看,看看是不是阻塞在select上.
      

  15.   

    我不知道你那边是什么情况下会出现这样,我这里试了N次都不会,都是可以发和收的,你具体的操作和环境如何??
    --
    PS:如果方便的话用QQ远程控制你那里看看到底你那里的环境如何.