TCP/UDP基础入门

本文详细介绍了使用Winsock实现UDP广播发送与接收的步骤及代码示例,对比TCP连接过程,涵盖WSAStartup、socket、setsockopt、sendto、recvfrom等关键函数的使用。

5.1UDP实例 UDP发送程序的步骤如下:

1. 用WSAStartup函数初始化Socket环境;

2. 用socket函数创建一个套接字;

3. 用setsockopt函数设置套接字的属性,例如设置为广播类型;很多时候该步骤可以省略;

4. 创建一个sockaddr_in,并指定其IP地址和端口号;

5. 用sendto函数向指定地址发送数据,这里的目标地址就是广播地址;注意这里不需要绑定,即使绑定了,其地址也会被sendto中的参数覆盖;若使用send函数则会出错,因为send是面向连接的,而UDP是非连接的,只能使用sendto发送数据;

6. 用closesocket函数关闭套接字;

7. 用WSACleanup函数关闭Socket环境。

 

UDP接收程序的步骤如下(注意接收方一定要bind套接字):

1. 用WSAStartup函数初始化Socket环境;

2. 用socket函数创建一个套接字;

3. 用setsockopt函数设置套接字的属性,例如设置为广播类型;

4. 创建一个sockaddr_in,并指定其IP地址和端口号;

5. 用bind函数将套接字与接收的地址绑定起来,然后调用recvfrom函数或者recv接收数据;注意这里一定要绑定,因为接收报文的套接字必须在网络上有一个绑定的名称才能保证正确接收数据;

6. 用closesocket函数关闭套接字;

7. 用WSACleanup函数关闭Socket环境。

 

发送端程序

#include <winsock2.h>
#include <iostream.h>

void main()
{
SOCKET sock; //socket套接字
char szMsg[] = "this is a UDP test package";//被发送的字段

//1.启动SOCKET库,版本为2.0
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 0);
err = WSAStartup( wVersionRequested, &wsaData );
if ( 0 != err ) //检查Socket初始化是否成功
{
cout<<"Socket2.0初始化失败,Exit!";
return;
}
//检查Socket库的版本是否为2.0
if (LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
{
WSACleanup( );
return;
}

//2.创建socket,
sock = socket(
AF_INET, //internetwork: UDP, TCP, etc
SOCK_DGRAM, //SOCK_DGRAM说明是UDP类型
0 //protocol
);

if (INVALID_SOCKET == sock ) {
cout<<"Socket 创建失败,Exit!";
return;
}

//3.设置该套接字为广播类型,
bool opt = true;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char FAR *>(&opt), sizeof(opt));

//4.设置发往的地址
sockaddr_in addrto; //发往的地址
memset(&addrto,0,sizeof(addrto));
addrto.sin_family = AF_INET; //地址类型为internetwork
addrto.sin_addr.s_addr = INADDR_BROADCAST; //设置ip为广播地址
addrto.sin_port = htons(7861); //端口号为7861

int nlen=sizeof(addrto);
unsigned int uIndex = 1;
while(true)
{
Sleep(1000); //程序休眠一秒
//向广播地址发送消息
if( sendto(sock, szMsg, strlen(szMsg), 0, reinterpret_cast<sockaddr*>(&addrto),nlen)
== SOCKET_ERROR )
cout<<WSAGetLastError()<<endl;
else
cout<<uIndex++<<":an UDP package is sended."<<endl;
}

if (!closesocket(sock)) //关闭套接字
{
WSAGetLastError();
return;
}
if (!WSACleanup()) //关闭Socket库
{
WSAGetLastError();
return;
}
}

 

接收端程序:

#include <winsock2.h>
#include <iostream.h>

void main(void)
{
SOCKET sock;

//1.启动SOCKET库,版本为2.0
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 0 );
err = WSAStartup( wVersionRequested, &wsaData );
if (0 != err)
{
cout<<"Socket2.0初始化失败,Exit!";
return;
}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
{
WSACleanup();
return;
}

//2.创建套接字
sock = socket(AF_INET,SOCK_DGRAM,0);
if (INVALID_SOCKET == sock)
{
cout<<"Socket 创建失败,Exit!";
return;
}

//3.设置该套接字为广播类型,
bool opt=true;
setsockopt(sock,SOL_SOCKET, SO_BROADCAST,(char FAR *)&opt,sizeof(opt));

//4.创建地址
sockaddr_in from;
memset(&from,0,sizeof(from));
from.sin_family=AF_INET;
//如果绑定地址不是本机地址或者ADDR_ANY,则recvfrom函数不会正确接收,而是立刻返回
// from.sin_addr.s_addr = ADDR_ANY;
from.sin_addr.s_addr = inet_addr("192.168.0.7");
//端口号必须和客户发往的端口号一致
from.sin_port=htons(7861);

//5.绑定接收地址
bind(sock,(sockaddr*)&from,sizeof(from));

memset(&from,0,sizeof(from));
int fromlength = sizeof(SOCKADDR);
char buf[256];
memset(buf,0,sizeof(buf));
long number = 0;
while(true){
number++;
// recvfrom(sock,buf,256,0,(struct sockaddr FAR *)&from,(int FAR *)&fromlength);
recv(sock,buf,256,0);
Sleep(1000);
cout<<number<<":"<<buf<<endl;
memset(buf,0,sizeof(buf));
}

if (!closesocket(sock)) {
WSAGetLastError();
return;
}
if (!WSACleanup()) {
WSAGetLastError();
return;
}
}

5.2UDP实例

TCP与UDP最大的不同之处在于TCP是一个面向连接的协议,在进行数据收发之前TCP必须进行连接,并且在收发的时候必须保持该连接。

