实验三、Socket编程实验
一、 实验目的
1. 学习Sockets编程基础知识和基于C的Socket编程相关函数和数据类型
2. 实现一个简单的客户机/服务器程序
3. 熟练掌握UDP、TCP Client/Server模式的通信原理。熟练掌握socket编程命令
二、实验原理
一个简单的客户机/服务器程序的实现
l 它用套接字接口在TCP连接上发送消息。这个程序还用到了其他的UNIX网络功能。
l 允许用户在一端的机器上输入并把文本发送给另一端机器的用户。它是UNIX中talk的一个简化版本,类似于WEB 聊天室的核心程序。
客户端
1. 客户端用远端的机器名作为参数。它调用UNIX程序gethostbyname把该名字转化为远端主机的IP地址。
2. 下一步构造套接字接口所需的地址数据结构(sin)。注意这个数据结构表明我们将一直用套接字与因特网(AF_INET)连接。在这个例子中,我们用TCP端口号5432作为共知的服务器端口号;它恰好不是分配给其他因特网服务的端口号。
3. 建立连接的最后一步是调用socket和connect。一旦connect操作返回,建立起连接,客户程序将进入主循环,不断从标准输入读文本并通过套接字发送。
服务器
1. 服务器首先填上自己的端口号(SERVER_PORT)构造地址数据结构。
2. 其次,它并不指明IP地址,从而使应用程序可以接受来自本地任一IP地址的连接。
3. 然后,服务器执行与被动打开有关的初始步骤:建立一个套接字,将它绑定到本地地址,然后设置允许同时连接的最大数。
4. 最后,主循环等待远端主机试图与之连接,当远端有一台主机试图与之连接时,它就接收并输出连接上送来的字符。
Sockets编程基础知识
网络编程就是通过计算机网络与其他程序进行通信的程序,Socket编程是网络编程的主流工具。
socket API出现于20世纪80年代早期,作为Berkeley Unix(BSD 4.2)操作系统程序库来通过进程间通信功能。现在主流操作系统都提供socket API。
在基于Unix系统中,如BSD、Linux系统,socket API是操作系统内核的一部分。
在MS-DOS、Windows OS、OS/2等操作系统中,socket API是以程序库形式提供的,如在Windows系统中,socket API被称为Winsock。Socket API是实现进程间通信的一种编程设施,也是一种为进程间提供底层抽象的机制。
尽管应用开发人员很少需要在该层编写代码,但是理解socket API还是非常重要的。
第一,高层设施是构建于socket API之上的,它们是利用socket API提供的操作来实现。
第二,对于响应时间要求较高或运行于有限资源平台上的应用,甚至socket API是唯一可用的进程间通信设施。
Socket接口规范可以适用多种通讯协议,主要是TCP/IP。TCP/IP是计算机互联最常适用的网络通讯协议,TCP/IP的核心部分由网络操作系统的内核实现,应用程序通过编程接口来访问TCP/IP。
TCP/IP使用一个网络地址和一个服务端口号来惟一地标识设备。
l 网络地址标识网络上的特定设备
l 端口号标识要连接到的该设备上的特定服务
网络通讯的基本模式如下:每一台通讯的主机都有一个本网络环境中惟一的IP地址,一台主机上往往有多个通讯程序存在,每个这样的程序都要占用一个通讯端口。因此,一个IP地址,一个通讯端口,就能确定一个通讯程序的位置。
TCP传输控制协议(Transport Control Protocol)是一种面向连接的,可靠的传输层协议。面向连接是指一次正常的TCP传输需要通过在TCP客户端和TCP服务端建立特定的虚电路连接来完成,该过程通常被称为“三次握手”。
可靠性可以通过很多种方法来提供保证,在这里我们关心的是数据序列和确认。
1. TCP通过数据分段(Segment)中的序列号保证所有传输的数据可以在远端按照正常的次序进行重组,而且通过确认保证数据传输的完整性。
2. 要通过TCP传输数据,必须在两端主机之间建立连接。举例说明,TCP客户端需要和TCP服务端建立连接。
第一步,客户端向服务端提出连接请求。这时TCP SYN标志置位。客户端告诉服务端序列号区域合法,需要检查。客户端在TCP报头的序列号区中插入自己的ISN。
第二步,服务端收到该TCP分段后,在第二步以自己的ISN回应(SYN标志置位),同时确认收到客户端的第一个TCP分段(ACK标志置位)。
第三步,客户端确认收到服务端的ISN(ACK标志置位)。到此为止建立完整的TCP连接,开始全双工模式的数据传输过程。
三、 实验内容
根据以上内容编写一个TCP Client/Server模式的通信程序。
事实上网络程序是由两个部分组成的--客户端和服务器端。它们的建立步骤如下
服务器端
socket-->bind-->listen-->accept
客户端
socket-->connect
实验按下述步骤进行:
1. 编写UDP、TCP Client/Server模式的通信程序;
2. 调试并运行自己编写的实现程序;
了解TCP Client/Server模式的工作原理,比较二者的不同,如出现异常情况,在实验报告中写出原因分析。
此程序摘录孙鑫《VC++深入详解》
UDP、TCP Client/Server模式的通信程序
//TCP server:
#include
#include
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("222.25.177.246");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
char recvBuf[100];
recv(sockClient,recvBuf,100,0);
printf("%s\n",recvBuf);
send(sockClient,"This is Weijian Peng!",strlen("This is Weijianpeng!")+1,0);
closesocket(sockClient);
WSACleanup();
}
TCP client
#include
#include
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("222.25.177.246");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
char recvBuf[100];
recv(sockClient,recvBuf,100,0);
printf("%s\n",recvBuf);
send(sockClient,"This is Weijian Peng!",strlen("This is Weijian Peng!")+1,0);
closesocket(sockClient);
WSACleanup();
}
//UDP server
#include
#include
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_DGRAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
char recvBuf[100];
recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);
printf("%s\n",recvBuf);
closesocket(sockSrv);
WSACleanup();
}
//UDP client
#include
#include
void main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 1, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return;
}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
return;
}
SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr=inet_addr("222.25.177.246");
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
sendto(sockClient,"Hello,my name is pengweijian!",strlen("Hello,my name is pengweijian!")+1,0,
(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
closesocket(sockClient);
WSACleanup();
}