C语言——网络客户端

举报
yd_221104950 发表于 2020/12/03 00:30:00 2020/12/03
【摘要】 在上一篇文章C语言——网络与套接字介绍了网络服务器的相关知识。现在我们来看看网络客户端的相关知识。 我们来写一个HTTP协议的网络客户端。说到HTTP协议,其实就像我们在C语言——网络与套接字自定义的IAHP协议一样。**协议是一段结构化对话。**网络客户端和服务器必须按照结构化的对话来进行通信。 打开telnet看看是如何下载网页的。 ~$ telnet www...

在上一篇文章C语言——网络与套接字介绍了网络服务器的相关知识。现在我们来看看网络客户端的相关知识。

我们来写一个HTTP协议的网络客户端。说到HTTP协议,其实就像我们在C语言——网络与套接字自定义的IAHP协议一样。**协议是一段结构化对话。**网络客户端和服务器必须按照结构化的对话来进行通信。

打开telnet看看是如何下载网页的。

~$ telnet www.csua.berkeley.edu 80
Trying 128.32.112.230...
Connected to tap.csua.berkeley.edu.
Escape character is '^]'.
GET /officers.html HTTP/1.0

  
 
  • 1
  • 2
  • 3
  • 4
  • 5

输入GET /officers.html HTTP/1.0 然后按两次回车,网络服务器响应如下:

~$ telnet www.csua.berkeley.edu 80
Trying 128.32.112.230...
Connected to tap.csua.berkeley.edu.
Escape character is '^]'.
GET /officers.html HTTP/1.0

HTTP/1.1 404 Not Found
Server: nginx/1.10.3
Date: Thu, 21 Feb 2019 03:43:36 GMT
Content-Type: text/html
Content-Length: 176
Connection: close
ETag: "5bd8e5b2-b0"

<html>
<head><title>404 Domain Not Found</title></head>
<body bgcolor="white">
<center><h1>404 Domain Not Found</h1></center>
<hr><center>nginx/1.10.3</center>
</body>
</html>
Connection closed by foreign host.
~$

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

从上面可以看出客户端连上网络服务器后,至少需要一样东西:
GET命令

GET /officers.html HTTP/1.0

  
 
  • 1

客户端和服务器使用套接字通信,但两者获取套接字的方式不同,服务器要用BLAB四步才能获得套接字:

  1. 绑定端口
  2. 监听
  3. 接受连接
  4. 开始通信

服务器终其一生都在等待新客户端的连接。在客户端连接之前,它什么事都不能做。但客户端就不一样了,客户端想什么时候连接服务器并开始通信都可以。客户端只需两步就可以取得套接字:

  1. 连接远程端口
  2. 开始通信

服务器在连接网络时必须决定使用哪个端口,而客户端除了要知道远程服务器使用的端口号还需要知道远程服务器的IP地址。

创建IP地址套接字

客户端知道了服务器的端口号和IP地址就可以创建客户端套接字了,客户端和服务器处理套接字的方式不同,服务器会把套接字绑定到本地端口,而客户端会把套接字连接至远程端口:

//创建套接字
int d_sock = socket(PF_INET,SOCK_STREAM,0);
struct sockaddr_in si; memset(&si,0,sizeof(si)); si.sin_family = PF_INET; si.sin_addr.s_addr = inet_addr(host); si.sin_port = htons(port); if(d_sock == -1){ error("Can't open socket"); } //把套接字连接到远程端口 int c = connect(d_sock,(struct sockaddr *)&si,sizeof(si)); if(c == -1){ error("Can't connect to socket"); }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

创建域名套接字

