//server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int serv_sock;
int clnt_sock;
char message[BUF_SIZE];
int str_len, i;
struct sockaddr_in serv_adr;
struct sockaddr_in clnt_adr;
socklen_t clnt_adr_sz;
if (argc != 2)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
if (serv_sock == -1)
{
error_handling("socket() error");
}
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
{
error_handling("bind() error");
}
if (listen(serv_sock, 5) == -1)
{
error_handling("listen() error");
}
clnt_adr_sz = sizeof(clnt_adr);
for (i = 0; i < 5; i++)
{
clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
if (clnt_sock == -1)
{
error_handling("accept() error");
}
else
{
printf("Connected client %d \n", i + 1);
}
while ((str_len = read(clnt_sock, message, BUF_SIZE)) != 0)
{
write(clnt_sock, message, str_len);
}
close(clnt_sock);
}
close(serv_sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
//client.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024
void error_handling(char *message);
int main(int argc, char *argv[])
{
int sock;
char message[BUF_SIZE];
int str_len, recv_len, recv_cnt;
struct sockaddr_in serv_adr;
if (argc != 3)
{
printf("Usage : %s <port>\n", argv[0]);
exit(1);
}
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
error_handling("socket() error");
}
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = inet_addr(argv[1]);
serv_adr.sin_port = htons(atoi(argv[2]));
if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)
{
error_handling("connect() error");
}
else
{
puts("Connected...........");
}
while (1)
{
fputs("Input message(Q to quit): ", stdout);
fgets(message, BUF_SIZE, stdin);
if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))
break;
str_len = write(sock, message, strlen(message));
recv_len = 0;
while (recv_len < str_len)
{
recv_cnt = read(sock, &message[recv_len], BUF_SIZE - 1);
if (recv_cnt == -1)
error_handling("read() error!");
recv_len += recv_cnt;
}
message[recv_len] = 0;
printf("Message from server: %s", message);
}
close(sock);
return 0;
}
void error_handling(char *message)
{
fputs(message, stderr);
fputc('\n', stderr);
exit(1);
}
클라이언트 코드를 먼저 작동시키면 에러가 발생하는 이유
다중 액세스가 작동하지 않는 이유
for (i = 0; i < 5; i++)
{
clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
...
while ((str_len = read(clnt_sock, message, BUF_SIZE)) > 0) {
write(clnt_sock, message, str_len);
}
close(clnt_sock);
}
여러 클라이언트 처리를 위한 솔루션
멀티스레딩: 각 클라이언트 연결에 대해 새 스레드를 만듭니다. 이를 통해 서버는 별도의 스레드에서 각 클라이언트의 세션을 실행하여 여러 클라이언트를 동시에 처리할 수 있습니다.
멀티프로세싱: 멀티스레딩과 유사하지만 스레드 대신 별도의 프로세스가 생성됩니다. 이 방법은 일반적으로 멀티스레딩보다 리소스 집약적이며 프로세스 간 통신이 필요하기 때문에 관리가 더 복잡할 수 있습니다.
비차단 I/O(비동기 I/O): 클라이언트 입력을 기다리는 동안 서버를 차단하지 않는 비차단 소켓 또는 비동기 I/O 라이브러리를 사용합니다. libuv 또는 Boost.Asio(C++용)와 같은 라이브러리와 Node.js(JavaScript용)와 같은 프레임워크는 이 모델을 사용합니다.
선택 또는 폴링: 'select()' 또는 'poll()' 시스템 호출을 사용하여 단일 스레드 내에서 동시에 여러 소켓의 가독성이나 쓰기 가능성을 모니터링하고 필요에 따라 소켓으로 주의를 전환합니다.
#include <pthread.h>
void *handle_client(void *arg) {
int clnt_sock = *((int *)arg);
free(arg);
char message[BUF_SIZE];
int str_len;
while ((str_len = read(clnt_sock, message, BUF_SIZE)) > 0) {
write(clnt_sock, message, str_len);
}
close(clnt_sock);
return NULL;
}
int main(int argc, char *argv[]) {
int serv_sock;
struct sockaddr_in serv_adr, clnt_adr;
socklen_t clnt_adr_sz;
serv_sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&serv_adr, 0, sizeof(serv_adr));
serv_adr.sin_family = AF_INET;
serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_adr.sin_port = htons(atoi(argv[1]));
bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr));
listen(serv_sock, 5);
while (1) {
clnt_adr_sz = sizeof(clnt_adr);
int *clnt_sock = malloc(sizeof(int));
*clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
pthread_t thread;
pthread_create(&thread, NULL, handle_client, clnt_sock);
pthread_detach(thread); // Optionally detach the thread to free resources once it's done
}
close(serv_sock);
return 0;
}