c언어 소켓 통신 예제 (멀티 스레드, 멀티 프로세스)

프로그래밍/C, C++|2021. 3. 2. 15:24
반응형

멀티 스레드와 멀티 프로세스의 차이점을 설명한 글입니다.

요약하자면, 가장 큰 차이는 멀티스레드 방식은 공유 메모리를 갖고, 멀티프로세스 방식은 별도 메모리를 갖는듯..

내용을 확인 하신 후 상황에 맞는 소켓을 작성 하시기 바랍니다.

https://you9010.tistory.com/136

 

예제는 해외 사이트에서 가져왔으나, client 수 제한이 없거나 좀비프로세스 발생을 원치 않는다면 아래 URL 에서도 깔끔한 소스를 보실 수 있습니다.

https://velog.io/@jyongk/Multi-Process-Server-Multi-Thread-Server

 

 

멀티 스레드 방식

socket_server.c

 

#include <stdio.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <string.h>

#include <arpa/inet.h>

#include <fcntl.h> // for open

#include <unistd.h> // for close

#include <pthread.h>

 

char client_message[2000];

char buffer[1024];

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

 

void * socketThread(void *arg)

{

  int newSocket = *((int *)arg);

  recv(newSocket , client_message , 2000 , 0);

 

  // Send message to the client socket 

  pthread_mutex_lock(&lock);

  char *message = malloc(sizeof(client_message)+20);

  strcpy(message,"Hello Client : ");

  strcat(message,client_message);

  strcat(message,"\n");

  strcpy(buffer,message);

  free(message);

  pthread_mutex_unlock(&lock);

  sleep(1);

  send(newSocket,buffer,13,0);

  printf("Exit socketThread \n");

  close(newSocket);

  pthread_exit(NULL);

}

 

int main(){

  int serverSocket, newSocket;

  struct sockaddr_in serverAddr;

  struct sockaddr_storage serverStorage;

  socklen_t addr_size;

 

  //Create the socket. 

  serverSocket = socket(PF_INET, SOCK_STREAM, 0);

 

  // Configure settings of the server address struct

  // Address family = Internet 

  serverAddr.sin_family = AF_INET;

 

  //Set port number, using htons function to use proper byte order 

  serverAddr.sin_port = htons(7799);

 

  //Set IP address to localhost 

  serverAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

 

 

  //Set all bits of the padding field to 0 

  memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);

 

  //Bind the address struct to the socket 

  bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

 

  //Listen on the socket, with 40 max connection requests queued 

  if(listen(serverSocket,50)==0)

    printf("Listening\n");

  else

    printf("Error\n");

    pthread_t tid[60];

    int i = 0;

    while(1)

    {

        //Accept call creates a new socket for the incoming connection

        addr_size = sizeof serverStorage;

        newSocket = accept(serverSocket, (struct sockaddr *) &serverStorage, &addr_size);

 

        //for each client request creates a thread and assign the client request to it to process

       //so the main thread can entertain next request

        if( pthread_create(&tid[i++], NULL, socketThread, &newSocket) != 0 )

           printf("Failed to create thread\n");

 

        if( i >= 50)

        {

          i = 0;

          while(i < 50)

          {

            pthread_join(tid[i++],NULL);

          }

          i = 0;

        }

    }

  return 0;

}

 

멀티프로세스 방식

socket_server.c

 

#include <stdio.h>

#include <stdlib.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <string.h>

#include <arpa/inet.h>

#include <fcntl.h> // for open

#include <unistd.h> // for close

#include <sys/types.h>

#include <pthread.h>

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;

 

void  socketThread(int clientSocket)

{

 

  char client_message[2000];

  char buffer[1024];

 

  int newSocket = clientSocket;

 

  recv(newSocket , client_message , 2000 , 0);

 

  // Send message to the client socket

  pthread_mutex_lock(&lock);

  char *message = malloc(sizeof(client_message)+20);

  strcpy(message,"Hello Client : ");

  strcat(message,client_message);

  strcat(message,"\n");

  strcpy(buffer,message);

  free(message);

  pthread_mutex_unlock(&lock);

  sleep(1);

  send(newSocket,buffer,13,0);

  printf("Exit socketThread \n");

  close(newSocket);

  }

 

 

 

int main(){

  int serverSocket, newSocket;

  struct sockaddr_in serverAddr;

  struct sockaddr_storage serverStorage;

  socklen_t addr_size;

  pid_t pid[50];

  //Create the socket.

  serverSocket = socket(PF_INET, SOCK_STREAM, 0);

 

  // Configure settings of the server address struct

  // Address family = Internet

  serverAddr.sin_family = AF_INET;

 

  //Set port number, using htons function to use proper byte order

  serverAddr.sin_port = htons(7799);

 

  //Set IP address to localhost

  serverAddr.sin_addr.s_addr = inet_addr("0.0.0.0");

  //Set all bits of the padding field to 0

  memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);

 

  //Bind the address struct to the socket

  bind(serverSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr));

 

  //Listen on the socket, with 50 max connection requests queued

  if(listen(serverSocket,50)==0)

    printf("Listening\n");

  else

    printf("Error\n");

    int i = 0;

