使用TCP/IP协议的应用程序通常采用两种应用编程接口(API):socket和TLI(运输层接)。前者有时称作"Berkeley socket",表明它是从伯克利版发展而来的。后者起初是由AT & T开发的,有时称作XTI(X/Open运输层接口),以承认X/Open这个自己定义标准的国际计算机生产商所做的工作。XTI实际上是TLI的一个超集。而在windows操作系统中,实现了windows版本的socketAPI,又称winsock。
大家都知道不管是TCP/IP还是OSI的ISO协议都是分层模式的,用层的概念屏蔽的下层的细节,只要完成自层的功能即可,因而程序员在做网络编程时并不需要去关心网络底层的具体实现,只需要关心软件的功能即可极大的简化了程序的编写。因而我们在学习网络socket编程时并不一定要很多的网络方面的知识,甚至是TCP/IP协议的知识也不需要太多。因而在着我就不介绍那方面的知识了,如果要了解网上也四处都有那些被别人应用了N遍的经典的介绍。 在这只介绍基于TCP和UDP的简单的编程实现。
现今的网络程序一般都是基于C/S模型,即客户机-服务器模型。这种结构将主要运算操作放在中心计算机上。同集中式大型计算系统比较,"客户-服务器"结构的主要优点 是提供了良好的实用性、灵活性、交互性和可扩展性。"客户-服务器"以数据库服务器取代集中式文件共享进而实现了计算机系统之间的松耦合。
"客户-服务器"(Client/Server)是典型的Web信息系统模式。"客户-服务器"一词在20世纪80年代首先被提出,起初,主要指个人计算机和Web的连接,在互联网中,主要指计算机系统之间通过Web的信息交互传递模式;"客户"是指信息服务的索取方,"服务器"指服务的提供方, 根据软件的不同设置,一台计算机可以是客户也可以是服务器。随着Web科技的发展,"客户-服务器"软件框架成为一种灵活、分布式和模块化的信息系统结构。
Windows Sockets是Microsoft Windows的网络程序设计接口,它是从Berkeley Sockets扩展而来的,以动态链接库的形式提供给我们使用。Windows Sockets在继承了Berkeley Sockets主要特征的基础上,又对它进行了重要扩充。这些扩充主要是提供了一些异步函数,并增加了符合Windows消息驱动特性的网络事件异步选择机制。Windows Sockets 1.1和Berkeley Sockets都是基于TCP/IP协议的;Windows Sockets 2从Windows Sockets 1.1发展而来,与协议无关并向下兼容,可以使用任何底 层传输协议提供的通信能力,来为上层应用程序完成网络数据通讯,而不关心底层网络链路的通讯情况,真正实现了底层网络通讯对应用程序的透明。
套接字的类型总共有三类:
1) 流式套接字(SOCK_STREAM)
提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。这种类型是基于TCP协议的。
2) 数据报式套接字(SOCK_DGRAM)
提供无连接服务。数据包以独立包形式发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。 这种类型是基于UDP协议的
3) 原始套接字(SOCK_RAW)。
用于编写基于IP协议的程序。 它可以访问ICMP和ICMP等协议包,可以编写内核不处理的IP数据包,还可以创建自定义的IP数据包首部。
基于 TCP( 面向连接 ) 的 socket 编程的流程:
服务器端程序:
1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、将套接字设为监听模式,准备接收客户请求(listen)。
4、等待客户请求到来;当请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。
5、用返回的套接字和客户端进行通信(send/recv)。
6、返回,等待另一客户请求。
7、关闭套接字。
程序代码:
//1.创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == WSAGetLastError())
MessageBox(NULL, "Create Socket failed!", "Error", MB_OK);
//2.绑定套接字到一个端口号
SOCKADDR_IN addrSrv;
memset(&addrSrv, 0, sizeof(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));
//3.设置套接字为监听模式
listen(sockSrv, /*SOMAXCONN*/ 5);
//4.等待用户的连接到来
SOCKADDR_IN addrClient;
int sockLen = sizeof(SOCKADDR);
while(1)
{
SOCKET sockConn = accept(sockSrv, (SOCKADDR*)&addrClient, &sockLen);
if(INVALID_SOCKET == WSAGetLastError())
MessageBox(NULL, "Connect client failed!", "Error", MB_OK);
//5.用返回的套接字和用户通信,发送数据或是接受数据
char sendBuf[100];
wsprintf(sendBuf, "welcome %s to my computer",
inet_ntoa(addrClient.sin_addr));
send(sockConn, sendBuf, sizeof(sendBuf) + 1, 0);
if(SOCKET_ERROR == WSAGetLastError())
MessageBox(NULL, "Server send data failed!", "Error", MB_OK);
char recvBuf[100];
recv(sockConn, recvBuf, 100, 0);
if(SOCKET_ERROR == WSAGetLastError())
MessageBox(NULL, "Server recieve data failed!", "Error", MB_OK);
printf("%s/n", recvBuf);
//6.关闭申请的套接字资源
closesocket(sockConn);
}
客户端程序:
1、创建套接字(socket)。
2、向服务器发出连接请求(connect)。
3、和服务器端进行通信(send/recv)。
4、关闭套接字。
程序代码:
//1.创建套接字
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(INVALID_SOCKET == WSAGetLastError())
MessageBox(NULL, "Create Socket failed!", "Error", MB_OK);
//2.向服务器发送连接请求
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);
connect(sockClient, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
//3.和服务器进行通信,接收或是发送数据
char recvBuf[100];
recv(sockClient, recvBuf, 100, 0);
if(SOCKET_ERROR == WSAGetLastError())
MessageBox(NULL, "Server recieve data failed!", "Error", MB_OK);
printf("%s/n", recvBuf);
char sendBuf[] = "Hello Server";
send(sockClient, sendBuf, sizeof(sendBuf) + 1, 0);
if(SOCKET_ERROR == WSAGetLastError())
MessageBox(NULL, "Server send data failed!", "Error", MB_OK);
//4.关闭套接字的资源,以及卸载套接字动态链接库
closesocket(sockClient);
WSACleanup();
基于 UDP( 面向无连接 ) 的 socket 编程
服务器端(接收端)程序:
1、创建套接字(socket)。
2、将套接字绑定到一个本地地址和端口上(bind)。
3、等待接收数据(recvfrom)。
4、关闭套接字。
程序代码:
//1.创建套接字
SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(INVALID_SOCKET == WSAGetLastError())
MessageBox(NULL, "Create Socket failed!", "Error", MB_OK);
//2.绑定套接字到指定的端口
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(5000);
bind(sockSrv, (SOCKADDR*)&addrSrv, sizeof(SOCKADDR));
if(SOCKET_ERROR == WSAGetLastError())
MessageBox(NULL, "bind fuction failed!", "Error", MB_OK);
//3.等待接收数据
char recvBuf[100];
SOCKADDR_IN addrClient;
int fromLen = 100;
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &fromLen);
if(SOCKET_ERROR == WSAGetLastError())
MessageBox(NULL, "recvfrom fuction failed!", "Error", MB_OK);
printf("%s/n", recvBuf);
//4.关闭套接字
closesocket(sockSrv);
WSACleanup();
客户端(发送端)程序:
1、创建套接字(socket)。
2、向服务器发送数据(sendto)。
3、关闭套接字。
程序代码:
//1.创建套接字
SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(INVALID_SOCKET == WSAGetLastError())
MessageBox(NULL, "Create Socket failed!", "Error", MB_OK);
//2.向服务器发送数据
char sendBuf[100] = "UDP Server, Good evening!";
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(5000);
int toLen = 100;
sendto(sockClient, sendBuf, sizeof(sendBuf) + 1, 0, (SOCKADDR*)&addrSrv, toLen);
//3.关闭套接字
closesocket(sockClient);
WSACleanup();