程序流程:
C++函数解释
根据上面的流程图,下面按步骤解释一下用程序怎么实现的,当然由于TCP 、UDP的C++源码量较大且相似度较高,所以下面只附上TCP serve的源代码并着重列出它们之间的差异:
makeword(2,2);//创建一个无符号的16位整数。
WASStartup();//异步套接字的启动函数,用于初始化套接字。
//字符数组 sendData[]:用于记录发送的次数。
socket();//原型是int socket (int domain, int type, int protocol)。用于初始化创建socket对象,成功时,返回非负数的socket描述符;失败是返回-1。
bind();//函数原型为int bind(int sockfd, const struct sockaddr* myaddr, socklen_t addrlen)。将创建的socket绑定到指定的IP地址和端口上,通常是第二个调用的socket接口。返回值:0成功,-1则出
listen();//原型为int listen(int sockfd, int backlog),listen()函数仅被TCP类型的服务器程序调用,实现监听服务,该函数的第一个参数为socket类的一个对象,第二个参数规定了套接字的连接个数。listen()成功时返回0,错误时返回-1。
accept();//原型为 int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen)。该函数仅被TCP类型的服务器程序调用,从已完成连接队列返回下一个建立成功的连接,如果已完成连接队列为空,线程进入阻塞态睡眠状态。成功时返回套接字描述符,错误时返回-1。
connetct();//原型为int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)。TCP特有,用来与服务器建立一个TCP连接,实际是发起3次握手过程,连接成功返回0,连接失败返回1。
send();//原型为int send(int sockfd, const void *msg, int len, int flags)。TCP类型的数据发送。
recv();//原型为int recv(int sockfd, void *buf, int len, unsigned int flags)。TCP类型的数据接收。
sendto();//原型为int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *dst_addr, int addrlen)。用于非可靠连接(UDP)的数据发送,因为UDP方式未建立连接socket,因此需要制定目的协议地址。
recvfrom();//原型int recvfrom(int sockfd,void *buf, size_t len, int flags, struct sockaddr *src_addr, int*fromlen)用于非可靠连接(UDP)的数据接收。
losesocket();//关闭socket。
C++源码示例:
#include <stdio.h>
#include <winsock2.h> //包含socket类的头文件
#include<stdlib.h>
#include<time.h>
#pragma comment(lib,"ws2_32.lib")
int main(int argc, char* argv[])
{
//初始化WSA
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
int f = 1, mode = 0;
int ret = -1;
char sendData[] = "Times:0000\n";
if (WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}
//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}
//绑定IP和端口
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8235);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("bind error !\n");
}
//开始监听
if (listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !\n");
return 0;
}
//循环接收数据
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[255];
while (f)
{
system("cls");
fflush(stdin);
printf("Wait...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
else
{
printf("Received a Response from: %s \r\n", inet_ntoa(remoteAddr.sin_addr));
printf("Send(1) or Receive(0)?\n");
scanf("%d", &mode);
if (mode)
{
send(sClient, sendData, strlen(sendData), 0);
sendData[9]++;
if (sendData[9] == ':')
{
sendData[9] = '0';
sendData[8] += 1;
}
}
else
{
ret = recv(sClient, revData, 255, 0);
if (ret > 0)
{
revData[ret] = '\0';
printf(revData);
printf("\n");
}
revData[0] = '\0';
closesocket(sClient);
}
}
printf("Continue or Quit(0)\n");
scanf("%d", &f);
}
closesocket(slisten);
WSACleanup();
return 0;
}
程序运行现象
同样地,仍以TCP Server 为例:
注意:上面的程序可以在同一台计算机上完成(正常的话需要两台计算机),不过需要一个小工具辅助完成字符串互发并展示,该工具名为“sokit.exe”,如果需要的话,大家可以关注微信公众号“24K纯学渣”,回复“CS网络通讯”,即可获取4套(TCP-Server,TCP-Client,UDP-Server,UDP-Client)完整的C++ VS工程和小工具"sokit.exe"。