客户端程序过程
一个Socket客户端程序的典型过程如下。- 客户端程序在运行后,首先需要使调用WSAStartup函数,确保进程加载socket应用程序所必须的环境和库文件,如Ws2_32.dll。
- 调用函数Socket创建SOCKET,在创建时需指定使用的网络协议、连接类型等。
- 填充SOCKADDR结构,指定服务端的地址、端口等。
- 调用connect函数连接到服务端。
- 如果连接成功,就可以使用send和recv函数发送和接收数据。
- 在数据传输完成后,可调用closesocket函数关闭Socket。
- 调用WSACleanup函数释放资源。
客户端程序
新建win32项目控制台程序 Win32Client项目:
- // Win32Client.cpp : 定义控制台应用程序的入口点。
- #include "stdafx.h"
- #include <stdio.h>
- #include <WinSock2.h>
- #pragma comment(lib,"wsock32.lib")
- #define DEFIP "127.0.0.1" //本地地址
- #define DEFPORT 10000 //端口要大于1024
- void main()
- {
- WSADATA wsaData;
- LPVOID recvbuf;
- if (WSAStartup(MAKEWORD(2,2),&wsaData) != NO_ERROR)
- {
- printf("Error at WSAStartup()\n");
- }
- SOCKET sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
- if (sockfd == INVALID_SOCKET)
- {
- printf("error at socket():%ld\n",WSAGetLastError());
- closesocket(sockfd);
- WSACleanup();
- return;
- }
- // SOCKADDR_IN clientAddr;
- // clientAddr.sin_family=AF_INET;
- // clientAddr.sin_addr.s_addr= inet_addr("192.168.1.102");
- // clientAddr.sin_port=htons(12345); //服务器会获取此端口
- // if(bind(sockfd,(SOCKADDR*)&clientAddr,sizeof(clientAddr)) == SOCKET_ERROR)
- // {
- // printf("error at bind():%ld\n",WSAGetLastError());
- // closesocket(sockfd);
- // WSACleanup();
- // return;
- // }
- SOCKADDR_IN serverAddr;
- serverAddr.sin_family=AF_INET;
- serverAddr.sin_addr.s_addr= inet_addr(DEFIP);
- serverAddr.sin_port=htons(DEFPORT);
- if(connect(sockfd,(SOCKADDR*)&serverAddr,sizeof(SOCKADDR)) == SOCKET_ERROR)
- {
- printf("error at connect()%ld\n",WSAGetLastError());
- closesocket(sockfd);
- WSACleanup();
- return;
- }
- int bytesSend=0;
- char sendBuf[32]="get information";
- bytesSend=send(sockfd,sendBuf,strlen(sendBuf)+1,0);
- if (bytesSend == SOCKET_ERROR)
- {
- printf("error at send()%ld\n",WSAGetLastError());
- closesocket(sockfd);
- WSACleanup();
- return;
- }
- printf("Bytes send: %ld\n",bytesSend);
- int bytesRecv=0;
- char recvBub[2096]={0};
- while( bytesRecv != SOCKET_ERROR)
- {
- bytesRecv=recv(sockfd,recvBub,strlen(recvBub)+1,0);
- if (bytesRecv == 0)
- {
- printf("error at recv()%d",WSAGetLastError());
- break;
- }
- printf("bytes recv:[%d] %s\n",bytesRecv,recvBub);
- }
- closesocket(sockfd);
- WSACleanup();
- }
函数解析
- /*
- WSAStartup():功能是加载ws2_32.dll等socket程序运行的环境。在程序初始化后,socket程序运行所依赖的动态链接库不一定已经加载,WSAStartup保证了Socket动态链接库的加载。
- int WSAStartup(_in Word wVersionRequested,_out LPWSDATA lpWSAData);
- wVersionRequested:是Socket程序库的版本,一般使用MAKEWORD(2,2)宏
- lpWSAData:输出参数,指向WSADATA结构的指针,用于返回scoket库初始化的信息
- 返回值:0表示成功,
- WSACleanup():与WSAStartup()的功能相反,WSACleanup释放ws2_32.dll库。
- SOCKET socket( int af, int type, int protocol );
- 参数:int af:通信协议族(AF_UNIX,AF_INET等),AF_UNIX只能够用于单一的Unix系统进程间的通信,而AF_INET是针对Internet的,因此可以允许远程——本机之间的通信。AF:Address families(地址协议族)。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位的)与端口号(16位的)的组合、AF_UNIX决定了要用一个绝对路径名作为地址。
- int type:socket类型:SOCK_STREAM(TCP/IP),SOCK_DGRAM(UDP)
- int protocol:指定协议(有IPPROTO_TCP、IPPTOTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等,它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议),由于我们指定了type,所以这个地方我们一般只要用0来代替就可以了
- 返回值:若成功返回SOCKET对象标识,SOCKET就是unsigned int;如失败,则返回INVALID_SOCKET,调用WSAGetLastError()可得知原因,所有WinSocket的函数都可以使用这个函数来获取失败的原因。
- 注意:并不是上面的type和protocol可以随意组合的,如SOCK_STREAM不可以跟IPPROTO_UDP组合。当protocol为0时,会自动选择type类型对应的默认协议。
- 当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。
- 地址结构说明:
- typedef struct sockaddr_in
- {
- short sin_family;//由于我们主要使用Internet,所以一般为AF_INET
- u_short sin_port;//16位端口号,网络字节顺序
- struct in_addr sin_addr;//32位IP地址,网络字节顺序
- char sin_zero[8];//保留
- }SOCKADDR_IN;
- struct in_addr // Internet address.
- {
- uint32_t s_addr; // address in network byte order
- };
- //sin_:socket address internet
- struct sockaddr是通用的套接字地址,而struct sockaddr_in则是internet环境下套接字的地址形式,二者长度一样,都是16个字节。二者是并列结构,指向sockaddr_in结构的指针也可以指向sockaddr。一般情况下,需要把sockaddr_in结构强制转换成sockaddr结构再传入系统调用函数中。
- 字节转换函数
- 在网络上面有着许多类型的机器,这些机器在表示数据的字节顺序是不同的,比如i386芯片是低字节在内存地址的低端,高字节在高端,而alpha芯片却相反.为了统一起来,则有专门的字节转换函数.
- unsigned long int htonl(unsigned long int hostlong)
- unsigned short int htons(unisgned short int hostshort)
- unsigned long int ntohl(unsigned long int netlong)
- unsigned short int ntohs(unsigned short int netshort)
- 在这四个转换函数中,h代表host, n代表 network.s代表short l代表long
- 第一个函数的意义是将本机器上的long数据转化为网络上的long.其他几个函数的意义也差不多
- int connect(SOCKET s,const struct sockaddr FAR * name,int namelen);
- 参数:s:socket的标识码
- name:存储服务器的连接信息(协议族,服务器的ip,端口)
- namelen:name的长度
- 返回值:成功返回0,失败返回SOCKET_ERROR
- int send( SOCKET s, const char FAR *buf,int len, int flags );
- 参数:s:Socket 的识别码
- buf:存放要传送的资料的暂存区
- len buf:的长度
- flags:此函数被调用的方式,可以设为 0或MSG_DONTROUTE及MSG_OOB组合
- 返回值:若成功则返回发送的资料的长度,否则返回SOCKET_ERROR
- int recv( SOCKET s, char FAR *buf, int len, int flags );
- 参数:s:Socket 的识别码
- buf:存放接收到的资料的暂存区
- len buf:的长度
- flags:此函数被调用的方式,可以设为 0或MSG_DONTROUTE及MSG_OOB组合
- 返回值:若成功则返回接收资料的长度,否则返回SOCKET_ERROR
- int closesocket(SOCKET s);
- */
其
bytesRecv=recv(sockfd,recvBub,
strlen(recvBub)+1,0); 改为 bytesRecv=recv(sockfd,recvBub,
sizeof(recvBub)+1,0);就可以了。
此文章来自于【http://blog.csdn.net/ouyangshima/article/details/8922575】