socket 프로그래밍 서버와 다중 클라이언트 [다중 클라이언트 다중 프로세스 접근]
34251 단어 내장형 C
socket 프로그래밍 서버와 다중 클라이언트 [다중 클라이언트 다중 프로세스 접근]
이전 socket 서버와 클라이언트는 단일 클라이언트와 서버의 상호작용을 썼는데 기본적인 기능을 실현하기 위해서일 뿐이다. 이 글은 내용을 추가했고 여러 클라이언트가 동시에 서버에 접근하여 다중 프로세스로 처리했다.벽돌을 던져 옥을 끌어올리고, 많은 문제점을 보고, 관리의 지적을 바랍니다. 감사합니다.
프로세스 병행 서버: 이 서버는 이전 서버의 부족함을 보완하고 여러 개의 클라이언트를 동시에 처리할 수 있으며 클라이언트가 연결하기만 하면 응답할 수 있습니다.이 서버에서 아버지 프로세스는 주로 감청을 담당하기 때문에 아버지 프로세스가 시작하자마자 아버지 프로세스의 수신 함수를 닫아서 아버지 프로세스가 수신 함수에서 막혀서 하위 프로세스가 성공적으로 만들어지지 않도록 해야 한다.마찬가지로 하위 프로세스는 주로 클라이언트를 수신하고 관련 처리를 하기 때문에 하위 프로세스는 만들자마자 감청 함수를 닫아야 한다. 그렇지 않으면 서버 기능의 문란을 초래할 수 있다.이 서버에서 특히 주의해야 할 것은 하위 프로세스가 종료될 때 좀비 프로세스가 발생할 수 있기 때문에 우리는 반드시 하위 프로세스가 종료된 후에 처리해야 한다는 것이다.
더 이상 말하지 말고 정리해라!
서버:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <signal.h>
#define SOCKET_PORT 8888
/*global*/
char buffer[1024] = {
0};
char new_buffer[1024] = {
0};
int create_socket_and_listen(void)
{
int iRet = -1;
int socket_fd = -1;
struct sockaddr_in addr;
/*1.create socket fd based on TCP*/
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd == -1)
{
printf("socket create faild!
");
exit(-1);
}
/*2.init the port and IP address*/
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET; //Internet address family
addr.sin_port = htons(SOCKET_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
/*3.bind socket with addr*/
iRet = bind(socket_fd, (struct sockaddr *)&addr, sizeof(addr));
if(iRet == -1)
{
printf("bind faild!
");
return 0;
}
/*4.listen the new client from socketfd*/
iRet = listen(socket_fd, 5);
if(iRet == -1)
{
printf("listen faild!
");
return 0;
}
return socket_fd;
}
int wait_client_accept(int socket_fd)
{
int new_socket_fd = -1;
struct sockaddr_in new_addr;
int new_addr_len = sizeof(new_addr);
/*5.create a new socket fd(different from the socket_fd before) and use it to accept the client*/
printf("Waiting for client connect:
");
new_socket_fd = accept(socket_fd, (struct sockaddr *)&new_addr, &new_addr_len);
if(new_socket_fd < 0)
{
printf("accept faild!
");
return 0;
}
printf("accept new_socket_fd : [%d]
", new_socket_fd);
return new_socket_fd;
}
int client_handler(int new_socket_fd, int pid)
{
int iRet = -1;
/*6.communciate with client*/
while(1)
{
iRet = read(new_socket_fd, buffer, sizeof(buffer)); /*if nothing to read, it will block*/
printf("[PID:%d]receive data:%s
", pid, buffer);
if(strcmp(buffer, "quit") == 0) /*best to use strncmp*/
{
return 0;
}
strcpy(new_buffer, "The server has received your data:"); /*best to use strncpy*/
strncat(new_buffer, buffer, sizeof(buffer));
iRet = write(new_socket_fd, new_buffer, sizeof(new_buffer));
if(iRet <= 0)
{
printf("write faild!
");
return 0;
}
memset(buffer, 0, sizeof(buffer));
memset(new_buffer, 0, sizeof(new_buffer));
}
return 0;
}
/*When child process exit, father process will receive the signal, then go to the handler to release the source*/
int pid_handler(void)
{
pid_t pw;
#if 0
if((pw = wait(NULL)) != 0)
#else
if((pw = waitpid(-1, NULL, WNOHANG)) > 0) /*wait and waitpid are all OK*/
#endif
{
printf(" A client (pid : %d) quit!
", pw);
}
}
int main(void)
{
int socketfd = -1;
int new_socketfd = -1;
pid_t pid;
socketfd = create_socket_and_listen();
signal(SIGCHLD, (__sighandler_t)pid_handler); /*In order to deal with Zombie process, if no trans here, will be warning!*/
while(1)
{
new_socketfd = wait_client_accept(socketfd);
pid = fork();
if(pid < 0)
{
printf("fork error!
");
break;
}
else if(pid == 0) //child
{
printf("[Notice!] A new client connect, pid:%d, ppid:%d
", getpid(), getppid());
close(socketfd);
client_handler(new_socketfd, getpid());
break;
}
else //father
{
close(new_socketfd);
continue;
}
}
return 0;
}
비고: 서버에 있는signal에 대한 지식은 전송문을 클릭하면 볼 수 있습니다. 상세하게 적혀있습니다.wait와waitpid 인터넷에도 많은 내용이 있습니다. 모두 매우 상세하게 적혀있습니다. 저도 이해하고 쓸 수 있습니다(사실은 복사 붙여넣기, 쓴웃음~). 의미가 없습니다. 저는 새로운 이해가 없거나 제가 실천한 것이 없으면 위에 쓰지 않고 시간을 낭비한다고 생각합니다.
클라이언트:
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#define SOCKET_PORT 8888
int main(void)
{
int iRet = -1;
int socket_fd = -1;
int addr_len = 0;
struct sockaddr_in addr;
char buffer[1024] = {
0};
/*1.create socket fd*/
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if(socket_fd == -1)
{
printf("scoket create faild!
");
exit(-1);
}
/*2.init the port and IP address which it will visit*/
addr.sin_family = AF_INET;
addr.sin_port = htons(SOCKET_PORT);
#if 0
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //The two ways are all OK,usually we use the second method
#else
inet_aton("127.0.0.1", &(addr.sin_addr));
#endif
/*3.connect it*/
addr_len = sizeof(addr);
iRet = connect(socket_fd, (struct sockaddr *)&addr, addr_len);
if(iRet == -1)
{
printf("connect faild!
");
return 0;
}
/*4.communicate with the server*/
printf("Please input what you want to server:
");
while(1)
{
scanf("%s", (char *)buffer);
iRet = write(socket_fd, buffer, sizeof(buffer));
if(!strcmp(buffer, "quit"))
{
break;
}
memset(&buffer, 0, sizeof(buffer));
iRet = read(socket_fd, buffer, sizeof(buffer)); /*when buffer is NULL, the read will block*/
printf("%s
", buffer);
printf("Communicate OK!
");
memset(&buffer, 0, sizeof(buffer));
}
return 0;
}
코드 직접 측정 가능!!!그림이 붙기 싫으니 직접 한번 해 보세요!
문외화: 접착식에 접촉한 지 얼마 되지 않아 2년이 넘고 많고 적으며 좀 쓸 것 같고 맹점도 많다.인터넷의 자원은 매우 강하고 원본 주석이 모두 갖추어져 있어서 확실히 편리하다. 그러나 때때로 내가 찾고 싶은 물건은 인터넷에서'정확하지 않다'는 것이 없다. 어쩌면 작가가 나의 그 점을 정확하게 얻지 못했을지도 모른다. 작가: "응?? 나를 원망해??"그래서 앞으로 가치 있고 의미 있는 것들은 모두 보존해서 불시의 필요에 대비할 거라고 생각해요~