一、实验目的
1.熟练掌握socket编程命令。
2.为下一步实验打好基础。
二、实验原理
1.网络编程就是通过计算机网络与其他程序进行通信的程序,Socket编程是网络编程的主流工具。
2.Socket API是实现进程间通信的一种编程设施,也是一种为进程间提供底层抽象的机制。
3.尽管应用开发人员很少需要在该层编写代码,但是理解socket API还是非常重要的。主要有两点原因:
第一,高层设施是构建于socket API之上的,它们是利用socket API提供的操作来实现。
第二,对于响应时间要求较高或运行于有限资源平台上的应用,甚至socket API是唯一可用的进程间通信设施。
4.socket API出现于20世纪80年代早期,作为Berkeley Unix(BSD 4.2)操作系统程序库来通过进程间通信功能。
5.现在主流操作系统都提供socket API。在基于Unix系统中,如BSD、Linux系统,socket API是操作系统内核的一部分;在MS-DOS、Windows OS、OS/2等操作系统中,socket API是以程序库形式提供的,如在Windows系统中,socket API被称为Winsock。
6.Socket接口规范可以适用多种通讯协议,主要是TCP/IP。TCP/IP是计算机互联最常适用的网络通讯协议,TCP/IP的核心部分由网络操作系统的内核实现,应用程序通过编程接口来访问TCP/IP。
7.TCP/IP使用一个网络地址和一个服务端口号来惟一地标识设备。
8.网络地址标识网络上的特定设备;端口号标识要连接到的该设备上的特定服务。
9.网络通讯的基本模式如下:每一台通讯的主机都有一个本网络环境中惟一的IP地址,一台主机上往往有多个通讯程序存在,每个这样的程序都要占用一个通讯端口。
10.因此,一个IP地址,一个通讯端口,就能确定一个通讯程序的位置。
三、实验内容
我们来看一个简单的客户机/服务器程序的实现,它用套接字接口在TCP连接上发送消息。这个程序还用到了其他的UNIX网络功能,我们将逐个介绍。我们的应用允许用户在一端的机器上输入并把文本发送给另一端机器的用户。它是UNIX中talk的一个简化版本,类似于WEB 聊天室的核心程序。
Client
我们先从客户端开始,它用远端的机器名作为参数。它调用gethostbyname把该名字转化为远端主机的IP地址。下一步构造套接字接口所需的地址数据结构(sin)。注意这个数据结构表明我们将一直用套接字与因特网(AF_INET)连接。在这个例子中,我们用TCP端口号5432作为共知的服务器端口号;它恰好不是分配给其他因特网服务的端口号。建立连接的最后一步是调用socket和connect。一旦connect操作返回,建立起连接,客户程序将进入主循环,不断从标准输入读文本并通过套接字发送。
Server
服务器的实现也很简单。首先,它填上自己的端口号(SERVER_PORT)构造地址数据结构。其次,它并不指明IP地址,从而使应用程序可以接受来自本地任一IP地址的连接。然后,服务器执行与被动打开有关的初始步骤:建立一个套接字,将它绑定到本地地址,然后设置允许同时连接的最大数。最后,主循环等待远端主机试图与之连接,当远端有一台主机试图与之连接时,它就接收并输出连接上送来的字符
四、实验报告
1. 详细描述实验过程。
2.实现1种数据通信传输
实验在Fedora 16 Linux下进行,实现了一个简单的socket通信的过程:客户端建立socket向指定服务器发送消息,服务器一直监听socket,一旦接收到消息就打印出来。
首先编写客户端代码如下:
/***************************************************************
*客户端程序
*Author:DuanCong
*Date: 2012/11/3
***************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
//一行的最大字节数
#define MAXLINE 4096
int main(int argc, char** argv)
{
int sockfd, n;
char recvline[4096], sendline[4096];//发送和接受行数组
struct sockaddr_in servaddr;//参数
struct hostent *hptr;//记录主机信息的结构
//判断是否输入参数
if( argc != 2){
//为输入主机名作为参数的时候提示使用方法
printf("使用方法: ./client \n");
exit(0);
}
//创建一个套接字
if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
//创建失败
printf("创建socket错误: %s(错误号: %d)\n", strerror(errno),errno);
exit(0);
}
//清空地址参数结构
memset(&servaddr, 0, sizeof(servaddr));
//配置地址族和端口号
servaddr.sin_family = AF_INET;//地址族
servaddr.sin_port = htons(5432);//端口号
//根据输入的主机名获取主机信息
if((hptr = gethostbyname(argv[1])) == NULL)
{
//获取失败
printf("根据主机名: %s\n 获取主机信息失败", argv[1]);
exit(0);
}
//从主机信息中提取出主机ip地址
char *ip=inet_ntoa(*((struct in_addr*)hptr->h_addr));
printf("主机IP地址: %s\n",ip);
//将IP地址从点分十进制转换成整数
if( inet_pton(AF_INET, ip, &servaddr.sin_addr) <= 0){
printf("IP地址转换成整数失败,IP: %s\n",ip);
exit(0);
}
//if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
//printf("inet_pton error for %s\n",argv[1]);
//exit(0);
//}
//与远程服务器建立TCP连接
if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
printf("连接错误: %s(错误号: %d)\n",strerror(errno),errno);
exit(0);
}
//提示和输入要发送的消息
printf("请输入要发送的消息: \n");
fgets(sendline, 4096, stdin);
//传输数据
if( send(sockfd, sendline, strlen(sendline), 0) < 0)
{
//传输失败
printf("传输消息错误: %s(错误号: %d)\n", strerror(errno), errno);
exit(0);
}
//关闭sockedt连接
close(sockfd);
exit(0);
}
服务器端程序如下:
/********************************************************************
*服务器端程序
*Author: DuanCong
*Date: 2012/11/3
* *****************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
//最大接收字节数
#define MAXLINE 4096
int main(int argc, char** argv)
{
int listenfd, connfd;
struct sockaddr_in servaddr;
char buff[4096];
int n;
//创建一个用于监听的套接字
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
//创建失败
printf("创建socket错误: %s(错误号: %d)\n",strerror(errno),errno);
exit(0);
}
//清空地址结构
memset(&servaddr, 0, sizeof(servaddr));
//配置地址族和端口号
servaddr.sin_family = AF_INET;//地址族
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//不指名IP,表示接受来自任何IP地址的连接
servaddr.sin_port = htons(5432);//端口号
//将套接字绑定到本地地址
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
//绑定失败
printf("绑定套接字错误: %s(错误号: %d)\n",strerror(errno),errno);
exit(0);
}
//监听端口并设置最大请求数为10
if( listen(listenfd, 10) == -1){
//监听失败
printf("监听套接字错误: %s(错误号: %d)\n",strerror(errno),errno);
exit(0);
}
//提示开始等待客户端的请求
printf("======等待客户端的请求======\n");
//开始等待请求并接收
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
//接收错误,循环继续
printf("接收请求错误: %s(错误号: %d)",strerror(errno),errno);
continue;
}
//接收数据
n = recv(connfd, buff, MAXLINE, 0);
buff[n] = '\0';//添加字符串结束符
//打印出接收到的消息
printf("从客户端收到消息: %s", buff);
//关闭socket连接
close(connfd);
}
close(listenfd);
}
接着使用gcc 4.6.3进行编译:
gcc client.c -o client
gcc server.c -o server
编译完成产生两个可执行文件client和server。
首先运行服务器端程序server
再运行客户端程序client,运行之前,先查看本机主机名称:
可以看出主机名为localhost.localdomain
继续运行client,以上面查看的主机名为参数,并输入要发送的消息
消息发送之后,socket自动关闭,服务器端收到消息
继续发送几条消息,服务器端同时显示出来