c++多客户端/服务器聊天
我陷入了谷底,正在思考聊天服务器和客户端遇到的问题的解决方案。
应该做什么,客户端请求用户名,然后向用户发出连接请求并回答 [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 %d\n" << 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的网络编程教程)。
I am hitting rock bottom, thinking about a solution for a problem I am having with my chat server and client.
What is supposed to do, client asks for a username, then for a connection request to the user with answer [Y/N].
When hitting yes, client has to connect to server, when it does it needs to go in a separate thread( for handling multiple clients (but my problem is, when more then one user joins( the username of the user current logged in is changed to the last one who joined the chat.
While that is happening (the server shows the username, while on the client screen it disappears and none or all weird signs appear).
What I need also help with is the distribution of the messages to the other clients connected (excluding the user himself)
Code Server:
#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 %d\n" << 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;
}
}
Client code:
#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;
}
}
Sorry if I didn't write it well (I'm just learning to program sockets) but I can't figure this one out. So don't be hard on me, I still need to learn but cant find the things I need. So I think if somebody can show me how to do it, I can see it how its done and why.
Always learn something(I'm currently also busy with beejee's network programming tutorial).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
sbericht 和 chatname - 全局变量
您的 2 个线程同时使用此全局缓冲区
所以一个线程重写了另一个线程的数据
sbericht and chatname - global variables
Your 2 threads working with this global buffers at the same time
So one thread rewrites data of another thread
这段代码存在一些问题,但是当您开始时,套接字是一个很难理解的东西。在您的服务器代码中,多线程似乎是您真正的野兽。请注意,线程和套接字是非常不同的概念,但它们经常一起使用。一个大问题(正如安德鲁所说)是你有竞争条件。如果要跨多个线程写入全局变量,则需要使用互斥体来确保互斥。例如,在
SocketHandler
中,您需要保护变量antwoord
。此外,在 C++ 中使用new
和delete
而不是malloc()
和free()
(这些用于C)。另请注意,对于每个新连接,您都会覆盖csock
变量的值。您需要单独的变量来保存每个客户端的打开连接套接字。服务器套接字基本上以这种方式工作:
socket()
、bind()
、listen()
、accept()
>。现在,为了保持您bind()
连接的端口和listen()
打开以获取更多连接,accept()< /code> 重新路由您的客户端并将它们放在另一个套接字文件描述符上(这是
accept()
的返回值),以便您可以继续与该客户端通信。因此,对于每个客户端,您需要唯一地保存accept()
的值。然而,我怀疑在您的客户端代码中,您是否收到了您所期望的结果。您的协议是否保证它只会发送用户名,然后发送聊天名?您可能只需要一次
recv()
调用即可检索所有数据。另外,以下行的相关性是什么?您只需执行两次相同的工作 - 一次就足够了(也许程序可能会在此时阻塞并在您不知情的情况下覆盖数据)。对于简单的文本协议,您很可能可以创建一个 ~512bytes 的变量,并在一次调用
recv()
中适当地从服务器接收所有信息。我试图找出代码中的大问题,这些问题或多或少在代码的其他区域也很常见。如果您不熟悉多线程,请稍后解决该问题。学习如何做单线程套接字,然后去学习多线程。同时解决这两个问题会让你很痛苦。祝你好运!
There are a few issues with this code but sockets are a sticky thing to wrap your head around when you start. It seems in your server code that multithreading is your real beast. Note that threads and sockets are very different concepts but they are often used together. A large issue (as Andrew said) is that you have race conditions. You need to use mutexes to ensure mutual exclusion if you are writing to your global variables across multiple threads. For instance, in
SocketHandler
you need to protect your variableantwoord
. Furthermore, usenew
anddelete
in C++ rather thanmalloc()
andfree()
(these are used in C). Also, notice that for every new connection you overwrite the value of yourcsock
variable. You need separate variables to hold each client's open connection socket.Server sockets basically work in this manner:
socket()
,bind()
,listen()
,accept()
. Now, in order to keep the port you arebind()
'ed to and arelisten()
'ing on open for more connections,accept()
reroutes your client and puts them on another socket file descriptor (which is the return value ofaccept()
) so that you can continue to communicate with this client. Therefore, for each client, you need to hold the value ofaccept()
uniquely.I am skeptical, however, that in your client code, you are receiving what you expect. Does your protocol somewhere guarantee it will send just the username and then the chatname? You may simply need a single
recv()
call to retrieve all your data. Also, what is the relevance of the following line?You are simply doing the same work twice - one time should be sufficient (and perhaps the program may block at this point and overwrite data without you knowing). For a simple text protocol, it is very likely that you can create a variable ~512bytes and receive all the information from the server appropriately in a single call to
recv()
.I tried to pick out the large issues in your code, and these problems are, more or less, frequent in other areas of your code too. If you are not familiar with multithreading, attack that problem later. Learn how to do single-threaded sockets, then go and learn multithreading. Tackling them both at once is going to bite you. Good luck!