sock 프로그래밍 학습 2

7036 단어
연결을 위한 서비스 SOCK_STREAM、SOCK_SEQPACKET, 데이터 교환을 시작하기 전에 서비스를 요청하는 프로세스 플러그인과 서비스를 제공하는 프로세스 플러그인 사이의 연결을 설정해야 합니다: 클라이언트는connect를 호출합니다.
      int connect(int sockfd const struct *addr, socklen_t len);
connect에서 지정한 주소는 통신하고 싶은 서버 주소입니다. sockfd가 하나의 주소에 연결되지 않으면,connect는 호출자에게 기본 주소를 연결합니다.
apue의 마지막 연결 예:
int connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen, int n)   /* n */
{
	int nsec;
        int nsleep = 2<<n;
	for(nsec = 1; nsec <= nsleep; nsec <<= 1)
	{
		if(connect(sockfd, addr, alen) == 0)
			return 0;
		if(nsec<= nsleep/2)
			sleep(nsec);
	}
	return (-1);
}
서버 프로세스 호출listen은 클라이언트의connect 요청을 받아들일 수 있음을 알립니다. 함수 원형:
       int  listen(int sockfd,  int backlog);/*backlog는 서버가 클라이언트의 요청에 응답할 수 있는 최대 연결 수입니다. */the upper limit is specified as SOMAXCONN in .
서버 프로세스가listen을 호출한 후, 플러그인 socket은accept를 호출하여connect 요청을 받고 연결을 만들 수 있습니다.
       int accept(int sockfd, struct sockaddr *addr, socklen_t n);
반환 값은 연결을 호출하는 클라이언트에 연결된 플러그인 설명자입니다.이 새로운 플러그인은 원시적인 accept 함수인 sockfd와 같은 유형과 주소족을 가지고 있습니다.원시sockfd는 이 연결과 관련이 없습니다.
다른 클라이언트 연결 요청을 수신하기 위해 계속 사용할 수 있는 상태를 유지합니다.
요청 처리가 없으면accept는 연결 요청이 도착하는 것을 계속 막고, sockfd가 막히지 않으면accept는 -1로 되돌아옵니다.
apue의 서버 소켓 초기 단점 함수:
     
#include "apue.h"
#include <errno.h>
#include <sys/socket.h>

int
initserver(int type, const struct sockaddr *addr, socklen_t alen,
  int qlen)
{
    int fd;
    int err = 0;

    if ((fd = socket(addr->sa_family, type, 0)) < 0)
        return(-1);
    if (bind(fd, addr, alen) < 0) {
        err = errno;
        goto errout;
    }
    if (type == SOCK_STREAM || type == SOCK_SEQPACKET) {
        if (listen(fd, qlen) < 0) {
            err = errno;
            goto errout;
        }
    }
    return(fd);

errout:
    close(fd);
    errno = err;
    return(-1);
}
먼저 socket 초기화 플러그인을 호출한 다음에 서버 프로세스 플러그인에 모두가 알고 있는 주소를 연결합니다. 대상을 향한 연결이라면listen을 호출해서connect 요청을 받아들일 수 있음을 알립니다.
연결을 설정하면 데이터 전송:
send,recv는 이미 연결된 플러그인 간의 통신에 사용됩니다.
sendto,recvfrom은 연결이 없는 플러그인에 주소를 지정할 수 있습니다.연결이 없는 플러그인은send를 사용할 수 없습니다. 연결을 호출하지 않으면 대상 주소를 미리 설정할 수 없습니다.
sendmsg,recvmsg는 더 많은 옵션의 수용과 전송입니다.
apue 위에서 연결된 서버와 클라이언트의 예:
서버:
#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 256
#endif

extern int initserver(int, const struct sockaddr*, socklen_t, int);
extern void daemonize(const char *cmd);
void serve(int sockfd)
{
	int clfd;
	FILE *fp;
	char buf[BUFLEN];
	while(1)
	{
		clfd = accept(sockfd, NULL, NULL);
		if(clfd < 0)
		{
			syslog(LOG_ERR, "ruptimed: accept error: %s", strerror(errno));
			exit(1);
		}
		if((fp = popen("/usr/bin/uptime", "r")) == NULL)
		{
			sprintf(buf, "error: %s
", strerror(errno)); send(clfd, buf, strlen(buf), 0); } else { while(fgets(buf, BUFLEN, fp)!= NULL) send(clfd, buf, strlen(buf), 0); pclose(fp); } close(clfd); } } int main() { struct addrinfo *ailist, *aip; struct addrinfo hint; int sockfd, err; int n; char *host; #ifdef _SC_HOST_NAME_MAX n = sysconf(_SC_HOST_NAME_MAX); if(n < 0) #endif n = HOST_NAME_MAX; host = malloc(n); if(host == NULL) { printf("malloc error
"); exit(-1); } if(gethostname(host, n) < 0) { printf("gethostname error
"); exit(-1); } daemonize("ruptimed"); hint.ai_flags = AI_PASSIVE; hint.ai_family = 0; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_addr = 0; hint.ai_canonname = 0; hint.ai_next = NULL; if((err = getaddrinfo(host, "ruptimed", &hint, &ailist)) != 0) { syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s", gai_strerror(err)); exit(1); } for(aip = ailist; aip != NULL; aip = aip->ai_next) { if((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0) { serve(sockfd); exit(0); } } exit(1); }

클라이언트:
void print_uptime(int sockfd)
{
	int		n;
	char	buf[BUFLEN];

	while((n = recv(sockfd, buf, BUFLEN, 0)) > 0)
	{
		write(STDOUT_FILENO, buf, n);
	}
	if(n < 0)
	{
		printf("recv error
"); exit(-1); } } int main(int argc, char *argv[]) { struct addrinfo *ailist, *aip; struct addrinfo hint; int sockfd, err; if(argc != 2) { printf("usage: ruptime hostname
"); exit(-1); } hint.ai_flags = 0; hint.ai_family = 0; hint.ai_socktype = SOCK_STREAM; hint.ai_protocol = 0; hint.ai_addrlen = 0; hint.ai_addr = NULL; hint.ai_canonname = NULL; hint.ai_next = NULL; if((err =getaddrinfo(argv[1],"ruptimed", &hint, &ailist)) != 0) { printf("getaddrinfo error: %s
", gai_strerror(err)); exit(-1); } for(aip = ailist; aip != NULL; aip = aip->ai_next) { if((sockfd = socket(aip->ai_family, SOCK_STREAM, 0)) < 0) { err = errno; continue; } if(connect_retry(sockfd, aip->ai_addr, aip->ai_addrlen, 1) < 0) err = errno; else { print_uptime(sockfd); exit(0); } } fprintf(stderr, "can't connect to %s: %s
", argv[1], strerror(err)); exit(1); }

연결이 없는 서버 서비스 함수:
serve(int sockfd)
{
    int         n;
    socklen_t   alen;
    FILE        *fp;
    char        buf[BUFLEN];
    char        abuf[MAXADDRLEN];

    for (;;) {
        alen = MAXADDRLEN;
        if ((n = recvfrom(sockfd, buf, BUFLEN, 0,
          (struct sockaddr *)abuf, &alen)) < 0) {
            syslog(LOG_ERR, "ruptimed: recvfrom error: %s",
              strerror(errno));
            exit(1);
        }
        if ((fp = popen("/usr/bin/uptime", "r")) == NULL) {
            sprintf(buf, "error: %s
", strerror(errno)); sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)abuf, alen); } else { if (fgets(buf, BUFLEN, fp) != NULL) sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)abuf, alen); pclose(fp); } } }

클라이언트가 요청을 보내고 수락하는 함수:
print_uptime(int sockfd, struct addrinfo *aip)
{
    int     n;
    char    buf[BUFLEN];

    buf[0] = 0;
    if (sendto(sockfd, buf, 1, 0, aip->ai_addr, aip->ai_addrlen) < 0)
        err_sys("sendto error");
    alarm(TIMEOUT);
    if ((n = recvfrom(sockfd, buf, BUFLEN, 0, NULL, NULL)) < 0) {
        if (errno != EINTR)
            alarm(0);
        err_sys("recv error");
    }
    alarm(0);
    write(STDOUT_FILENO, buf, n);
}

이 몇 가지 예는 모두 getaddrinfo를 통해 플러그인 주소 정보를 얻은 것이다.
연결을 위한 요청은 연결이 왔을 때 서버가 제공한 서비스를 판단하고 데이터 패키지 기반 프로토콜에 대해 연결이 없기 때문에 먼저 서버의 데이터를 보내서 서비스를 요청해야 한다.
데이터 패키지를 기반으로 하는 통신 클라이언트의recvfrom는 대기 서버의 데이터를 막습니다. 서버가 열리지 않으면 무제한 대기하기 때문에recvfrom 무제한 차단을 피하기 위해 타이머가 필요합니다.

좋은 웹페이지 즐겨찾기