网络编程——UDP通信(c++)

WSAStartup()

这里用通俗的语言解释一下这个函数,就类似于OpenCV一样,要添加链接库函数,cv.lib等,要添加到附加依赖项,或者通过#pragma commentlib,”cv.lib“)一样,然后才能包含头文件进行各种函数的调用。当然了,socket编程要调用各种socket函数,但是需要库Ws2_32.lib和头文件Winsock2.h,这里的WSAStartup就是为了向操作系统说明,我们要用哪个库文件,让该库文件与当前的应用程序绑定,从而就可以调用该版本的socket的各种函数了。

头文件 header  Winsock2.h

library      Ws2_32.lib
原型:int   PASCAL   FAR  WSAStartup ( WORD  wVersionRequested, LPWSADATA   lpWSAData );
参数:wVersionRequestedWindows Sockets API提供的调用方可使用的最高版本号。高位字节指出副版本(修正)号,低位字节指明主版本号
        lpWSAData 
是指向WSADATA数据结构指针,用来接收Windows Sockets实现的细节

WSADATA数据类型:这个结构被用来存储 WSAStartup函数调用后返回的 Windows Sockets数据。它包含Winsock.dll执行的数据。

其实,wsastartup主要就是进行相应的socket库绑定。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0


socket()

原型:int socket (int domain, int type, int protocol)

功能描述:初始化创建socket对象,通常是第一个调用的socket函数。 成功时,返回非负数的socket描述符;失败是返回-1。socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。调用socket()函数时,socket执行体将建立一个socket,实际上"建立一个socket"意味着为一个socket数据结构分配存储空间。socket执行体为你管理描述符表。

参数解释

domain -- 指明使用的协议族。常用的协议族有,AF_INET、AF_INET6、AF_LOCAL(或称AF_UNIX,Unix域socket)、AF_ROUTE等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。

type -- 指明socket类型,有3种:

                                                 SOCK_STREAM -- TCP类型,保证数据顺序及可靠性;

                                                 SOCK_DGRAM --  UDP类型,不保证数据接收的顺序,非可靠连接;

                                                 SOCK_RAW -- 原始类型,允许对底层协议如IP或ICMP进行直接访问,不太常用。

protocol -- 通常赋值"0",由系统自动选择。也可以是IPPROTO_UDP、IPPROTO_TCP,分别表示用UDP协议和TCP协议。





inet_addr()    将字符串点数格式地址转化成无符号长整型(unsigned long s_addr s_addr;)
inet_aton()    将字符串点数格式地址转化成NBO
inet_ntoa ()     将NBO地址转化成字符串点数格式
htons()    "Host to Network Short"
htonl()    "Host to Network Long"
ntohs()    "Network to Host Short"
ntohl()    "Network to Host Long"
常用的是htons(),inet_addr()正好对应结构体的端口类型和地址类型


sockaddr_in(在netinet/in.h中定义):
struct sockaddr_in {
short int sin_family;                      /* Address family 一般来说AF_INET(地址族)代表TCP/IP协议族*/
unsigned short int sin_port;       /* Port number (必须要采用网络数据格式,普通数字可以用htons()函数转换成网络数据格式的数字)*/
struct in_addr sin_addr;              /* Internet address */
unsigned char sin_zero[8];         /* Same size as struct sockaddr */
};



在windows下in_addr的结构
typedef struct in_addr
{
    union{
            struct { unsigned char s_b1,s_b2,s_b3,s_b4; } S_un_b;
            struct { unsigned short s_w1,s_w2; } S_un_w;
            unsigned long S_addr;
    }S_un;
}in_addr;



sendto()

原型:int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *dst_addr, int addrlen)

功能描述用于非可靠连接(UDP)的数据发送,因为UDP方式未建立连接socket,因此需要制定目的协议地址。

当本地与不同目的地址通信时,只需指定目的地址,可使用同一个UDP套接口描述符sockfd,而TCP要预先建立连接,每个连接都会产生不同的套接口描述符,体现在:客户端要使用不同的fd进行connect,服务端每次accept产生不同的fd。