域名系统是一本巨大的通讯录。计算机向网络发送数据包时需要用到IP地址,而DNS可以把域名转化为IP地址。通常情况下,应该让客户端用DNS来创建套接字,这样用户就不用自已去查找IP地址了。那么就需要用另外一种方式来创建客户端套接字。
利用getaddrinfo()函数(这个函数可以根据域名找IP地址)在堆上创建一种叫名字资源的新数据结构,然后给定域名和端口号,就可以得到名字资源。名字资源把计算机需要的IP地址隐藏起来,大型网站通常有好几个IP地址,代码会从中挑选一个。随后可以用名字资源创建套接字。

 //用名字资源创建套接字了 struct addrinfo *res; struct addrinfo  hints; memset(&hints,0,sizeof(hints)); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if(getaddrinfo(host,port,&hints,&res) == -1){ error("Can't resolve the address"); } //创建套接字 int d_sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol); if(d_sock == -1){ error("Can't open socket"); } //把套接字连接到远程端口 int c = connect(d_sock,res->ai_addr,res->ai_addrlen); //因为名字资源在堆上创建,所以要用一个叫freeaddrinfo()函数清除它。 freeaddrinfo(res); if(c == -1){ error("Can't connect to socket"); }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

一旦把套接字连接到远程端口,就可以用recv()和send()函数读写数据了。
我们给出一个完整的运行实例test1.c:

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>

void error(char *msg);
int open_socket(char *host,char *port);

int open_socket_ip(char *host,int port);
int say(int socket,char *s);

int main(int argc,char *argv[]){
	int d_sock;
	d_sock = open_socket("www.baidu.com","80");
	char buf[255];
	sprintf(buf,"GET /officers.html HTTP/1.0\r\n\r\n");
	say(d_sock,buf);
	char rec[255];
	int bytesRcvd = recv(d_sock,rec,255,0);
	while(bytesRcvd){ if(bytesRcvd == -1){ error("Can't reed from server!");
		}
		rec[bytesRcvd] = '\0';
		printf("%s",rec);
		bytesRcvd = recv(d_sock,rec,255,0);
	}
	close(d_sock);
	return 0;
}
void error(char *msg){
	fprintf(stderr,"%s:%s\n",msg,strerror(errno));
	exit(1);
}