while(1)

    {

        /* Accept call creates a new socket for the incoming connection */

        addr_size = sizeof serverStorage;

        newSocket = accept(serverSocket, (struct sockaddr *) &serverStorage, &addr_size);

        int pid_c = 0;

 

      if ((pid_c = fork())==0)

        {

          socketThread(newSocket);

        }

        else

        {

          pid[i++] = pid_c;

          if( i >= 49)

           {

             i = 0;

             while(i < 50)

                waitpid(pid[i++], NULL, 0);

             i = 0;

           }

        }

    }

  return 0;

}


클라이언트 소켓 파일

socket_client.c

 

#include <stdio.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <string.h>

#include <arpa/inet.h>

#include <stdlib.h>

#include <fcntl.h> // for open

#include <unistd.h> // for close

#include<pthread.h>

 

void * cientThread(void *arg)

{

  printf("In thread\n");

  char message[1000];

  char buffer[1024];

  int clientSocket;

  struct sockaddr_in serverAddr;

  socklen_t addr_size;

 

  // Create the socket. 

  clientSocket = socket(PF_INET, SOCK_STREAM, 0);

 

  //Configure settings of the server address

 // Address family is Internet 

  serverAddr.sin_family = AF_INET;

 

  //Set port number, using htons function 

  serverAddr.sin_port = htons(7799);

 

 //Set IP address to localhost

  serverAddr.sin_addr.s_addr = inet_addr("localhost");

  memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);

 

    //Connect the socket to the server using the address

    addr_size = sizeof serverAddr;

    connect(clientSocket, (struct sockaddr *) &serverAddr, addr_size);

    strcpy(message,"Hello");

 

   if( send(clientSocket , message , strlen(message) , 0) < 0)

    {

            printf("Send failed\n");

    }

 

    //Read the message from the server into the buffer

    if(recv(clientSocket, buffer, 1024, 0) < 0)

    {

       printf("Receive failed\n");

    }

    //Print the received message

    printf("Data received: %s\n",buffer);

    close(clientSocket);

    pthread_exit(NULL);

}

int main(){

  int i = 0;

  pthread_t tid[51];

  while(i< 50)

  {

    if( pthread_create(&tid[i], NULL, cientThread, NULL) != 0 )

           printf("Failed to create thread\n");

    i++;

  }

  sleep(20);

  i = 0;

  while(i< 50)

  {

     pthread_join(tid[i++],NULL);

     printf("%d:\n",i);

  }

  return 0;

} 

 

 

 

[출처] https://dzone.com/articles/parallel-tcpip-socket-server-with-multi-threading

[수정] 출처의 예제에 오타가 있어서 바로 실행하면 에러가 발생합니다. 소스를 보완하여 작성하였습니다.

반응형

댓글()

c언어 파일 이벤트 감시 (inotify)

프로그래밍/C, C++|2021. 2. 24. 11:56
반응형

파일이 생성, 변경, 삭제 되었을때 실시간으로 감지하여 특정 소스를 처리할 수 있습니다.

아래 소스를 백그라운드로 돌릴 경우 메세지 출력이 되지 않으나 동작은 되는것을 확인하였습니다.

파일 이벤트 종류는 아래 세가지 외에 더 있으므로 상세 감지가 필요하신 분은 기타 문서를 참고하시기 바랍니다.


#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include <sys/types.h>

#include <sys/inotify.h>

#include <unistd.h>


#define EVENT_SIZE  (sizeof(struct inotify_event))

#define BUF_LEN     (1024 * (EVENT_SIZE + 16))


int main(int argc, char **argv) {

    int length, i = 0;

    int fd;

    int wd;

    char buffer[BUF_LEN];


    fd = inotify_init();


    if (fd < 0) {

        perror("inotify_init");

    }


    wd = inotify_add_watch(fd, ".", // 현재 경로를 나타내며, 특정파일로 지정 가능합니다.

        IN_MODIFY | IN_CREATE | IN_DELETE);

    length = read(fd, buffer, BUF_LEN);


    if (length < 0) {

        perror("read");

    }


    while (i < length) {

        struct inotify_event *event =

            (struct inotify_event *) &buffer[i];

        if (event->len) {

            if (event->mask & IN_CREATE) {

                printf("The file %s was created.\n", event->name);

            } else if (event->mask & IN_DELETE) {

                printf("The file %s was deleted.\n", event->name);

            } else if (event->mask & IN_MODIFY) {

                printf("The file %s was modified.\n", event->name);

            }

        }

        i += EVENT_SIZE + event->len;

    }


    (void) inotify_rm_watch(fd, wd);

    (void) close(fd);


    return 0; 

}




