面向连接的Socket通信流程
服务器端:
- 创建流式套接字,返回套接字号s
- 将套接字s绑定到一个已知地址,通常为本地IP地址
- 将套接字s设置为监听模式,准备接收各个客户机的请求
- 建立连接,得到新的套接字ns
- 在套接字上读写数据,直到结束
- 关闭套接字ns
- 关闭套接字s
客户端:
- 创建流式套接字,返回套接字号s
- 将套接字s与远程主机连接
- 在套接字上读写数据,直到结束
- 关闭套接字,结束会话
Socket编程相关函数
socket()
用于创建与指定的服务提供者绑定套接字,如果成功则返回新的socket句柄,否则返回INVALID_SOCKET.
SOCKET socket( int af, /*指定协议的地址家族,通常使用AF_INET*/ int type, /*指定套接字类型*/ int protocol /*套接字的协议*/ );
套接字类型 说明 SOCKET_STREAM 提供顺序、可靠、双向和面向连接的字节数据传输机制,使用TCP SOCKET_DGRAM 支持无连接的数据报,使用UDP SOCKET_RAM 原始套接字,用于接收本机网卡上的数据帧或数据包 bind()
将本地地址与socket绑定在一起,如果未发生错误,则返回0,否则返回SOCKET_ERROR.
int bind( SOCKET s, /*标识一个未绑定的Socket的描述符*/ const struct sockaddr FAR * name, /*绑定到Socket的sockaddr结构体地址*/ int namelen /*参数name的长度*/ );
如果应用程序不关心分配给它的本地地址,则可以把地址设置未ADDR_ANY.
listen()
将套接字设置为监听接入连接的状态,如果未发生错误,则返回0,否则返回SOCKET_ERROR。可以调用WSAGetLastError()函数获取错误代码int listen( SOCKET s, /*指定一个已经绑定(bind)但尚未连接的套件字*/ int backlog /*指定等待连接队列的最大长度*/ );
accept()
服务器端调用listen()函数监听接入连接后,可以调用accept()函数来等待接受连接请求。如果函数调用成功,则返回一个新建的Socket句柄,该Socket用于实现服务器和客户端之间的通信。如果调用失败,则返回INVALID_SOCKETSOCKET accept( SOCKET s, /*设置为监听状态的Socket*/ struct sockaddr FAR * addr, /*输出参数,用于接收接入地址信息,如果不关注接入地址,可以设置为NULL,可选*/ int FAR * addrlen /*输出参数,指定接入地址的长度,可选*/ );
recv()
从已连接的Socket中接收数据。如果函数调用成功,则返回接收数据的字节数;如果连接已经关闭,则返回0;否则返回SOCKET_ERROC.int recv( SOCKET s, /*已连接的Socket*/ char FAR * buf, /*用于接收数据的缓存区*/ int len, /**缓冲区的长度/ int flags /*用于影响函数的行为*/ );
参数flag的可选值 说明 MSG_PEEK 数据将复制到缓冲区buf中,但并不从输入队列中移除这些数据 MSG_OOB 处理带外数据 send()
在已建立连接的Socket上发送数据。如果函数调用成功,则返回发送数据的字节数,可以小于参数len中指定的长度;如果出现错误,则返回SOCKET_ERROC.int send( SOCKET s, /*已连接的Socket*/ const char FAR * buf, /*要发送数据的缓存区*/ int len, /*buf缓冲区的长度*/ int flags /*用于影响函数的行为*/ );
参数flag的可选值 说明 0 指定该函数没有特殊行为 MSG_DONTROUTE 指定数据不选择路由 MSG_OOB 从带外发送数据 closesocket()
关闭一个Socket,释放其所占用的资源。如果没有错误则返回0;否则返回SOCKET_ERROC.int closesocket( SOCKET s /*要关闭的Socket*/ );
shutdown()
禁止在指定的Socket上发送和接收数据。如果没有错误则返回0;否则返回SOCKET_ERROC.int shutdown( SOCKET s, /*要关闭的Socket*/ int how /*指定禁用的操作*/ );
参数how的选项 说明 SD_RECEIVE 不允许调用recv()接收数据 SD_SEND 不允许调用send()发送数据 SD_BOTH 不允许发送接收数据 connect()
用于建立到Socket的连接,该Socket必须处于监听状态。如果没有错误则返回0;否则返回SOCKET_ERROC.int connect( SOCKET s, /*未连接的Socket句柄*/ const struct sockaddr FAR * name, /*指定要连接的Socket的名称*/ int namelen /*指定Socket名的长度*/ );
TCP Socket编程实例
TCP服务器端:
#include <WinSock2.h>
#include <iostream>
#include <tchar.h>
#pragma comment(lib,"WS2_32.lib")
#define BUF_SIZE 64 //服务器接收数据缓冲区大小
int _tmain(int argc,_TCHAR* argv[])
{
//初始化WindowsSocket
WSADATA wsd;
SOCKET sServer;
SOCKET sClient;
int retVal; //调用各种socket函数的返回值
char buf[BUF_SIZE];
//初始化Socket动态库
if(WSAStartup(MAKEWORD(2,2),&wsd)!=0){
printf("WSASTARTUP Failed!\n");
return 1;
}
//创建用于监听的socket
sServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (INVALID_SOCKET == sServer)
{
printf("socket failed!\n");
WSACleanup();
return -1;
}
//设置服务器Socket地址
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET; //地址家族,TCP
addrServ.sin_port = htons(9990); //端口
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY); //地址
//绑定socket server到本地
retVal = bind(sServer,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN));
if (SOCKET_ERROR == retVal)
{
printf("bind failed!\n");
closesocket(sServer);
WSACleanup();
return -1;
}
//在socketscrver上进行监听
retVal = listen(sServer,1 );
if (SOCKET_ERROR == retVal)
{
printf("listen failed!\n");
closesocket(sServer);
WSACleanup();
return -1;
}
//接收请求
printf("TCP server start...\n");
sockaddr_in addrClient; //客户端地址
int addrClientLen = sizeof(addrClient);
sClient = accept(sServer,(sockaddr FAR*)&addrClient,&addrClientLen);
if (INVALID_SOCKET == sClient)
{
printf("accept failed\n");
closesocket(sServer);
WSACleanup();
return -1;
}
while(true)
{
ZeroMemory(buf,BUF_SIZE);
retVal = recv(sClient,buf,BUFSIZ,0);
if (SOCKET_ERROR == retVal)
{
printf("recv failed\n");
closesocket(sServer);
closesocket(sClient);
WSACleanup();
return -1;
}
//获取当前系统时间
SYSTEMTIME st;
GetLocalTime(&st);
char sDataTime[30];
sprintf(sDataTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
printf("%s,Recv From Cliet [%s:%d] :%s\n",sDataTime,inet_ntoa(addrClient.sin_addr),addrClient.sin_port,buf);
//如果客户端发送字符串为quit,则退出
if(strcmp(buf,"quit")==0){
retVal = send(sClient,"quit",strlen("quit"),0);
break;
}
else{
char msg[BUF_SIZE];
sprintf(msg,"message received - %s",buf);
retVal = send(sClient,msg,sizeof(msg),0);
if(SOCKET_ERROR == retVal){
printf("send failed\n");
closesocket(sServer);
closesocket(sClient);
WSACleanup();
return -1;
}
}
}
//释放socket
closesocket(sServer);
closesocket(sClient);
WSACleanup();
system("pause");
}
TCP客户端:
#include <WinSock2.h>
#include <string>
#include <iostream>
#include <tchar.h>
#pragma comment(lib,"WS2_32.lib")
#define BUF_SIZE 64
int _tmain(int argc,_TCHAR* argv[])
{
WSADATA wsd;
SOCKET sHost;
SOCKADDR_IN servAddr;
char buf[BUF_SIZE];
int retVal;
//初始化socket
if (WSAStartup(MAKEWORD(2,2),&wsd)!=0)
{
printf("WSASTARTUP failed!\n");
return -1;
}
//创建socket
sHost = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(INVALID_SOCKET == sHost)
{
printf("socket failed\n");
WSACleanup();
return -1;
}
//设置服务器socket地址
servAddr.sin_family = AF_INET;
servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
servAddr.sin_port = htons(9990);
int sServerAddLen = sizeof(servAddr);
//连接服务器
retVal = connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr));
if(SOCKET_ERROR == retVal)
{
printf("connect failed\n");
closesocket(sHost);
WSACleanup();
return -1;
}
while(true)
{
printf("Input:");
std::string str;
std::getline(std::cin,str);
ZeroMemory(buf,BUF_SIZE);
strcpy(buf,str.c_str());
retVal = send(sHost,buf,strlen(buf),0);
if(SOCKET_ERROR == retVal)
{
printf("send failed\n");
closesocket(sHost);
WSACleanup();
return -1;
}
retVal = recv(sHost,buf,sizeof(buf)+1,0);
printf("Recv:%s\n",buf);
if (strcpy(buf,"quit")==0)
{
printf("quit!\n");
break;
}
}
closesocket(sHost);
WSACleanup();
system("pause");
return 0;
}