앞에 client.cpp를 했기 때문에 server.cpp를 해보겠다. 처음에는 client쪽에서 모든 기능을 구현 할려 했지만 dm기능은 server쪽에서 구 현 하는게 편할거 같아서 server쪽에서 채팅내용 저장과 dm기능을 구현을 했다.
#pragma comment(lib, "ws2_32.lib")
#include <WinSock2.h> //소켓사용을 위한 헤더
#include < string>
#include < iostream>
#include < thread>
#include < vector>
#include < sstream>
#include <mysql/jdbc.h>
#define MAX_SIZE 1024
#define MAX_CLIENT 4
using std::cout;
using std::endl;
using std::string;
using std::cin;
using std::vector;
const string server = "tcp://127.0.0.1:3306";
const string username = "root";
const string password = "abc1234";
sql::mysql::MySQL_Driver driver;
sql::Connection con;
sql::Statement stmt;
sql::PreparedStatement pstmt;
sql::ResultSet* result;
struct SOCKET_INFO
{
SOCKET sck;
string user;
};
vector<SOCKET_INFO> sck_list;
SOCKET_INFO server_sock;
int client_count = 0;
void server_init(); //socket, bind,listen 함수가 들어있다
void add_client(); //accept함수
void send_msg(const char msg); //전체메시지 내용을 모든 접속한 인원한테 보내준다.
void recv_msg(int idx); //recv함수
void del_client(int idx); //채팅방을 나가면 clientlist에서 빠지는 역할을 한다.
void send_msg_dm(const char msg, string recv);
//dm메시지 대상자한테만 보내주는 함수
int main()
{
WSADATA wsa;
int code = WSAStartup(MAKEWORD(2, 2), &wsa);
if (!code)
{
//sql연결문
try {
driver = sql::mysql::get_mysql_driver_instance();
con = driver->connect(server, username, password);
}
catch (sql::SQLException& e) {
cout << "Could not connect to server. Error message: " << e.what() << endl;
exit(1);
}
con->setSchema("project");
//한글을 받아도 깨지지않게 해준다
stmt = con->createStatement();
stmt->execute("set names euckr");
if (stmt) { delete stmt; stmt = nullptr; }
server_init();
std::thread th1[MAX_CLIENT];
for (int i = 0; i < MAX_CLIENT; i++)
{
th1[i] = std::thread(add_client);
}
while (1)
{
//client에게 send를 받으면 내용을 buf에 받는다.
string text, msg = "";
std::getline(cin, text);
const char* buf = text.c_str();
msg = server_sock.user + ":" + buf;
send_msg(msg.c_str());
}
for (int i = 0; i < MAX_CLIENT; i++)
{
th1[i].join();
}
}
else
{
cout << "프로그램 종류. (Error Code:" << code << ")";
}
WSACleanup();
}
void server_init() {
server_sock.sck = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); //소켓 생성
SOCKADDR_IN server_addr = {};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//소켓의 주소를 할당한다.
bind(server_sock.sck, (sockaddr*)&server_addr, sizeof(server_addr));
//소켓을 활성화하한다.
listen(server_sock.sck, SOMAXCONN);
server_sock.user = "server";
cout << "open" << endl;
}
void add_client() {
SOCKADDR_IN addr = {};
int addrsize = sizeof(addr);
char buf[MAX_SIZE] = { };
ZeroMemory(&addr, addrsize);
SOCKET_INFO new_client = {};
//accept:클라이언트의 연결 요규를 들어올 때까지 대기한다.
new_client.sck = accept(server_sock.sck, (sockaddr*)&addr, &addrsize);
recv(new_client.sck, buf, MAX_SIZE, 0);
new_client.user = string(buf);
string msg = new_client.user + "님이 입장했습니다";
cout << msg << endl;
sck_list.push_back(new_client);
std::thread th(recv_msg, client_count);
client_count++;
cout << "현재 인원수:" << client_count << "명" << endl;
send_msg(msg.c_str());
th.join();
}
//전체 채팅 내용을 모든 유저 한테 보내주는 함수
void send_msg(const char* msg) {
for (int i = 0; i < client_count; i++)
{
send(sck_list[i].sck, msg, MAX_SIZE, 0);
}
}
//recv를 받아 user들중 똑같은 id를 가지 사람한테만 보내준다.(dm기능)
void send_msg_dm(const char* msg, string recv) {
for (int i = 0; i < client_count; i++)
{
if (sck_list[i].user == recv) {
send(sck_list[i].sck, msg, MAX_SIZE, 0);
break;
}
}
}
void recv_msg(int idx) {
char buf[MAX_SIZE] = { };
string msg = "";
while (1)
{
ZeroMemory(&buf, MAX_SIZE);
if (recv(sck_list[idx].sck, buf, MAX_SIZE, 0) > 0)
{
cout << sck_list[idx].user + ":" + buf << endl;
string msg1 = buf; //dm기능이 ->로 시작하기 때문에 msg로 하면 앞에 유저이름까지 나오기 때문에 순수 채팅 내용만 필요하기 때문에 만든 변수
msg = sck_list[idx].user + ":" + buf;
// string ss;
string to;
if (msg1.substr(0, 2) == "->") {
std::stringstream ss(msg);
// 공백 기준 두번째 단어 to에 저장, 임시로 msg에 ':' 저장 여기서 to는 받는 사람의 아이디 이다.
ss >> to >> to ;
// 나머지 문자열 msg에 저장
std::getline(ss, msg);
if (buf != "") {
//dm메시지를 저장하기 위한 테이블에 내용을 넣는 쿼리문
pstmt = con->prepareStatement("insert into direct_msg(send_id,recv_id,msg) values(?,?,?)");
pstmt->setString(1, sck_list[idx].user);
pstmt->setString(2, to);
pstmt->setString(3, msg);
pstmt->execute();
}
string dm="";
dm = "(1:1 msg)" + to + ":" + msg;
send_msg_dm(dm.c_str(), to);
}
else
{ //->가 없으면 일반 chatting테이블에 내용을 저장한다.
if (buf != "") {
pstmt = con->prepareStatement("insert into chatting(id, chat) values(?,?)");
pstmt->setString(1, sck_list[idx].user);
pstmt->setString(2, buf);
pstmt->execute();
}
send_msg(msg.c_str());
}
}
else
{
msg = sck_list[idx].user + "님이 퇴장했습니다";
cout << msg << endl;
send_msg(msg.c_str());
del_client(idx);
return;
}
}
}
//유저가 퇴장시 소켓을 닫아주는 역할을 한다.
void del_client(int idx) {
closesocket(sck_list[idx].sck);
client_count--;
}

로그인 하고 채팅을 하면 전체 채팅으로 전체가 볼수 있다. 하지만 -> "상대방id 내용 을 작성 하면 상대 한테만 (1:1 msg)로 메시지가 보인다.