[출처] https://stackoverflow.com/questions/13351172/inotify-file-in-c


반응형

댓글()

c언어 마지막줄 문자열 중복 출력하지 않기

프로그래밍/C, C++|2021. 2. 24. 10:41
반응형

feof() 함수를 이용해 파일내용을 출력할 경우 파일의 맨 마지막줄 문자열은 중복 출력되는 경우가 있습니다. 이럴때 아래와 같은 코드를 추가하여 중복 출력을 방지할 수 있습니다.


while (feof(fb) == 0) {   // 파일내용이 끝날때까지 반복

    if(fgets(str, 100, fb) == NULL) break;  // 괄호 내용은 순서대로 '문자열, 사이즈, FILE*'

    ... 생략 ...

}




반응형

댓글()

c언어 string 문자형을 int 형으로 변환하기

프로그래밍/C, C++|2021. 2. 24. 10:15
반응형

str 문자열에 숫자만 있을 경우 아래와 같은 코드를 사용하여 int 를 입력할 곳에 넣을 수 있습니다.

 

(int) strtol(str, (char **)NULL, 10)

 

 

[출처] https://stackoverflow.com/questions/7021725/how-to-convert-a-string-to-integer-in-c

반응형

댓글()

c언어 변수 내용 비우기 (문자열 초기화)

프로그래밍/C, C++|2021. 2. 24. 10:05
반응형

시스템에 따라 아래 코드가 정상 컴파일 되거나 안되는 경우가 있습니다.


sprintf(str, ""); // str 변수 내용 비우기


이러한 코드를 가진 소스가 아래와 같이 컴파일 할 때 에러를 출력한다면


# gcc sysdocu.c -o sysdocu

sysdocu.c:72:22: warning: zero-length gnu_printf format string [-Wformat-zero-length]

         sprintf(str, "");

                      ^~


아래와 같이 변경하여 사용이 가능합니다.


sprintf(str, "%s", "");





반응형

댓글()

c언어 파일 내용 출력하기

프로그래밍/C, C++|2021. 2. 19. 08:41
반응형

# cat test.txt

aaa

bbb

ccc


# vi test.c


#pragma warning(disable :4996)

#define MAX_LEN 100

#include <stdio.h>


int main()

{

    FILE* fs;

    fs = fopen("test.txt", "r");

    while (feof(fs) == 0) {

        char str[MAX_LEN];

        fgets(str, MAX_LEN, fs);

        printf("%s", str);

    }

}


# gcc test.c -o test


# ./test

aaa

bbb

ccc



[소스 출처] https://jeckl.tistory.com/entry/C%EC%96%B8%EC%96%B4-21%EA%B0%95-%ED%8C%8C%EC%9D%BC-%EC%9E%85%EC%B6%9C%EB%A0%A5-%EC%9D%BD%EA%B8%B0-fopen-fgets-fseek-feof-fclose

반응형

댓글()

c언어 문자열에서 숫자만 가져와서 출력하기

프로그래밍/C, C++|2021. 2. 18. 16:15
반응형

/* 문자와 숫자가 섞여있는 문자열이 주어지면 그 중 숫자만 추출하여 그 순서대로 자연수를 만듭니다. 만들어진 자연수와 그 자연수의 약수 개수를 출력합니다. 만약 "fsaknf0012lsaf"에서 숫자만 추출하면 0,0,1,2고 이것을 자연수로 만들면 12가 됩니다. 즉 첫 자리 0은 자연수화 할 때 무시합니다. 출력은 12를 출력하고, 다음 줄에 12의 약수의 개수를 출력하면 됩니다. 추출하여 만들어지는 자연수는 100,000,000을 넘지 않습니다. */ 


# include <stdio.h> 

int main(){ 

    char a[100]; 

    int res = 0, cnt = 0, i; 

    scanf("%s", &a); 

    for(i = 0; a[i]!='\0'; i++) { 

        if(a[i]>=48 && a[i]<=57) { 

            res = res * 10 + (a[i]-48); 

        }

    } 

    printf("자연수 : %d\n",res); 


    for(i = 1; i<=res; i++) { 

        if(res%i ==0) 

            cnt++; 

    } 

    printf("약수의 개수 : %d\n",cnt); return 0; }


출처: https://asthtls.tistory.com/53 [포장빵의 IT]

반응형

댓글()

c언어 소켓 close 할때 TIME_WAIT 상태로 남아 있는 경우

프로그래밍/C, C++|2021. 2. 16. 08:34
반응형

TIME_WAIT는 소켓 close를 호출하는 쪽에서 설정되는 소켓의 상태이다.

