这里用通俗的语言解释一下这个函数,就类似于OpenCV一样,要添加链接库函数,cv.lib等,要添加到附加依赖项,或者通过#pragma comment(lib,”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 );
参数:wVersionRequested是Windows Sockets API提供的调用方可使用的最高版本号。高位字节指出副版本(修正)号,低位字节指明主版本号。
lpWSAData 是指向WSADATA数据结构的指针,用来接收Windows Sockets实现的细节。
WSADATA数据类型:这个结构被用来存储 被WSAStartup函数调用后返回的 Windows Sockets数据。它包含Winsock.dll执行的数据。
其实,wsastartup主要就是进行相应的socket库绑定。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的Socket库中的其它Socket函数了。该函数执行成功后返回0。
原型: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 */
};
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;
原型: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 -- 地址长度。
原型: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;
}
运行——
服务器端
客户端