网络编程——基于TCP的程序设计和基于UDP的程序设计
网络编程可分为基于TCP的网络程序设计和基于UDP的网络程序设计。TCP是基于字节流的面向连接的,常用于可靠的网络传输,而UDP是基于数据报的无连接的网络传输,常用语即时通信。
无论是基于TCP或者是基于UDP的程序设计,它都是有固定的步骤可循的。只要理解这些步骤,实现起来也是比较简单的。下面将介绍基于TCP和UDP的网络编程的详细步骤以及实现实例。
在介绍网络编程之前,首先要说明一点:Winsock函数是Windows提供的网络编程的借口,无论是基于TCP的还是UDP的网络编程,在程序设计之前,都要首先加载Winsock库。
一、基于TCP的网络应用程序
网络应用程序都是基于C/S(客户端/服务器)模式的,因此在进行网络应用程序开发时,不仅要开发服务器应用程序也要开发客户端应用程序。开发服务器应用程序和客户端应用程序在步骤上略有不同。下面介绍一下基于TCP的网络应用程序开发的详细步骤:
服务器端应用程序: 客户端应用程序:
1、创建socket套接字 1、创建socket套接字
2、将套接字绑定(bind)到指定的本机IP地址和端口上
3、将套接字设为监听模式(listen),准备接受客户端的请求 2、向服务器发送连接请求(connect)
4、等待客户端请求的到来(accept),并返回新的套接字进行通信
5、服务器和客户端相互通信(send/recv) 3、服务器和客户端相互通信(send/recv)
6、返回继续等待新的客户端请求到来
7、关闭socket套接字 4、关闭socket套接字
注释:服务器要绑定端口,监听客户端请求,当接受到请求后才开始通信。而客户端只需要先发送请求,只要请求被接收后就可以通信了。
在理解示例代码之前,先介绍一些知识点和函数:
第一点:在网络编程中,要用到IP地址和端口号,比如在bind()和accept()函数中都需要有到IP地址和端口号,在Windows API中有一个SOCKADDR_IN结构体中可以保存IP地址和端口号的信息。
第二点:服务器要绑定的IP地址应该用(INADDR_ANY)属性,表示服务器可以接受任何端口发送来的连接请求,这是因为有的机器可能有多个网卡,因此可能有多个IP地址,这样设定可以方便后面的程序开发。
第三点:网络通信中用到的是网络字节序,intel的机器本机字节序和网络字节序的存放格式是不一样的,所以要用想用的函数进行转化。
inet_addr()将点分十进制的IP地址转化为u_long型
inet_ntoa()将in_addr结构类型的参数转化为点分十进制的IP地址
htonl()将u_long型的IP地址从主机字节序转换为网络字节序
htons()将u_short型的IP地址从主机字节序转换为网络字节序
第四点:网络编程要用到Winsock库,,所以不仅要加载winsock的头文件,并且要绑定ws2_32.lib动态链接库。绑定动态链接库有两种方法。第一种就是在工程的“属性”里设置“Link”的链接库加上ws2_32.lib就可以了。第二种方法就是在工程的源文件中加上代码:#pragma comment(lib,"ws2_32.lib")就可以了。
/**************************************************************
基于TCP的服务器应用程序示例代码
****************************************************************/
#include "stdafx.h"
#include <stdlib.h>
#include <Winsock2.h>
#include <stdio.h>
void main()
{
/**************************************************************
加载Winsock库
****************************************************************/
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;
}
/**************************************************************
第一步:创建Winsock套接字
****************************************************************/
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,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,(const sockaddr*)&addrSrv,sizeof(SOCKADDR));
/**************************************************************
第三步:将套接字设置为监听模式,准备接受客户端的请求
****************************************************************/
listen(sockSrv,5);
/**************************************************************
第四步:接受客户端的请求
第五步:接收客户端的消息和向客户端发送消息
第六步:返回等待
第七步:关闭套接字
****************************************************************/
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
while(1)
{
SOCKET sockCon=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
char sendBuf[100];
sprintf(sendBuf,"This is server,Welcome%s",inet_ntoa(addrClient.sin_addr));
send(sockCon,sendBuf,strlen(sendBuf)+1,0);
char recvbuf[100];
recv(sockCon,recvbuf,strlen(recvbuf)+1,0);
printf("%s\n",recvbuf);
closesocket(sockCon);
}
system("PAUSE");
}
/**************************************************************
基于TCP的客户端应用程序示例代码
****************************************************************/
#include "stdafx.h"
#include <stdlib.h>
#include <Winsock2.h>
#include <stdio.h>
void main()
{
/**************************************************************
加载Winsock库
****************************************************************/
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;
}
/**************************************************************
第一步:创建Winsock套接字
****************************************************************/
SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
/**************************************************************
第二步:向服务器发送连接请求
****************************************************************/
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,(const sockaddr*)&addrSrv,sizeof(SOCKADDR));
/**************************************************************
第三步:客户端和服务器的相互通信
****************************************************************/
char recvbuf[100];
recv(sockClient,recvbuf,strlen(recvbuf)+1,0);
printf("%s",recvbuf);
send(sockClient,"My name is zhangsan Client",strlen("My name is zhangsan Client")+1,0);
closesocket(sockClient);
WSACleanup();
system("PAUSE");
}
先运行服务器程序再运行客户端程序,运行结果如下:
二、基于UDP的网络应用程序
上面介绍了基于TCP的网络应用程序,为了便于比较,下面介绍基于UDP的网络应用程序的设计方法。
和上面一样,先接受基于UDP的网络应用程序的开发步骤:
服务器端应用程序: 客户端应用程序:
1、创建socket套接字 1、创建socket套接字
2、将套接字绑定(bind)到指定的本机IP地址和端口上 2、向服务器发送消息(sendto)
3、如果检测到有消息到来就接收消息(recvfrom)
4、关闭socket套接字 3、关闭socket套接字
注释:由于基于UDP的网络应用程序是面向无连接的,所以不需要服务器的监听,也不需要客户端的连接请求。实现起来比TCP的面向连接的简单,适用于即时通信。主要用到的函数和方法和TCP的设计方法大致一样。示例代码如下:
/**************************************************************
基于UDP的服务器应用程序示例代码
****************************************************************/
#include "stdafx.h"
#include <stdlib.h>
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/**************************************************************
加载Winsock库
****************************************************************/
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;
}
/**************************************************************
第一步:创建Winsock套接字
****************************************************************/
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,(const sockaddr*)&addrSrv,sizeof(SOCKADDR));
/**************************************************************
第三步:等待并接受客户端发送的消息
****************************************************************/
SOCKADDR_IN addrClient;
char recvBuf[100];
int len=sizeof(SOCKADDR);
recvfrom(sockSrv,recvBuf,100,0,(sockaddr*)&addrClient,&len);
printf("%s",recvBuf);
char sendBuf[100];
sprintf(sendBuf,"This is UDP Serve! Welcome %s",inet_ntoa(addrClient.sin_addr));
sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,
(const sockaddr*)&addrClient,sizeof(SOCKADDR));
closesocket(sockSrv);
WSACleanup();
system("pause");
}
/**************************************************************
基于UDP的客户端应用程序示例代码
****************************************************************/
#include "stdafx.h"
#include <stdlib.h>
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main()
{
/**************************************************************
加载Winsock库
****************************************************************/
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;
}
/**************************************************************
第一步:创建Winsock套接字
****************************************************************/
SOCKET sockClient=socket(AF_INET,SOCK_DGRAM,0);
/**************************************************************
第一步:向服务器发送消息
****************************************************************/
SOCKADDR_IN addrClient;
addrClient.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrClient.sin_family=AF_INET;
addrClient.sin_port=htons(6000);
sendto(sockClient,"My name is UDP Client!",strlen("My name is UDP Client!")+1,0,
(const sockaddr*)&addrClient,sizeof(SOCKADDR));
char recvBuf[100];
int len=sizeof(SOCKADDR);
recvfrom(sockClient,recvBuf,100,0,(sockaddr*)&addrClient,&len);
printf("%s",recvBuf);
closesocket(sockClient);
WSACleanup();
system("pause");
}
运行结果如下: