服务器与客户端网络通信方式有多种,既可以使用listen,accept进行监听实现,也可以使用sendto,recvfrom来实现。现将一后者方法经典详解实例贴在这儿供大家学习。
server:
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MAXBUF 256//定义了常量MAXBUF的值256,下面会用到.当然您也可以不用定义,在后面的数组里直接写256这个数字也可以
main()
{
char buf[MAXBUF];//字符串数组buf的大小是256
int passiveSocket;//定义一个int类型的变量passiveSocket,它用来存储套接字描述符
socklen_t clientAddrLen;//类型的变量clientAddrlen,用来接收客户端地址的长度
//定义一个sockaddr_in类型的结构变量serverAddr,用来存储服务器端的IP地址,端口等信息
struct sockaddr_in serverAddr;
//定义一个sockaddr_in类型的结构变量clientAddr,用来存储客户端的IP地址,端口等信息
struct sockaddr_in clientAddr;
//socket函数需要传递3个值: 对于使用IPV4的网络接口,第1个参数必须是AF_INET.由于是UDP传输方式,第2个参数必须是SOCK_DGRAM.对于使用TCP或者UDP的传输,第3个参数都设置为0
passiveSocket=socket(AF_INET,SOCK_DGRAM,0);
//初始化变量serverAddr,使其为数字0
memset(&serverAddr,0,sizeof(serverAddr));
//把serverAddr结构中的sin_family变量赋值为AF_INET,这个值表示TCP/IP网络
serverAddr.sin_family=AF_INET;
//把serverAddr结构中的sin_port变量赋值为1234,这个值代表服务器的接收端口为1234,您可以自行设置.htons函数是必须的,它把主机字节顺序转换为网络字节顺序
serverAddr.sin_port=htons(1234);
//设置要绑定服务器的哪个IP地址.使用INADDR_ANY可以把服务器绑定在本机的所有IP地址.也可以使用inet_addr捆绑在特定端口.因为IP地址是32位的,
//所以需要使用htonl来转换网络字节顺序,而htons是用来转换2个字节16位的端口地址
serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);/* serverAddr.sin_addr.s_addr = inet_addr(”127.0.0.1″); */
//使用bind函数把刚才创建的套接字描述符passiveSocket与服务器套接字结构serverAddr捆绑在一起.
if(bind (passiveSocket,(struct sockaddr *)&serverAddr,sizeof(serverAddr))==-1)
printf ("bind Error!");
clientAddrLen=sizeof(serverAddr);
memset(buf,0,MAXBUF);//初始化buf数组,设置为数字0
//因为是服务器端程序,需要一直打开接收来自客户端的请求,所以必须创建一个死循环
for(;;)
{
//利用recvfrom函数接收来自客户端的信息,并把信息存放在数组buf中.同时也把客户端的地址存放在了clientAddr中,方便信息的回送,本例服务器端为使用回送.
if(recvfrom(passiveSocket,buf,MAXBUF,0,(struct sockaddr *)&clientAddr,&clientAddrLen)>0)
{
//打印输出来buf中自客户端的信息
printf("Come from Client is : %s/n", buf);
memset (buf,0,sizeof(buf));//清空buf
}
}
}
client:
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
//定义了常量MAXBUF的值256,下面会用到.当然您也可以不用定义,在后面的数组里直接写256这个数字也可以
#define MAXBUF 256
main()
{
//给buf数组赋值为Hello World,这个字符串是后面要传送给服务器的信息
char buf[MAXBUF]="Hello World";
//定义一个整型变量activeSocket,它用来存储套接字描述符.
int activeSocket;
//同server.c中注释,设置Internet套接字地址结构变量remoteAddr
struct sockaddr_in remoteAddr;
//同server.c中注释,设置Internet套接字地址结构变量localAddr
struct sockaddr_in localAddr;
//如果链接远程服务器使用URL的方式,则需定义此变量用来接收解析后的IP地址.
struct hostent *hptr;
//同server.c中的注释,创建一个套接字结构,成功创建后把返回的套接字描述符存储在activeSocket
activeSocket=socket(AF_INET,SOCK_DGRAM,0);
//同server.c中的注释
memset(&remoteAddr,0,sizeof(remoteAddr));
//同server.c中注释
remoteAddr.sin_family=AF_INET;
//设置远程服务器的端口地址
remoteAddr.sin_port=htons(1234);
//设置远程服务器的IP地址,本例因为服务器和客户端在同一台机器上,所以使用同一个地址.
remoteAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
/*hptr=gethostbyname(”www.aorb.org”);
memcpy((char*)&remoteAddr.sin_addr.s_addr,hptr->h_addr_list[0],hptr->h_length);*/
//如果链接远程服务器使用URL,则需要用gethostbyname函数得到预解析URL的地址储存在hptr结构中,
//然后把hptr结构中的h_addr_list[0]值拷贝到套接字remoteAddr.sin_addr.s_addr变量. memcpy函数就是起到拷贝的作用.
printf("Remote IP address is: %s…/n",inet_ntoa(remoteAddr.sin_addr));
//发从存储在数组buf中的信息,也就是发送”Hello World”字符串到remoteAddr中指定的IP地址与端口.
sendto (activeSocket,buf,sizeof(buf),0,(struct sockaddr *)&remoteAddr,sizeof(remoteAddr));
//如果sendto成功则显示Send Success!
printf("Send Success!/n");
memset (buf,0,sizeof(buf));//重置buf数组
close(activeSocket);//关闭activeSocket套接字释放内存.
}
//在UNIX系统中,进程若对文件进行操作,一般使用open函数调用打开一个文件进行访问,每个进程都有一个文件描述符表,
//该表中存放了被进程打开的文件的索引(也称文件描述符),索引指出了文件在文件描述符表中的位置,
//这个索引值是一个指向操作系统文件表的指针.应用程序只要使用该描述符就可以对指定文件进行操作.类似的,
//每个打开的socket函数都对应一个整数,我们称它为socket描述符,该整数也是socket描述符在文件描述符表中的索引值.
//但socket描述符在描述符表中的表项并不指向文件表,而是指向一个与该socket有关的数据结构– socket结构.
// 当程序用open函数打开一文件描述符,文件描述符指向一个文件表,而这个表指向文件在硬盘的具体位置.类似的,
//当程序用socket函数创建一个socket结构,但这个结构并不完整,需要使用send或者recvfrom等函数向socket结构填写其他部分,
//以指明预访问的目的地址或接收到的源地址.