我在VS 2010中使用C编写聊天应用程序(客户端和服务器)。
我已经完成了我的代码编写,如下所示,但仍然存在问题。 此问题是客户端未按时收到服务器发送的消息。
服务器代码:
#include <WinSock2.h>
#include <stdio.h>
#include <time.h>
main()
{
SOCKET ListeningSocket;
SOCKET AcceptSocket;
SOCKADDR_IN ServerAddr;
SOCKADDR_IN ClientAddr;
WSADATA wsaData;
const unsigned short PORT = 4444;
FD_SET fdread;
FD_SET BackUpfdread;
FD_SET fdwrite;
FD_SET BackUpfdwrite;
int maxDescriptor;
SOCKET SocketArray[20];
int index = 0;
int selectResults;
int i,k;
int clientAddrSize;
int RecvBytes;
int SentBytes;
char SentBuff[500];
char RecvBuff[500];
struct timeval timeout;
// Initialize Winsock2.2
WSAStartup(MAKEWORD(2,2),&wsaData);
// Initialize Listening Socket
ListeningSocket = socket(AF_INET,SOCK_STREAM,0);
// Initialize ServerAddr
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
ServerAddr.sin_port = htons(PORT);
// Bind the ServerAddr with ListeningSocket
bind(ListeningSocket,(SOCKADDR *)&ServerAddr,sizeof(ServerAddr));
// Listening Socket
listen(ListeningSocket,5);
FD_ZERO(&fdread);
FD_ZERO(&BackUpfdread);
FD_ZERO(&fdwrite);
FD_ZERO(&BackUpfdwrite);
// Asign ListeningSocket at fdread
FD_SET(ListeningSocket,&fdread);
maxDescriptor = ListeningSocket;
SocketArray[index] = ListeningSocket;
index++;
timeout.tv_sec = 0;
timeout.tv_usec = 0;
// Main loop starts here
for(; ;)
{
BackUpfdread = fdread;
BackUpfdwrite = fdwrite;
selectResults = select(maxDescriptor+1,&BackUpfdread,&BackUpfdwrite,NULL,&timeout);
//printf("server select() OK\n");
if(selectResults == -1)
{
printf("Select() error");
WSACleanup();
return 0;
}
for(i=0;i<=index-1;i++)
{
//printf("%d\n",SocketArray[i]);
if(FD_ISSET(SocketArray[i],&BackUpfdread))
{
if(SocketArray[i] == ListeningSocket) // we have a new connection
{
clientAddrSize = sizeof(ClientAddr);
AcceptSocket = accept(ListeningSocket,(SOCKADDR *)&ClientAddr,&clientAddrSize);
// Add the newest accepted socket to the fdread and fdwrite sets.
FD_SET(AcceptSocket,&fdread);
FD_SET(AcceptSocket,&fdwrite);
// Add the newest accepted socket into SocketArray
SocketArray[index] = AcceptSocket;
index++;
// keep track of the maxDescriptor.
if(AcceptSocket > maxDescriptor)
{
maxDescriptor = AcceptSocket;
}
printf("New connection from %s on socket %d\n", inet_ntoa(ClientAddr.sin_addr), AcceptSocket);
}else{ // That means that the socket is not from a new conection and has something sent.
memset(RecvBuff,0,sizeof(RecvBuff));
RecvBytes = recv(SocketArray[i], RecvBuff, sizeof(RecvBuff)-1, 0);
if(RecvBytes > 0) // Some data received.
{
printf("Message Sent.\n");
printf("Message was: %s\n",RecvBuff);
for(k=0;k<=index-1;k++)
{
if(SocketArray[k] != ListeningSocket && SocketArray[k] != SocketArray[i])
{
memset(SentBuff,0,sizeof(SentBuff));
strcpy(SentBuff,RecvBuff);
SentBytes = send(SocketArray[k],SentBuff,sizeof(SentBuff),0);
if(SentBytes > 0)
{
printf("Message Forwarded\n");
}else{
printf("Message forward error\n");
}
}
}
}
}
}
}
SleepEx(10, FALSE);
}// Main loop ends here
}
客户代码:
#include <WinSock2.h>
#include <stdio.h>
#include <time.h>
main()
{
SOCKET ConnectSocket;
SOCKET SocketArray[20];
SOCKADDR_IN ServerAddr;
WSADATA wsaData;
FD_SET fdwrite;
FD_SET fdread;
FD_SET BackUpfdread;
FD_SET BackUpfdwrite;
char server_address[20] = "192.168.1.4";
char SentBuff[500];
char RecvBuff[500];
const unsigned short PORT = 4444;
int maxDescriptor;
int index = 0;
int SelectResults;
int i;
int RecvBytes;
int SentBytes;
BOOL bOpt = TRUE;
struct timeval timeout;
// Initialize Winsock 2.2
WSAStartup(MAKEWORD(2,2),&wsaData);
// Initialize ServerAddr
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr(server_address);
ServerAddr.sin_port = htons(PORT);
// Create a new socket to make a client connection.
ConnectSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
setsockopt(ConnectSocket, IPPROTO_TCP, TCP_NODELAY,(char*)&bOpt,sizeof(BOOL));
// Clear the fd sets
FD_ZERO(&fdread);
FD_ZERO(&BackUpfdread);
FD_ZERO(&fdwrite);
FD_ZERO(&BackUpfdwrite);
// Asign ConnectSocket into fdread and fdwrite.
FD_SET(ConnectSocket,&fdread);
FD_SET(ConnectSocket,&fdwrite);
// Set timer
timeout.tv_sec = 0;
timeout.tv_usec = 0;
maxDescriptor = ConnectSocket;
SocketArray[index] = ConnectSocket;
index++;
// Make a connection to the server with socket s.
if(connect(ConnectSocket, (SOCKADDR *) &ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR)
{
printf("Couldn't connect to the server\n");
}
// Main loop starts here
for(; ;)
{
BackUpfdread = fdread;
BackUpfdwrite = fdwrite;
memset(SentBuff, 0, sizeof(SentBuff));
printf("Write: ");
gets_s(SentBuff, sizeof(SentBuff));
SelectResults = select(maxDescriptor+1,&BackUpfdread,&BackUpfdwrite,NULL,&timeout);
for(i=0;i<=index-1;i++)
{
// Something to read from server.
if(FD_ISSET(SocketArray[i],&BackUpfdread) && SocketArray[i] == ConnectSocket)
{
RecvBytes = recv(SocketArray[i], RecvBuff, sizeof(RecvBuff), 0);
if(RecvBytes > 0)
{
printf("%s\n",RecvBuff);
// Cleaning the Receive Buffer
memset(RecvBuff,0,sizeof(RecvBuff));
}
}
// Something to write.
if(FD_ISSET(SocketArray[i],&BackUpfdwrite) && SocketArray[i] == ConnectSocket)
{
SentBytes = send(SocketArray[i], SentBuff,sizeof(SentBuff),0);
// Cleaning the Sent Buffer
memset(SentBuff,0,sizeof(SentBuff));
}
}
SleepEx(10, FALSE);
} // Main menu ends here
}
在我看来,客户方似乎没有什么好处。我告诉这个,因为如果我使用telnet应用程序作为客户端消息传输正确。
有没有人知道如何解决这个问题?
由于
答案 0 :(得分:0)
一个疯狂的猜测:当你使用硬编码的ip-address:port tuple来访问服务器时,你确定它是正确的吗?
客户端2不“......必须写一些东西......”来接收,但在gets_s()
等待用户输入被阻止,因此{{1}不能因此不能select()
。
要解决此阻止问题,您可以选择两种方法。
1多线程解决方案(更便携)
更改客户端设计以异步处理服务器连接(写入并读取它),例如在主线程的不同线程中。
2单线程解决方案(非便携式)
2.1 Windows
您可以使用recv()
从控制台实现非阻塞读取。
例如,可以这样做:
ReadConsoleEvent()
2.2 UNIX
您可以使用/*
* Does a non-blocking read of characters from the stdin into the
* buffer referenced by 'pszBuffer' up to a maximum of 'sizeBuffer'.
*
* If bEcho is TRUE the characters read are echoed to the console window.
*
* Returns the characters read or a negative value on error.
*/
DWORD ReadConsoleNonBlocking(char * pszBuffer, size_t sizeBuffer, BOOL bEcho)
{
DWORD dwRC = 0;
static HANDLE stdinHandle = 0;
stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
{
if (!pszBuffer)
return -1;
if (!sizeBuffer)
return -2;
{
INPUT_RECORD input = {0};
DWORD NumberOfEventsRead = 0;
if (!ReadConsoleInput(stdinHandle, &input, 1, &NumberOfEventsRead))
return -3;
if (NumberOfEventsRead && (KEY_EVENT == input.EventType) && input.Event.KeyEvent.bKeyDown)
{
DWORD dwCharactersRead = 0;
while (input.Event.KeyEvent.wRepeatCount > dwCharactersRead && dwCharactersRead < sizeBuffer)
{
pszBuffer[dwCharactersRead] = input.Event.KeyEvent.uChar.AsciiChar;
if ('\r' == pszBuffer[dwCharactersRead])
pszBuffer[dwCharactersRead] = '\n';
if (bEcho)
printf("%c", pszBuffer[dwCharactersRead]); /* echo character read to console */
++ dwCharactersRead;
}
dwRC = dwCharactersRead;
}
}
}
return dwRC;
}
的结果来检索标准输入的文件描述符,并将其添加到传递给fileno(stdin)
的文件描述符集中,以便在通过控制台输入内容时收到通知。 / p>