UNIX 네트워크 프로그래밍의 기초

머리말

소켓 프로그래밍과 관련된 많은 구조가 있기 때문에 후속 참조를 용이하게 하기 위해 여기에 일부 레코드가 만들어집니다.


1. 구조 관련

1) sockaddr 및 sockaddr_in에 대한 자세한 설명

sockaddr구성원이 통신을 위해 IP 주소와 포트를 혼합하는 초기 버전에서 사용된 sa_data구조 및 헤더 파일은 다음과 같습니다.

#include <sys/socket.h>

struct sockaddr {
	sa_family_t 				sin_family;
	char 						sa_data[14];	
}

sockaddr_in초기 구조의 ip와 port를 분리하여 구조는 다음과 같습니다.

#include <netinet/in.h>
#include <arpa/inet.h>

struct sockaddr_in
{
	sa_family_t 				sin_family;
	uint16_t 					sin_port;
	struct in_addr				sin_addr;
	char						sin_zero[8];
};
struct in_addr
{
	in_addr_t					s_addr;
};

sin_port와 sin_addr은 모두 네트워크 바이트 순서(big endian)여야 하고, 우리가 주로 사용하는 리눅스는 little endian(호스트 바이트 순서)이므로 인터페이스 변환이 필요하다.

2) 바이트 순서 변환을 위한 함수

~~ inet_addr~~ 지금은 폐지되었습니다. <<UNIX 네트워크 프로그래밍 1권>> 섹션 3.6 P68을 참조하십시오. 교체는 inet_addr입니다.更好的替代是inet_pton(支持IPv4和IPv6)

#include <arpa/inet.h>
/*返回:若字符串有效则为1,否则为0/*/
int inet_aton(const char *strptr, struct in_addr *addrptr);
/*返回:若字符串有效则为32位二进制网络字节序的IPv4地址,否则为INADDR_NONE*/
in_addr_t inet_addr(const char *strptr);
/*返回:指向一个点分十进制数串的指针*/
char *inet_ntoa(struct in_addr inaddr); 

/*返回:若成功则为1,若输入不是有效的表达式则为0, 若出错则为-1*/
int  inet_pton(int family, const char *strptr, void *addrptr/*&foo.sin_addr*/);
/*如成功则为指向结果的指针,若出错则为NULL*/
len:如果是IPv4,填写16,在<netinet/in.h>中有定义宏 
#define INET_ADDRSTRLEN 16 
#define INET6_ADDRSTRLEN 46
char str[INET_ADDRSTRLEN];
ptr = inet_ntop(AF_INET, &foo.sin_addr, str, sizeof(str));
const char *inet_ntop(int family, const void *addrptr,  char *strptr, size_t len);

예:

struct sockaddr_in servaddr;
servaddr.sin_port = htons(13);
servaddr.sin_addr.s_addr = inet_addr("192.168.1.1");
//尽量使用如下,因为该接口不仅支持IPV4还支持IPV6

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

int  inet_pton(int af, const char *src, void *dst); //将"点分十进制"->"二进制整数”
//struct  in_addr s;
char *ip = "192.168.1.1";
inet_pton(AF_INET, (void*) ip,  (void*)&s);

端口是0참고: () 또는 (값이 일반적으로 0)인 경우 INADDR_ANYhtonl을 사용할 필요가 없기 때문에 네트워크 바이트 순서(NBO)로 변환할 필요가 없지만 모든 INADDR_은 헤더 파일 <netinet/in에 정의되어 있습니다. .h> 호스트 단어에 따라 시퀀스 정의를 위해 <<UNIX 네트워크 프로그래밍 볼륨 1>> 섹션 4.4 P83에 htonl을 사용하는 데 여전히 익숙해져야 합니다.
를 사용하고 inet_pton, IPV4 및 IPV6를 지원하고, 약간만 변경하고,
다른 곳은 수정해야 하므로 더 좋은 방법은 协议无关프로그램을 작성하는 것입니다(11-11, getaddrinfo 기능 사용).

struct sockaddr_in ->struct sockaddr_in6
socket(AF_INET, SOCK_STREAM,0) 	-> socket(AF_INET6, SOCK_STREAM, 0);
servaddr.sin_family = AF_INET	->	servaddr.sin6_family = AF_INET6;
servaddr.sin_port 				->		servaddr_sin6_port
inet_pton(AF_INET,...,...)		->		inet_pton(AF_INET6,...,...);

#include	"unp.h"