发送方的步骤如下(省略了Socket环境的初始化、关闭等内容):

1. 用socket函数创建一个套接字sock;

2. 用bind将sock绑定到本地地址;

3. 用listen侦听sock套接字;

4. 用accept函数接收客户方的连接,返回客户方套接字clientSocket;

5. 在客户方套接字clientSocket上使用send发送数据;

6. 用closesocket函数关闭套接字sock和clientSocket;

而接收方的步骤如下:

1. 用socket函数创建一个套接字sock;

2. 创建一个指向服务方的远程地址;

3. 用connect将sock连接到服务方,使用远程地址;

4. 在套接字上使用recv接收数据;

5. 用closesocket函数关闭套接字sock;

值得注意的是,在服务方有两个地址,一个是本地地址myaddr,另一个是目标地址addrto。本地地址myaddr用来和本地套接字sock绑定,目标地址被sock用来accept客户方套接字clientSocket。这样sock和clientSocket连接成功,这两个地址也连接上了。在服务方使用clientSocket发送数据,则会从本地地址传送到目标地址。

在客户方只有一个地址,即来源地址addrfrom。这个地址被用来connect远程的服务方套接字,connect成功则本地套接字与远程的来源地址连接了,因此可以使用该套接字接收远程数据。其实这时客户方套接字已经被隐性的绑定了本地地址,所以不需要显式调用bind函数,即使调用也不会影像结果。

 

发送端程序

#include <stdlib.h>
#include <winsock2.h>
#include <iostream.h>

void main()
{
SOCKET sock, clientSocket; //socket
char szMsg[] = "this is a UDP test package";//被发送的字段

//1.启动SOCKET库,版本为2.0
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 0 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
cout<<"Socket2.0初始化失败,Exit!";
return;
}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
{
WSACleanup( );
return;
}

//2.创建socket,
sock = socket(
AF_INET, //internetwork: UDP, TCP, etc
SOCK_STREAM, //SOCK_STREAM说明是TCP socket
0 //protocol
);

if (sock == INVALID_SOCKET )
{
cout<<"Socket 创建失败,Exit!";
return;
}

//3.bind套接字,相当于给本地套接字赋值
sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family=AF_INET;

myaddr.sin_addr.s_addr = inet_addr("192.168.0.7");
myaddr.sin_port=htons(7861);
bind(sock,(sockaddr*)&myaddr,sizeof(myaddr));

//4.设置发往的地址
sockaddr_in addrto; //发往的地址
memset(&addrto,0,sizeof(addrto));
addrto.sin_family=AF_INET;
addrto.sin_addr.s_addr=inet_addr("192.168.0.9");
//端口号必须和服务器绑定的端口号一致
addrto.sin_port=htons(7861);

//5.listen 另一端的socket
if (listen(sock,5) == SOCKET_ERROR )
{
closesocket(sock);
WSACleanup();
abort();
}
else
{
cout<<"listen succeed!"<<endl;
}

//6. accept 对方连接
int nlen = sizeof(addrto);
clientSocket = accept(sock,(sockaddr*)&addrto,&nlen);
if (clientSocket < 0)
{
cout<<"client socket error, exit!"<<endl;
abort();
}
else
{
cout<<"accepted client socket!"<<endl;
}

unsigned int uIndex = 1;
while(true)
{
Sleep(1000);

if( send(clientSocket, szMsg, strlen(szMsg), 0) == SOCKET_ERROR )
cout<<"error, error id = "<<WSAGetLastError()<<endl;
else
cout<<uIndex++<<":an UDP package is sended."<<endl;
}

if (!closesocket(sock))
{
WSAGetLastError();
return;
}
if (!closesocket(clientSocket))
{
WSAGetLastError();
return;
}
if (!WSACleanup())
{
WSAGetLastError();
return;
}
}

接收端程序:

#include <winsock2.h>
#include <iostream.h>
#include <stdlib.h>

void main(void)
{
SOCKET sock;

//1.启动SOCKET库,版本为2.0
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 0 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 )
{
cout<<"Socket2.0初始化失败,Exit!";
return;
}
if ( LOBYTE( wsaData.wVersion ) != 2 || HIBYTE( wsaData.wVersion ) != 0 )
{
WSACleanup();
return;
}

//2.创建套接字
sock = socket(AF_INET,SOCK_STREAM,0);
if (sock == INVALID_SOCKET ) {
cout<<"Socket 创建失败,Exit!";
return;
}

//3.定义地址
sockaddr_in myaddr;
memset(&myaddr,0,sizeof(myaddr));
myaddr.sin_family=AF_INET;

myaddr.sin_addr.s_addr = inet_addr("192.168.0.9");
//端口号必须和客户发往的端口号一致
myaddr.sin_port=htons(7861);

//4.connect
if (connect(sock,(sockaddr*)&myaddr,sizeof(myaddr)) == SOCKET_ERROR)
{
cout<<"connect failed, exit!"<<endl;
closesocket(sock);
WSACleanup();
exit(1);
}
else
{
cout<<"connect succeed!"<<endl;
}

char buf[256]="";
long number=0;
int iResult = 0;
while(true)
{
number++;
iResult = recv(sock,buf,sizeof(buf),0);
if (iResult < 0)
{
cout<<"服务方Socket关闭,退出!"<<endl;
break;
}
Sleep(1000);
cout<<number<<":"<<buf<<endl;
memset(buf,0,sizeof(buf));
}

if (!closesocket(sock))
{
WSAGetLastError();
return;
}
if (!WSACleanup())
{
WSAGetLastError();
return;
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值