一、背景
之前我讲到管道,管道是一种很便捷的进程间通信方式,使得进程之间传输数据就像给文件传输数据一样,但是管道也有其局限性,一方面它只能在相关的进程间使用,另一方面它只能连接同一台主机上的进程。所以我在这里讨论另一种进程间通信方式:socket。
同时我会编写一个简单的时间服务器与对应的客户端来理解运作机理。
二、socket
socket的英文原义是“插座”,通常也称作“套接字”。用于描述IP地址和端口,可以用来实现不同虚拟机或不同计算机之间的通信。socket允许在不相关的进程见创建类似管道的连接,甚至可以通过socket连接其他主机上的进程。
三、重要概念
1、客户端与服务器
顾名思义,服务器是提供服务的进程,而客户端是被服务的对象。服务器进程等待请求,在处理完请求后循环回去等待下一个请求。客户端则无需循环,需要服务时访问服务器即可。
2、主机名和端口
运行于internet上的服务器其实是某台计算机上运行的一个进程。这里计算机被称为主机。而服务器在该主机上拥有一个端口。主机和端口的组合才标识了一个服务器。
3、协议
协议是服务器与客户端之间交互的规则。每个客户端/服务器模型都必须定义一个协议。
四、端口
我们可以通过more/etc/services来查看各个端口号及其服务。
五、编写时间服务器程序
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<time.h>
#include<strings.h>
#include<stdlib.h>
#define PORTNUM13000
#define HOSTLEN 256
#define oops(msg){perror(msg);exit(1);}
int main(int ac,char*av[])
{
struct sockaddr_insaddr;
struct hostent*hp;
charhostname[HOSTLEN];
intsock_id,sock_fd;
FILE *sock_fp;
char *ctime();
time_t thetime;
sock_id =socket(PF_INET,SOCK_STREAM,0);
if(sock_id == -1)
oops("socket");
bzero((void*)&saddr,sizeof(saddr));
gethostname(hostname,HOSTLEN);
hp =gethostbyname(hostname);
bcopy((void*)hp->h_addr,(void *)&saddr.sin_addr,hp->h_length);
saddr.sin_port =htons(PORTNUM);
saddr.sin_family =AF_INET;
if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr)) != 0)
oops("bind");
if(listen(sock_id,1) != 0)
oops("listen");
while(1){
sock_fd =accept(sock_id,NULL,NULL);
printf("Wow!got a call!\n");
if(sock_fd ==-1)
oops("accept");
sock_fp =fdopen(sock_fd,"w");
if(sock_fp ==NULL)
oops("fopen");
thetime =time(NULL);
fprintf(sock_fp,"The time here is..");
fprintf(sock_fp,"%s",ctime(&thetime));
fclose(sock_fp);
}
}
六、编写客户端程序
#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<netdb.h>
#include<string.h>
#define oops(msg){perror(msg);exit(1);}
int main(int ac,char*av[])
{
struct sockaddr_inservadd;
struct hostent*hp;
intsock_id,sock_fd;
charmessage[BUFSIZ];
int messlen;
sock_id =socket(AF_INET,SOCK_STREAM,0);
if(sock_id == -1)
oops("socket");
bzero(&servadd,sizeof(servadd));
hp =gethostbyname(av[1]);
if(hp == NULL)
oops(av[1]);
bcopy(hp->h_addr,(struct sockaddr*)&servadd.sin_addr,hp->h_length);
servadd.sin_port =htons(atoi(av[2]));
servadd.sin_family= AF_INET;
if(connect(sock_id,(struct sockaddr *)&servadd,sizeof(servadd))!= 0)
oops("connect");
messlen =read(sock_id,message,BUFSIZ);
if(messlen == -1)
oops("read");
if(write(1,message,messlen) != messlen)
oops("write");
close(sock_id);
}
七、测试
我这里是在自己的主机上进行试验的。先使用hostname命令看自己的主机名,然后使用13000端口进行时间查询服务。
八、总结
这篇博客主要讲解了客户端与服务器的概念和socket的简单使用。意在继续进行深入的网络编程的学习。