int
main(int argc, char **argv)
{
	int					sockfd, n, counter = 0;
	char				recvline[MAXLINE + 1];
	struct sockaddr_in	servaddr;

	if (argc != 2)
		err_quit("usage: a.out <IPaddress>");

	if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
		err_sys("socket error");

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;	/*协议族成员设置为AF_INET*/
	servaddr.sin_port   = htons(13);	/* daytime server */
	if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
		err_quit("inet_pton error for %s", argv[1]);

	if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
		err_sys("connect error");

	while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
		counter++;
		recvline[n] = 0;	/* null terminate */
		if (fputs(recvline, stdout) == EOF)
			err_sys("fputs error");
	}
	if (n < 0)
		err_sys("read error");

	printf("counter = %d\n", counter);
	exit(0);
}

3) 질문과 결론

3-1) IPv4 및 IPv6를 지원하는 프로토콜 독립적인 코드 작성

3-2) 점분리 십진수 대신 도메인 이름 사용


2. API 관련

1) 연결 기능

#include <sys/socket.h>
/*返回: 若成功则为0,若出错则为-1*/
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);

出错返回TCP 소켓인 경우 연결에 성공하거나 다음 상황에서 오류가 발생하는 경우에만 connect가 TCP 3방향 핸드셰이크 프로세스를 트리거합니다 .

  • 클라이언트는 SYN 세그먼트를 보내지만 서버로부터 응답을 받지 못하고 ETIMEDOUT 오류를 반환하며 일정 간격으로 계속 SYN을 보냅니다.
  • 클라이언트에 대한 SYN 응답이 RST(리셋을 의미)이면 클라이언트가 지정한 포트에서 프로세스 없이 서버가 실행 중임을 의미합니다.
    클라이언트는 실행 중이고 서버는 실행 중이 아닙니다.
root@ubuntu:/opt/socket/unpv13e/intro# ./daytimetcpcli 127.0.0.1
connect error: Connection refused

패킷 캡처:
여기에 이미지 설명 삽입

  • 클라이언트가 SYN 세그먼트를 보내고 라우터가 ICMP 오류를 전달하면 이를 소프트 오류(soft error)로 간주하고 클라이언트 호스트 커널은 메시지를 저장했다가 시간 간격에 따라 계속해서 보낸다. 일정 시간(75초) 후에 ICMP 오류(EHOSTUNREACH 또는 ENETUNREACH)가 클라이언트에 반환됩니다.

2) 바인드 기능

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr * myaddr, socklen_t addrlen);//返回0成功,-1出错
  • 클라이언트는 일반적으로 bing을 호출하지 않습니다. 즉, IP 주소와 포트 커널로 선택됩니다 使用通配地址(INADDR_ANY).端口0(内核自行分配端口)
  • 일반적으로 서버는 포트를 지정하고 IPv4 주소를 사용하는데 通配地址(INADDR_ANY,其值一般为0), 서버가 소켓에 IP 주소를 바인딩하지 않으면 커널은 클라이언트가 보낸 SYN의 대상 IP 주소를 서버의 소스 IP 주소로 사용합니다. IPv6입니다. 4.4 섹션 방법을 참조하십시오.

여기에 이미지 설명 삽입

  • 커널이 소켓에 대한 임시 포트를 선택하도록 하고 할당된 포트를 얻으려면 getsockname을 호출하여 프로토콜 주소를 반환해야 합니다. 일반적으로 지정된 서버의 포트는 (5000, 49152) 범위에 있어야 하며 임시 포트 번호는 피해야 합니다(일반적으로 49152보다 크거나 5000 P98 세부 정보보다 작음).

  • 바인딩 기능에서 반환되는 일반적인 오류는 EADDRINUSE(이미 사용 중인 주소)입니다. 해결책은 섹션 7.5에서 설명한 것처럼 SO_REUSEADDR/SO_REUSEPORT 소켓 옵션을 사용하는 것입니다.

주제별 기록을 여는 후속 조치, 여기에는 카탈로그 가이드만 있고 후속 링크는 여기에 있습니다.

3) 듣기 기능

#include <sys/socket.h>
int listen(int sockfd, int backlog);//成功返回0, 出错返回-1

4) 수락 기능

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);//成功非负描述符,出错-1

3. 어려움

후속 특별 기록, 여기에는 후속 링크만 있습니다.

계속하려면...

추천

출처blog.csdn.net/nc_linux/article/details/125045976