CSAPP学习记录 | 网络编程(一)

本文介绍了网络编程的基础知识,包括客户端-服务器模型的事务流程,网络通信中的套接字接口,以及IP地址和域名系统的运作。详细讨论了IP地址结构、字节序转换函数、DNS如何维护IP到域名的映射,以及套接字地址结构、socket、connect、bind、listen和accept等关键函数在连接建立中的作用。此外,还涉及了因特网连接和负载平衡策略。
摘要由CSDN通过智能技术生成

Chapter 11 网络编程

11.1 客户端-服务器编程模型

基本操作:事务transaction

事务组成:

(1)客户端需要服务,向服务器发送请求,发起一个事务
(2)服务器收到请求并解释,并操作它的资源
(3)服务器给客户端发送一个响应,等待下一个请求
(4)客户端收到响应并处理它。

11.2 网络

介绍了封装思想和具体实现,建议是看计算机网络调理一下

11.3 全球IP因特网

因特网的客户端和服务器混合使用套接字接口函数和Unix I/O函数来进行通信。套接字函数典型地是作为会陷入内核的系统调用来实现的,并调用各种内核模式的TCP/IP函数。

11.3.1 IP地址

/*Internet address structure*/
struct in_addr {
    unsigned int s_addr; /*Network byte order (big-endian)*/
};

IP地址结构,由于原本的实现是这样,后期虽然觉得不好但已经大量使用,所以不改了。

TCP/IP为任意整数数据项定义了统一的网络字节顺序(大端),所以对于不同主机,可能需要实现网络和主机字节顺序间实现转换:

#include<netinet/in.h>
/*返回:按照网络字节顺序的值*/
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
/*返回:按照主机字节顺序的值*/
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netlong);

因特网程序使用inet_aton和inet_ntoa来实现IP地址与点分十进制的转换
a: appliction n:network

#include<arpa/inet.h>
/*将点分十进制转化为网络字节顺序的IP地址,成功了返回1,否则为0*/
int inet_aton(const char* cp, struct in_addr *inp);
/*将网络字节顺序的IP地址转化为点分十进制,返回指向点分十进制字符串的指针*/
char *inet_ntoa(struct in_addr in);
练习题11.1
十六进制地址点分十进制地址
0x00.0.0.0
0xffffffff255.255.255.255
0x7f000001127.0.0.1
0xcdbca079205.188.160.121
0x400c950d64.12.149.13
0xcdbc9217205.188.146.23

练习题乱写的。。。

练习题11.2
/*hex2dd.c*/
#include<stdio.h>

int hexto(char i)
{
	if (i <= '9' && i >= '0') {
		return i - '0';
	} else if (i <= 'f' && i >= 'a'){
		return i - 'a' + 10;
	} else {
		printf("FORMAT ERROR\n");
		return -1;
	}
}

void getip(int ip[])
{
	int i = 0;

	for (; i < 3; i++)
		printf("%d.", ip[i]);
	printf("%d\n", ip[i]);
}

int main(int argv, char*argc[])
{
	int i = 1;

	if (argv < 2) {
		printf("missing.\n");
	} else {
		for (; i < argv; i ++) {
			int ip[4];
			int cnt = 0;
			int pos = 2;/*跳过16进制标识0x,直接从开始读取数据*/

			while (pos < 10) {
				int h = hexto(argc[i][pos]);
				int l = hexto(argc[i][pos+1]);

				if (h != -1 && l != -1) {
					ip[cnt] = h*16 + l;
					cnt ++;
				}

				pos = pos + 2;
			}
			getip(ip);
		}
	}
	return 0;
}
练习题11.3
#include<stdio.h>
#include<stdlib.h>
char gethex(int z)
{
	if (z >= 0 &&z <= 9)NS系统维护IP到域名的映射
		return z+'0';
	else if (z >= 10 && z <= 15)
		return z-10 +'a';
	else
		return '\0';
}

char* ddtohex(int dd)
{
	char *hex = (char*)malloc(3*sizeof(char));
	hex[0] = gethex(dd/16);
	hex[1] = gethex(dd%16);
	if(hex[0] != '\0' && hex[1] != '\0')
		return hex;
	else
		return NULL;
}

void getip(char *ip[])
{
	printf("0x");
	int i = 0;

	for(; i < 3; i++)
		printf("%s", ip[i]);
	printf("%s\n", ip[i]);
}

int main(int argv, char*argc[])
{
	int i = 1;

	if (argv < 2) {
		printf("missing.\n");
	} else {
		for (; i < argv; i ++) {
			/*以点为分界读取转换*/
			int j = 0;
			char *ip[4];
			int cnt = 0;

			while (argc[i][j] != '\0') {
				int dd = 0;

				for (; argc[i][j] != '.' && argc[i][j] != '\0'; j++)
					dd = dd*10 + (argc[i][j] - '0');

				ip[cnt] = ddtohex(dd);
				cnt ++;

				if (argc[i][j] == '.')
					j++;
			}
			getip(ip);
		}
	}
	return 0;
}

