一、基础
计算机是一栋大楼,里面有很多房间,这些房间是程序或者服务。端口是房间的门牌号,比如 80 号房和443 号房。数据像快递包裹,通过门牌号被准确送到对应的房间,即通过端口被送给对应的程序。
这里有一些需要注意的地方。首先,门牌号不能重复,一栋楼里不能有两个 80 号房间;其次,常用的门牌号有固定的用途,比如80号房间专门收“网页快递”,22号房间收“远程控制快递”;最后,有些门牌号需要特权,比如1024以下的端口,如80、443,需要管理员权限sudo才能使用,就像VIP房间,而1024以上的端口,如3000和8000等,普通用户就可以直接使用。
二、代码
1.客户端程序
client.cc代码如下:
extern "C" {
#include "unp.h"
}
int main(int argc, char **argv) {
int sockfd, n;
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;
servaddr.sin_port = htons(1013);
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) {
recvline[n] = 0;
if (fputs(recvline, stdout) == EOF) {
err_sys("fputs error");
}
}
if (n < 0) {
err_sys("read error");
}
exit(0);
}
2.服务器程序
web.cc代码如下:
extern "C" {
#include "unp.h"
}
#include <time.h>
int main(int argc, char **argv) {
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[MAXLINE];
time_t ticks;
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(1013);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
for(;;) {
connfd = Accept(listenfd, (SA *) NULL, NULL);
ticks = time(NULL);
snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
Write(connfd, buff, strlen(buff));
Close(connfd);
}
}
客户端与服务器程序形成一对“一问一答”的关系。服务端程序像接电话的服务台,一直等待别人打进来,客户端程序像打电话的人,会主动拨号联系服务端。服务端代码的步骤为
socket()->bind()->listen()->accept()->write()->close()
客户端代码的步骤为
socket()->connect()->read()->close()
服务端程序通过bind(listenfd,...)
绑定到1013号端口,用listen()
和accept()
循环等待客户端连接,连接后通过write()
发送时间给客户端。客户端程序通过connect(sockfd,...)
连接到服务端1013号端口,用read()
读取服务端程序返回的数据,并打印到屏幕。
服务端运行在本机(IP为127.0.0.1)的1013号端口,客户端与其交互过程如下:
sudo ./web #启动服务端
./client 127.0.0.1 #启动客户端
综上,服务端守株待兔,绑定固定端口,等待连接并响应数据。客户端主动出击,通过IP和端口找到服务端,获取数据。