TCP 处理流程图
TCP流程
1、检查版本号——
// Init WinSock
WSADATA data;
int ret = WSAStartup(0x0202, &data);
if (ret != 0)
{
WSACleanup();
return FALSE;
}
2、建立套接字,绑定,监听——
mListener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (mListener != INVALID_SOCKET)
{
// Settings on socket
BOOL sopt = TRUE;
setsockopt(mListener, IPPROTO_TCP, TCP_NODELAY,
(char *)&sopt, sizeof(BOOL));
setsockopt(mListener, SOL_SOCKET, SO_DONTLINGER,
(char *)&sopt, sizeof(BOOL));
// Bind socket and listen
SOCKADDR_IN addr;
memset(&addr, 0, sizeof(SOCKADDR_IN));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(mListenPort);
val = bind(mListener, (struct sockaddr*) &addr, sizeof(addr));
pass = (val != SOCKET_ERROR);
}
if (pass)
{
// Put the socket into the listening status
val = listen(mListener, SOMAXCONN);
pass = (val != SOCKET_ERROR);
}
SOCKET socket( IN int af, IN int type, IN int protocol );
af (AF_INET) ARPA因特网协议(UNIX网络套接字);
type——SOCK_STREAM 流式套接字TCP;SOCK_DGRAM 数据报套接字UCP
protocol——0为默认值,IPPROTO_TCP IP网的TCP协议
int setsockopt(IN SOCKET s, IN int level, IN int optname, IN const char FAR * optval, IN int optlen);
——设置套接口的选项
// TCP_NODELAY选项禁止Nagle算法
//SO_DONTLINER BOOL 不要因为数据未发送就阻塞关闭操作。相当于将SO_LINGER的 l_onoff 元素置为零
int bind(IN SOCKET s,IN const struct sockaddr FAR * name,IN int namelen);
——绑定socket和地址和端口监听因特网上的任意ip地址接入 INADDR_ANY
int listen( IN SOCKET s,IN int backlog);
——backlog队列中可以容纳的未处理连接的最大数目限制
——listen函数为服务器程序创建一个队列来保存未处理的请求
3、接收连接
DWORD threadID = 0;
mLsnThread = CreateThread(NULL, 0, ListeningThrd,
this, 0, &threadID);
//______________________ListenThrd 函数_______________________________
SOCKET accepted = accept(mListener, (SOCKADDR *)&saddr, &len);
if (accepted == INVALID_SOCKET)
{
return FALSE;
}
DeleteAccepted();
mAccepted = accepted;
// Settings on socket
BOOL sopt = TRUE;
setsockopt(mAccepted, IPPROTO_TCP, TCP_NODELAY,
(char *)&sopt, sizeof(BOOL));
setsockopt(mAccepted, SOL_SOCKET, SO_DONTLINGER,
(char *)&sopt, sizeof(BOOL));
SOCKET accept(IN SOCKET s,OUT struct sockaddr FAR * addr,IN OUT int FAR * addrlen);
——accept系统调用只有当客户程序试图连接到由socket参数指定的套接字时才返回,即是说,accept将阻塞知道有客户建立连接为止。通常的做法是开一条新的线程。
——accept 返回新的套接字accepted。
4、客户端请求连接
mSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (mSocket != INVALID_SOCKET)
{
BOOL sopt = TRUE;
setsockopt(mSocket, IPPROTO_TCP, TCP_NODELAY,
(char *)&sopt, sizeof(BOOL));
setsockopt(mSocket, SOL_SOCKET, SO_DONTLINGER,
(char *)&sopt, sizeof(BOOL));
SOCKADDR_IN saddr;
memset(&saddr, 0, sizeof(SOCKADDR_IN));
saddr.sin_addr.S_un.S_addr = inet_addr(inTarget);
saddr.sin_family = AF_INET;
saddr.sin_port = htons((WORD)inPort);
if (connect(mSocket, (SOCKADDR *)&saddr, sizeof(SOCKADDR_IN)) != 0){
}
}
int connect(IN SOCKET s,IN const struct sockaddr FAR * name,IN int namelen);
——客户程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。
——inTarget为服务器端ip地址。
——如果连接不能立刻建立,connect调用将阻塞一段不确定的超时时间。connect调用是阻塞的。
5、发送/接收数据
//__recv___
DWORD threadID = 0;
mRcvThread = CreateThread(NULL, 0, ReceivingThrd,
this, 0, &threadID);
//_________________________ReceivingThrd 函数____________________________________
while (mIsReceiving)
{
bytes = recv(mSocket, buf, sizeof(Net_Header), 0);
if (bytes == SOCKET_ERROR || bytes == 0)
{
Detach();
mIsReceiving = FALSE;
break;
}
pHeader->my_ntoh();
bytes = recv(mSocket, buf, pHeader->pack_size, 0);
if (bytes == SOCKET_ERROR || bytes == 0)
{
Detach();
mIsReceiving = FALSE;
break;
}
buf[bytes] = '/0';
}
//__send___
DWORD threadID = 0;
mSndThread = CreateThread(NULL, 0, SendingThrd,
this, 0, &threadID);
//_________________________ SendingThrd函数____________________________________
char str[] = "hello world!";
int len = strlen(str) + sizeof(Net_Header);
Net_Header * pHeader = (Net_Header *) buf;
pHeader->pack_size = strlen(str);
pHeader->my_hton();
strcpy(buf+sizeof(Net_Header), str);
bytes = send(mSocket, buf, len, 0);
}
int recv( IN SOCKET s, OUT char FAR * buf, IN int len, IN int flags );
int send( IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags );
——开新线程来发送和接收数据
6、关闭socket连接
if (mSocket != INVALID_SOCKET)
{
closesocket(mSocket);
mSocket = INVALID_SOCKET;
mIsConnected = FALSE;
}
7、关闭发送接收数据线程
if (mIsReceiving)
{
Detach();
if (mRcvThread != NULL)
{
WaitForSingleObject(mRcvThread, INFINITE);
mRcvThread = NULL;
}
}
//__________
if (mIsReceiving)
{
Detach();
if (mRcvThread != NULL)
{
WaitForSingleObject(mRcvThread, INFINITE);
mRcvThread = NULL;
}
}
——listen同样需要关闭socket和监听线程