11.3.2 因特尔域名

域名集合形成了一个层次结构,每个域名编码了它在这个层次中的位置。层次结构可以表示为一棵树。树的节点表示域名,反向到根的路径形成了域名。

DNS系统维护IP到域名的映射

struct hostent
{
  char *h_name;			/* Official name of host.  */
  char **h_aliases;		/* Alias list.  */
  int h_addrtype;		/* Host address type.  */
  int h_length;			/* Length of address.  */
  char **h_addr_list;		/* List of addresses from name server.  */
};

因特尔应用程序通过gethostbyname和gethostbyaddr函数从DNS数据库中搜索任意的主机条目

#include<netdb.h>
struct hostent *gethostbyname(const char* name);
struct hostent *gethostbyaddr(const char* name);
/*出错返回NULL,且同时设置h_errno*/
#include<stdio.h>
#include<netdb.h>
#include<arpa/inet.h>
#include<stdlib.h>

int main(int argc, char **argv)
{
	char **pp;
	struct in_addr addr;/*IP地址*/
	struct hostent *hostp;/*DNS主机条目结构*/

	if (argc != 2) {
		fprintf(stderr, "usage: %s <domain name or dotted-decNS系统维护IP到域名的映射imal>\n", argv[0]);
		exit(0);
	}

	if (inet_aton(argv[1], &addr) != 0)
		hostp = gethostbyaddr((const char*)&addr, sizeof(addr), AF_INET);
	else
		hostp = gethostbyname(argv[1]);

	printf("official hostname: %s\n", hostp->h_name);

	for (pp = hostp->h_aliases; *pp != NULL; pp++)
		printf("alias: %s\n", *pp);

	for (pp = hostp->h_addr_list; *pp != NULL; pp++) {
		addr.s_addr = ((struct  in_addr *)*pp)->s_addr;
		printf("address: %s\n", inet_ntoa(addr));
	}

	exit(0);
}
练习题11.4

在这里插入图片描述

DNS轮转:返回地址的不同顺序,对一个大量使用的域名请求做负载平衡。

11.3.3 因特尔连接

一个套接字是连接的一个端点。每个套接字都有相应的套接字地址,是由因特网地址和一个16位整数端口组成的。一个连接是由它两端的套接字地址唯一确定的。这对套接字地址叫做套接字对。(cliaddr:cliport, servaddr:servport)

11.4 套接字接口

11.4.1 套接字地址结构

/*Generic aocket address structure (for connect, bind, and accept)*/
struct sockaddr {  
    unsigned short  sa_family;      /*Protocol family*/
    char            sa_data[14];    /*Address data*/
};
NS系统维护IP到域名的映射
/*Internet-style socket address structure _in来源*/
struct sockaddr_in {
    unsigned short sin_family;  /*Address family (always AF_INET)*/
    unsigned short sin_port;    /*Port number in network byte order*/
    struct in_addr sin_addr;    /*IP address in network byte order*/
    unsigned char sin_zero[8];  /*Pad to sizeof(struct sockaddr)*/
}; 

11.4.2 socket函数

#include<sys/types.h>
#include<sys/socket.h>

int socket(int domain, int type, int protocol);
/*domain: AF_INET 因特网;  type: SOCK_STREAM 因特网连接的一个端点*/

11.4.3 connect函数

客户端通过调用connect函数建立与服务器的连接,connect返回套接字描述符给客户端的时候,客户端就可以立即开始用UnixI/O和服务器通信了!

#include<sys/socket.h>
int connect(int sockfd, struct sockaddr *serv_addr, int addrlen);
/*成功则为0,出错则为1*/

11.4.4 open_clientfd函数

可以将socket函数和connect函数包装成一个叫open_clientfd的辅助函数,客户端利用它和服务器进行链接。

int open_clientfd(char *hostname, int port);
/*返回:成功-描述符,Unix出错- -1,DNS出错- -2 */

剩余的bind、listen和accept被服务器用来和客户端建立连接

11.4.5 bind函数

#include<sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

告诉内核将my_addr中的服务器套接字地址和套接字描述符sockfd联系起来。

11.4.6 listen函数

#include<sys/socket.h>
int listen(int sockfd, int backlog);

listen函数将sockfd从一个主动套接字转化为一个监听套接字,用来接受客户端的连接请求。

11.4.7 open_listenfd函数

我们将socket,bind和listen结合为这个函数,服务器用它简单地创建一个监听套接字

int open_listenfd(int port);

11.4.8 accept函数

服务器用这个函数来等待来自客户端的连接请求

#include<sys/socket.h>
int accept(int listenfd, struct sockaddr *addr, int *addrlen);

监听描述符是只被创建一次,并存在于服务器的整个生命周期,已连接描述符则是客户端与服务器之间已经建立起来的连接的一个端点。区分两者,有利于我们建立并发服务器,同时处理多个客户端连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值