netstat을 사용하여 각 소켓의 상태를 확인할 수 있다.

짧은 작업을 위한 소켓인 경우, Server에서 close를 호출하는 경우가 생기는데, 이 때 부하가 많을수록 TIME_WAIT 상태인 소켓이 많아져 Client에서 Server로 접속을 못하는 경우가 생긴다.

 

다음과 같이 강제적으로 TIME_WAIT상태를 거치지 않고 소켓을 종료할 수 있다.

(기본적으로 TIME_WAIT는 소켓의 필수 상태이니 테스트 필수!!)

 

struct linger ling;

ling.l_onoff = 1;

ling.l_linger = 0;

shutdown(sockfd, SD_BOTH);

setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));

#define WIN32

sockclose(fd);

#else

close(fd);

#endif

 

apache proxy loadbalancer를 사용하는 경우, native 데몬(혹은 tomcat등의 proxy와 연결되는 daemon)과 apache proxy사이의 소켓은 즉시 종료 되나, 클라이언트와 apache간의 소켓은 apache의 KeepAlive설정에 따라 달라진다. KeepAlive를 On으로 설정하는 경우, 클라이언트의 소켓이 close를 먼저 하게 되므로 서버쪽에서는 소켓이 남지 않고, Off로 설정하는 경우, 서버에서 먼저 close를 호출하므로 서버쪽에 소켓이 남게 된다. (남게 되는 소켓은 전부 TIME_WAIT 상태의 소켓을 뜻함)

 

 

출처: https://indra17.tistory.com/entry/소켓-Close시-TIMEWAIT-대기시간-설정C언어 [피로에 살고 피로에 죽자]

 

---------------------------

[ 실제 적용 소스 ]

 

shutdown(client_fd, SHUT_RDWR);
struct linger ling;
ling.l_onoff = 1;
ling.l_linger = 0;
setsockopt(server_fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
close(client_fd);

 

반응형

댓글()

c언어 서버 소켓 종료 후 바로 재사용하기 (bind 실행 에러시)

프로그래밍/C, C++|2021. 2. 16. 08:07
반응형

소켓 프로그램을 만들고 사용중 종료, 재구동이 필요한 경우 소켓이 정상 종료되지 않아 (계속 사용중) 에러가 발생할때 아래와 같은 소켓 옵션 코드를 이용해 해결이 가능합니다.

 

server_fd = socket(AF_INET,SOCK_STREAM,0);

 

// 소켓 종료 후 binding 곧바로 재사용 가능하게 하기

int reuse = 1;

setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse));

 

이름에서 알 수 있듯이 SO_REUSEADDR 옵션이 기존 바인딩 된 주소를 재사용 할 수 있게 해줍니다.

 

반응형

댓글()

c언어 문자열 개행 제거 (두가지 방법)

프로그래밍/C, C++|2021. 2. 9. 07:35
반응형

문자열 변수를 str 이라고 했을때 아래와 같은 코드를 이용해 개행을 제거할 수 있습니다.

 

첫번째

fgets(str, strlen(str), stdin);

str[strlen(str) - 1] = '\0';

 

두번째

for (int i = 0; str[i] != 0; i++) {
    if (str[i] == '\n') {
        str[i] = 0;
        break;
    }
}

 

반응형

댓글()

c언어 문자열 치환하기

프로그래밍/C, C++|2021. 2. 8. 11:12
반응형

# replace.c

#include <stdio.h>

#include <stdlib.h>
#include <string.h>
  
// Function to replace a string with another
// string
char* replaceWord(const char* s, const char* oldW,
                  const char* newW)
{
    char* result;
    int i, cnt = 0;
    int newWlen = strlen(newW);
    int oldWlen = strlen(oldW);
  
    // Counting the number of times old word
    // occur in the string
    for (i = 0; s[i] != '\0'; i++) {
        if (strstr(&s[i], oldW) == &s[i]) {
            cnt++;
  
            // Jumping to index after the old word.
            i += oldWlen - 1;
        }
    }
  
    // Making new string of enough length
    result = (char*)malloc(i + cnt * (newWlen - oldWlen) + 1);
  
    i = 0;
    while (*s) {
        // compare the substring with the result
        if (strstr(s, oldW) == s) {
            strcpy(&result[i], newW);
            i += newWlen;
            s += oldWlen;
        }
        else
            result[i++] = *s++;
    }
  
    result[i] = '\0';
    return result;
}
  
// Driver Program
int main()
{
    char str[] = "xxforxx xx for xx";
    char c[] = "xx";
    char d[] = "Geeks";
  
    char* result = NULL;
  
    // oldW string
    printf("Old string: %s\n", str);
  
    result = replaceWord(str, c, d);
    printf("New String: %s\n", result);
  
    free(result);
    return 0;
}



# gcc replace.c -o replace

# ./replace



반응형

댓글()