int open_socket(char *host,char *port){
	struct addrinfo *res;
	struct addrinfo  hints;
	memset(&hints,0,sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	if(getaddrinfo(host,port,&hints,&res) == -1){
		error("Can't resolve the address");
	}
	int d_sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
	if(d_sock == -1){
		error("Can't open socket");
	}
	int c = connect(d_sock,res->ai_addr,res->ai_addrlen);
	freeaddrinfo(res);
	if(c == -1){
		error("Can't connect to socket");
	}
	return d_sock;
}


int say(int socket,char *s){
	int result = send(socket,s,strlen(s),0);
	if(result == -1){
		fprintf(stderr,"%s:%s\n","Error talking to the server",strerror(errno));
	}
	return result;
}

int open_socket_ip(char *host,int port){
	struct sockaddr_in si;
	memset(&si,0,sizeof(si));
	si.sin_family = PF_INET;
	si.sin_addr.s_addr = inet_addr(host);
	si.sin_port = htons(port);
	int d_sock = socket(PF_INET,SOCK_STREAM,0);
	if(d_sock == -1){
		error("Can't open socket");
	}
	int c = connect(d_sock,(struct sockaddr *)&si,sizeof(si));
	if(c == -1){
		error("Can't connect to socket");
	}
	return d_sock;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89

编译运行一下:

~/Desktop/MyC$ gcc test1.c -o test1
~/Desktop/MyC$ ./test1
HTTP/1.0 302 Found
Cache-Control: max-age=86400
Content-Length: 222
Content-Type: text/html; charset=iso-8859-1
Date: Thu, 21 Feb 2019 03:59:50 GMT
Expires: Fri, 22 Feb 2019 03:59:50 GMT
Location: https://www.baidu.com/search/error.html
Server: Apache

<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>302 Found</title>
</head><body>
<h1>Found</h1>
<p>The document has moved <a href="http://www.baidu.com/search/error.html">here</a>.</p>
</body></html>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

还记得我们在C语言——网络与套接字自定义了一个IAHP网络协议,那么我们为那个服务器写一个客户端吧,使用IAHP协议通信。
test1.c

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <strings.h>
//输入错误消息并退出
void error(char *msg);
//使用域名和端口打开套接字
int open_socket(char *host,char *port);
//使用IP和端口打开套接字
int open_socket_ip(char *host,int port);
//发消息
int say(int socket,char *s);
//读取消息
int read_in(int socket,char *buf,int len);

int main(int argc,char *argv[]){
	int d_sock = open_socket_ip("127.0.0.1",30000);//通信用的网络套接字
	say(d_sock,"Who are you?\r\n"); //根据IAHP协议发送消息
	char rec[255];
	int bytesRcvd = recv(d_sock,rec,255,0);
	while(1){ say(d_sock,"Apple who?\r\n\r\n"); //根据IAHP协议发送消息
		if(bytesRcvd == -1){ error("Can't reed from server!");
		}
		rec[bytesRcvd] = '\0';
		printf("%s",rec);
		bytesRcvd = recv(d_sock,rec,255,0); //接收消息
	}
	close(d_sock);
	return 0;
}
void error(char *msg){
	fprintf(stderr,"%s:%s\n",msg,strerror(errno));
	exit(1);
}

int open_socket(char *host,char *port){
	struct addrinfo *res;
	struct addrinfo  hints;
	memset(&hints,0,sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	if(getaddrinfo(host,port,&hints,&res) == -1){
		error("Can't resolve the address");
	}
	int d_sock = socket(res->ai_family,res->ai_socktype,res->ai_protocol);
	if(d_sock == -1){
		error("Can't open socket");
	}
	int c = connect(d_sock,res->ai_addr,res->ai_addrlen);
	freeaddrinfo(res);
	if(c == -1){
		error("Can't connect to socket");
	}
	return d_sock;
}


int say(int socket,char *s){
	int result = send(socket,s,strlen(s),0);
	if(result == -1){
		fprintf(stderr,"%s:%s\n","Error talking to the server",strerror(errno));
	}
	return result;
}

int open_socket_ip(char *host,int port){ int d_sock = socket(PF_INET,SOCK_STREAM,0);
	struct sockaddr_in si;
	memset(&si,0,sizeof(si));
	si.sin_family = PF_INET;
	si.sin_addr.s_addr = inet_addr(host);
	si.sin_port = htons(port);
	if(d_sock == -1){
		error("Can't open socket");
	}
	int c = connect(d_sock,(struct sockaddr *)&si,sizeof(si));
	if(c == -1){
		error("Can't connect to socket");
	}
	return d_sock;
}

int read_in(int socket,char *buf,int len){ char *s = buf; int slen = len; int c = recv(socket,s,slen,0); while((c>0) && (s[c-1] != '\n')){//循环读取字符,直到没有字符可以读或读到了\n s += c;slen -= c; c = recv(socket,s,slen,0); } if(c <0){ return c;//防止错误 }else if(c == 0){ buf[0] = '\0';//什么也没有读到,返回一个空字符串 }else{ s[c-1] = '\0';//用\0替换\r } return len - slen;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108

编译运行一下:
先启动服务器:

~/Desktop/MyC$ ./test
Waiting for connection
|

  
 
  • 1
  • 2
  • 3

再启动客户端:

~/Desktop/MyC$ gcc test1.c -o test1
~/Desktop/MyC$ ./test1
Internet Apple Hello Protocol Server 
 Version 1.0
 Hi ! Hi !
>Apple
>Ha Ha Stupid
<html><body>hello world!</body></html>

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

注:html部分是后来在服务器端加上的。
所以网络客户端是很简单的,建立好套接字,然后去连接远程端口,就可以与服务器通信了。上面仅仅为了显示用而已。

谢谢阅读!

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/87855824

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。