我正在考虑解决我与聊天服务器和客户端之间的问题。
应该怎么做,客户端请求用户名,然后为连接请求用户回答[Y/N]。
当点击是,客户端必须连接到服务器,当它需要去一个单独的线程(处理多个客户端),但我的问题是,当多个用户加入(用户当前登录的用户名更改为最后一个谁加入了聊天。当这种情况发生时(服务器显示用户名,而在客户端屏幕上它消失,没有或所有奇怪的符号出现)。
我还需要帮助的是将消息分发到连接的其他客户端(不包括用户自己)
代码服务器:#include "stdafx.h"
long antwoord;
char chatname[100];
char bericht[498];
char sbericht[498];
using namespace std;
DWORD WINAPI SocketHandler(void*);
//our main function
void main()
{
//here we set the Winsock-DLL to start
WSAData wsaData;
WORD DLLVERSION;
DLLVERSION = MAKEWORD(2,1);
//here the Winsock-DLL will be started with WSAStartup
//version of the DLL
antwoord = WSAStartup(DLLVERSION, &wsaData);
if(antwoord != 0)
{
WSACleanup();
exit(1);
}
else
{
cout << "WSA started successfully" <<endl;
cout << "The status: n" << wsaData.szSystemStatus <<endl;
}
//the DLL is started
//structure of our socket is being created
SOCKADDR_IN addr;
//addr is our struct
int addrlen = sizeof(addr);
//socket sListen - will listen to incoming connections
SOCKET sListen;
//socket sConnect - will be operating if a connection is found.
SOCKET sConnect;
//setup of our sockets
//opgezocht op internet - AF_INET bekend dat het lid is van de internet familie
//Sock_STREAM betekenend dat onze socket een verbinding georiënteerde socket is.
sConnect = socket(AF_INET,SOCK_STREAM,NULL);
//now we have setup our struct
//inet_addr is our IP adres of our socket(it will be the localhost ip
//that will be 127.0.0.1
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
//retype of the family
addr.sin_family = AF_INET;
//now the server has the ip(127.0.0.1)
//and the port number (4444)
addr.sin_port = htons(4444);
//here we will define the setup for the sListen-socket
sListen = socket(AF_INET,SOCK_STREAM,NULL);
if (sConnect == INVALID_SOCKET)
{
cout << "Error at socket(): n" << WSAGetLastError() <<endl;
WSACleanup();
}
else
{
cout << "Connect socket() is OK!" <<endl;
}
if(sListen == INVALID_SOCKET)
{
cout << "Error at socket(): n" << WSAGetLastError() <<endl;
WSACleanup();
}
else
{
cout << "Listen socket() is OK!" <<endl;
}
//here the sListen-socket will be bind
//we say that the socket has the IP adress of (127.0.0.1) and is on port (4444)
//we let the socket become the struct "addr"
if(bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
cout << "bind() failed: n" << WSAGetLastError() <<endl;
WSACleanup();
exit(1);
}
else{
cout << "bind() is OK!" <<endl;
}
if(listen( sListen, 10) == -1 ){
cout << "Error listening %dn" << WSAGetLastError() <<endl;
}
//here we will tell what the server must do when a connection is found
//therefor we will create an endless loop
cout << "Waiting for a incoming connection..." <<endl;
//now we let the socket listen for incoming connections
//SOMAXCOMM heeft het nut dat het dan voordurend luisterd naar inkomende verbindingen zonder limiet
int* csock;
while(true)
{
csock = (int*)malloc(sizeof(int));
//if a connection is found: show the message!
if((*csock = accept(sListen, (SOCKADDR*)&addr, &addrlen))!= INVALID_SOCKET)
{
cout << "A Connection was found with :" << inet_ntoa(addr.sin_addr) <<endl;
antwoord = send(*csock, "Welcome to our chat:", 21,NULL);
CreateThread(0,0,&SocketHandler, (void*)csock , 0,0);
cout << *csock <<endl;
}
}
}
//sbericht is the message
DWORD WINAPI SocketHandler(void* lp)
{
int *csock = (int*)lp;
for(;;)
{
antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL);
antwoord = recv(*csock, chatname, sizeof(chatname), NULL);
while(antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL) && (antwoord = recv(*csock, sbericht, sizeof(sbericht), NULL)) )
{
printf("%s: "%s"n", chatname, sbericht);
antwoord = send(*csock, sbericht, sizeof(sbericht), NULL);
antwoord = send(*csock, chatname, sizeof(chatname), NULL);
}
return 0;
}
}
客户机代码:#include "stdafx.h"
using namespace std;
//our main function
int main()
{
//here we set the Winsock-DLL to start
string bevestiging;
char chatname[100];
char bericht[250];
char sbericht[250];
string strbericht;
string strsbericht;
long antwoord;
//here the Winsock-DLL will be started with WSAStartup
//version of the DLL
WSAData wsaData;
WORD DLLVERSION;
DLLVERSION = MAKEWORD(2,1);
antwoord = WSAStartup(DLLVERSION, &wsaData);
if(antwoord != 0)
{
exit(1);
}
else
{
cout << "WSA started successfully" <<endl;
cout << "The status: n" << wsaData.szSystemStatus <<endl;
}
SOCKADDR_IN addr;
int addrlen = sizeof(addr);
SOCKET sConnect;
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
if (sConnect == INVALID_SOCKET)
{
cout << "Error at socket(): n" << WSAGetLastError() <<endl;
}
else
{
cout << "socket() is OK!n" <<endl;
}
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(4444);
cout << "What is your chat name?" <<endl;
cin.getline(chatname, 100);
cout << "Do you want to connect to the server? [Y/N]" <<endl;
cin >> bevestiging;
if (bevestiging == "N")
{
exit(1);
}
else
{
if(bevestiging == "Y")
{
connect(sConnect, (SOCKADDR*)&addr, sizeof(addr));
antwoord = recv(sConnect, bericht, sizeof(bericht), NULL);
strbericht = bericht;
cout << strbericht << chatname <<endl;
while(true)
{
if(antwoord > 1)
{
cin.clear();
cin.sync();
cout << chatname << " :" <<endl;
cin.getline(sbericht, sizeof(sbericht));
antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL);
antwoord = send(sConnect, chatname, sizeof(chatname), NULL);
while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL)))
{
antwoord = recv(sConnect, sbericht, sizeof(sbericht), NULL);
antwoord = recv(sConnect, chatname, sizeof(chatname), NULL);
cout << chatname << ":" <<endl;
cout << sbericht <<endl;
cin.getline(sbericht, 250);
}
}
else
{
cout << "The connection to the server has been lost... n" << "please exit the client." <<endl;
}
}
对不起,如果我没有写好(我只是学习编程套接字),但我不能弄清楚这一点。所以不要对我太苛刻,我仍然需要学习,但我找不到我需要的东西。所以我想如果有人能告诉我怎么做,我就能明白它是怎么做的以及为什么。
总要学点东西(我现在也在忙beejee的网络编程教程)
sbericht和chatname—全局变量你的2个线程同时使用这个全局缓冲区所以一个线程重写了另一个线程的数据
这段代码有一些问题,但是套接字在开始时是一个棘手的事情。在您的服务器代码中,多线程似乎是您真正的野兽。注意,线程和套接字是非常不同的概念,但它们经常一起使用。一个大问题(正如Andrew所说)是你有竞争条件。如果跨多个线程写入全局变量,则需要使用互斥锁来确保互斥。例如,在SocketHandler
中,您需要保护变量antwoord
。此外,在c++中使用new
和delete
,而不是malloc()
和free()
(这些在C中使用)。另外,请注意,对于每个新连接,您都要覆盖csock
变量的值。您需要单独的变量来保存每个客户端打开的连接套接字。
服务器套接字基本上是这样工作的:socket()
, bind()
, listen()
, accept()
。现在,为了保持bind()
连接到的端口和listen()
打开更多连接,accept()
重新路由您的客户端并将它们放在另一个套接字文件描述符上(这是accept()
的返回值),以便您可以继续与此客户端通信。因此,对于每个客户端,您需要唯一地保存accept()
的值。
我怀疑,然而,在您的客户端代码中,您正在接收您所期望的。你的协议在某个地方保证它只发送用户名然后发送chatname吗?您可能只需要一个recv()
调用来检索所有数据。还有,下面这一行的相关性是什么?
while(antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL) && (antwoord = send(sConnect, sbericht, sizeof(sbericht), NULL)))
您只是做了两次相同的工作—一次应该足够了(也许程序可能在此时阻塞并在您不知情的情况下覆盖数据)。对于一个简单的文本协议,您很可能可以创建一个~512bytes的变量,并在对recv()
的一次调用中适当地从服务器接收所有信息。
我试着挑出你代码中的大问题,这些问题或多或少也经常出现在你代码的其他地方。如果您不熟悉多线程,请稍后再解决这个问题。先学习如何使用单线程套接字,然后再学习多线程。同时处理这两个问题会让你吃亏。好运!