DCN_Chat_Program/client.cpp
2025-06-09 17:07:34 +08:00

222 lines
6.6 KiB
C++

#include <winsock2.h>
#include <cstdio>
#include <string>
#include <thread>
#include <iostream>
#define DEFAULT_PORT 5019
#define DEFAULT_IP "127.0.0.1"
#define BUFFER_SIZE 256
#define MAX_CONNECT_ATTEMPT 10
void send_msg(SOCKET sock);
void recv_msg(SOCKET sock);
std::string name, msg;
int main(){
int attempts = 0;
struct hostent* hp;
struct sockaddr_in server_addr = {0};
SOCKET client_sock;
WSADATA wsaData;
const char* server_name = DEFAULT_IP;
unsigned short port = DEFAULT_PORT;
// Handle WSAStartup
if (WSAStartup(0x202, &wsaData) == SOCKET_ERROR){
fprintf(stderr, "WSAStartup failed with error %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
// Initialize an IPV4 address & port for the client
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
// Resolves host's IP address
if (isalpha(server_name[0])){
hp = gethostbyname(server_name);
if (hp == nullptr){
fprintf(stderr, "Cannot resolve address: %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
memcpy(&(server_addr.sin_addr), hp->h_addr, hp->h_length);
}
else{
server_addr.sin_addr.s_addr = inet_addr(server_name);
}
// Initialize a TCP socket for client
client_sock = socket(AF_INET, SOCK_STREAM, 0);
if (client_sock == INVALID_SOCKET){
fprintf(stderr, "socket() failed with error %d\n", WSAGetLastError());
WSACleanup();
return -1;
}
printf("Client connecting to: %s\n", hp->h_name);
// Connection retry strategy
while (attempts < MAX_CONNECT_ATTEMPT){
if (connect(client_sock, (struct sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
fprintf(stderr, "connect() failed with error %d, attempt %d\n", WSAGetLastError(), attempts + 1);
Sleep(1000);
attempts++;
}
else{
break;
}
}
if (attempts >= MAX_CONNECT_ATTEMPT) {
fprintf(stderr, "Failed to connect after %d attempts\n", attempts);
closesocket(client_sock);
WSACleanup();
return -1;
}
// Announce username to server
char szBuff[BUFFER_SIZE] = {0};
while(true){
printf("Pick a username: ");
std::getline(std::cin, name);
if (name.length() > 32 || name.length() < 3){
printf("Username shall have 3-32 characters. Please choose another one.\n");
continue;
}
std::string my_name = "#New Client:" + name;
send(client_sock, my_name.c_str(), my_name.length() + 1, 0);
int msg_len = recv(client_sock, szBuff, sizeof(szBuff)-1, 0);
printf("%s\n", szBuff);
if(msg_len == my_name.length() + 1){
break;
}
else{
name.clear();
continue;
}
}
printf("### Chat commands:\n");
printf("Chatroom Message: msg\n");
printf("Direct Message: @username msg\n");
printf("Group Message: @[Group name] msg\n");
printf("### Group commands:\n");
printf("Create Group: Group @[user1 user2...] Group name, password\n");
printf("Add into a Group: Group_add @Group name, password\n");
printf("Delete a Group: Group_del @Group name, password\n");
printf("Delete a Group Member: Group_delp @Group name, username, password\n");
printf("### Hints:\n");
printf("#clientList - Show the current online client list\n");
printf("#help - Show this help message\n");
printf("#quit - Quit the chat\n");
// Send & Recv messages
std::thread snd(send_msg, client_sock);
std::thread rcv(recv_msg, client_sock);
snd.join();
rcv.join();
shutdown(client_sock, SD_SEND);
closesocket(client_sock);
WSACleanup();
return 0;
}
void send_msg(SOCKET sock){
int msg_len;
char szBuff[BUFFER_SIZE] = {0};
while(true){
// Get user input
fgets(szBuff, sizeof(szBuff), stdin);
szBuff[strcspn(szBuff, "\n")] = '\0';
if((int)strlen(szBuff) == 0){
printf("You cannot send empty message!\n");
continue;
}
// Handle commands
if (std::string(szBuff) == "#help"){
printf("### Chat commands:\n");
printf("Chatroom Message: msg\n");
printf("Direct Message: @username msg\n");
printf("Group Message: @[Group name] msg\n");
printf("### Group commands:\n");
printf("Create Group: Group @[user1 user2...] Group name, password\n");
printf("Add into a Group: Group_add @Group name, password\n");
printf("Delete a Group: Group_del @Group name, password\n");
printf("Delete a Group Member: Group_delp @Group name, username, password\n");
printf("### Hints:\n");
printf("#clientList - Show the current online client list\n");
printf("#help - Show this help message\n");
printf("#quit - Quit the chat\n");
continue;
}
// Client List
if (std::string(szBuff) == "#clientList"){
msg = "[" + name + "] " + "#applyforclientList";
}
// Quit
else if (std::string(szBuff) == "#quit"){
closesocket(sock);
WSACleanup();
exit(0);
}
else{
msg = "[" + name + "] " + szBuff;
}
// Send input to server
msg_len = send(sock, msg.c_str(), msg.length() + 1, 0);
if (msg_len == SOCKET_ERROR){
fprintf(stderr, "send() failed with error %d\n", WSAGetLastError());
break;
}
// Check if server closed connection
if (msg_len == 0){
closesocket(sock);
printf("server closed connection\n");
break;
}
}
}
void recv_msg(SOCKET sock){
int msg_len;
char szBuff[BUFFER_SIZE + name.length() + 1];
while(true){
// Get respond from server
msg_len = recv(sock, szBuff, sizeof(szBuff)-1, 0);
if (msg_len == SOCKET_ERROR){
fprintf(stderr, "recv() failed with error %d\nProgram will be closed in 3s.\n", WSAGetLastError());
closesocket(sock);
Sleep(3000);
exit(-1);
}
// Check if server closed connection
if (msg_len == 0){
printf("server closed connection\nProgram will be closed in 3s.\n");
closesocket(sock);
Sleep(3000);
exit(-1);
}
// Display other user's messages
szBuff[msg_len] = '\0';
if(strcmp(szBuff, msg.c_str()) != 0){
printf("%s\n", szBuff);
}
}
}