因为UDP没有真正的发送缓冲区,因为是不可靠连接,不必保存应用进程的数据拷贝,应用进程中的数据在沿协议栈向下传递时,以某种形式拷贝到内核缓冲区,当数据链路层把数据传出后就把内核缓冲区中数据拷贝删除。因此它不需要一个发送缓冲区。写UDP套接口的sendto/write返回表示应用程序的数据或数据分片已经进入链路层的输出队列,如果输出队列没有足够的空间存放数据,将返回错误ENOBUFS.

参数解释

sockfd -- 发送端套接字描述符(非监听描述符);

msg -- 待发送数据的缓冲区;

len -- 待发送数据的字节长度;

flags -- 一般情况下置为0;

dst_addr -- 数据发送的目的地址;

addrlen -- 地址长度。


 recvfrom()

原型:int recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, int*fromlen)

功能描述:用于非可靠连接(UDP)的数据接收。

参数解释

sockfd -- 接收端套接字描述;

buf -- 用于接收数据的应用缓冲区地址;

len -- 指名缓冲区大小;

flags -- 通常为0;

src_addr -- 数据来源端的地址;

fromlen -- 作为输入时,fromlen常置为sizeof(struct sockaddr);当输出时,fromlen包含实际存入buf中的数据字节数。




服务器端:

#include 
#include 
#include 

#pragma comment (lib,"WS2_32.lib")

using namespace std;

int main()
{
	WSADATA wsaData;
	int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (ret != 0)
	{
		cout << "WSAStartup error" << endl;
		system("pause");
		return 0;
	}
	cout << "WSAStartup Successful"< 0)
		{
			cout << "客户端:(" << inet_ntoa(addr.sin_addr) << "):" << recvbuf << endl;
		}

		//发送
		cout << "输入发送数据" << endl;
		cin >> sendbuf;
		if ((sendto(s, sendbuf, strlen(sendbuf), 0, (SOCKADDR*)&addr, sizeof(addr))) == SOCKET_ERROR)
		{
			cout << "send error" << endl;
			system("pause");
			return 0;
		}
		cout << "send successful" << endl;
	}

	closesocket(s);
	WSACleanup();

	system("pause");
	return 0;
}



客户端:

#include 
#include 
#include 

#pragma comment (lib,"WS2_32.lib")

using namespace std;

int main()
{
	WSADATA wsaData;
	int ret = WSAStartup(MAKEWORD(2, 2), &wsaData);
	if (ret != 0)
	{
		cout << "WSAStartup error" << endl;
		return 0;
	}
	cout << "WSAStartup Successful" << endl;

	SOCKET s;
	s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (s == INVALID_SOCKET)
	{
		cout << "socket error" << endl;
	}
	cout << "socket successful" << endl;

	//设置服务器地址
	sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(5000);
	sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	//发送和接受数据

	//发送缓冲区
	char sendbuf[1024];

	//定义一个地址结构,接收时用
	sockaddr_in addr;
	int addrlen = sizeof(addr);
	addr.sin_family = AF_INET;
	addr.sin_port = htons(5000);
	addr.sin_addr.S_un.S_addr = inet_addr("210.210.210.210");


	//接收缓冲区
	char recvbuf[1024];

	while (true)
	{
		//开始发送
		cout << "输入发送数据" << endl;
		cin >> sendbuf;
		int r = sendto(s, sendbuf, sizeof(sendbuf),0,(sockaddr*)&sin,sizeof(sin));
		if (r == SOCKET_ERROR)
		{
			cout << "发送失败" << endl;
			return 0;
		}

		//接收
		int rr = recvfrom(s, recvbuf, 1024, 0, (sockaddr*)&sin, &addrlen);
		if (rr == SOCKET_ERROR)
		{
			cout << "recvfrom()Failed.:" << WSAGetLastError() << endl;
			return 0;
		}
		if (rr > 0)
		{
			recvbuf[rr] = '\0';
			cout << "服务器:(" << inet_ntoa(addr.sin_addr) << "):" << recvbuf << endl;
		}
	}

	closesocket(s);
	WSACleanup();



	system("pause");
	return 0;
}


运行——


服务器端

网络编程——UDP通信(c++)_第1张图片



客户端

网络编程——UDP通信(c++)_第2张图片



你可能感兴趣的:(====网络编程====)