您的位置:首页 > 理论基础 > 计算机网络

C++网络编程基础代码(服务器)---基于TCP协议

2017-02-17 09:42 471 查看
//网络编程服务器
/*服务器端编程的步骤:

1:加载套接字库,创建套接字(WSAStartup() / socket());

2:绑定套接字到一个IP地址和一个端口上(bind());

3:将套接字设置为监听模式等待连接请求(listen());

4:请求到来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept());

5:用返回的套接字和客户端进行通信(send() / recv());

6:返回,等待另一连接请求;

7:关闭套接字,关闭加载的套接字库(closesocket() / WSACleanup())。
*/

#include <WinSock2.h>
#include <stdio.h>
#include <stdlib.h>

#pragma comment(lib, "ws2_32.lib")

int main()
{
WSADATA wsaData;
int port = 5099;					//自定义的链接端口号,大于1024就行

char buf[] = "I am a Server";

//判断是否可以使用window的网络编程库,初始化sock资源
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("Faied to Load winsork");
return 0;
}

//socket函数指定通信协议类型,套接字类型
//int socket(int family, int type, int protocol); // 成功返回非负描述符,出错-1
//family指定协议族,type指定套接字类型,protocol指定某个协议类型常值,或者设为0。
/*family的值有:

AF_INET IPv4协议
AF_INET6 Ipv6协议
AF_LOCAL Unix协议域
AF_ROUTE 路由套接字
AF_KEY 秘钥套接字
type的值有:

SOCK_STREAM 字节流套接字
SOCK_DGRAM 数据报套接字
SOCK_SEQPACKET 有序分组套接字
SOCK_RAW 原始套接字
protocol的值有:

IPPROTO_CP TCP传输协议
IPPROTO_UDP UDP传输协议
IPPROTO_SCTP SCTP传输协议

socket函数在成功时返回一个小的非负整数值,与文件描述符类似,成为套接字描述符,为了得到这个描述符,需要指定协议族和套接字类型,但是并没有指定本地协议地址和远端协议地址。
*/
//创建套接字
SOCKET	sockSrv = socket(AF_INET,SOCK_STREAM,0);	//IPv4 IP协议,字节流传输(TCP传输协议)

//套接字结构体
SOCKADDR_IN	addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);						//4个函数来完成主机字节序和网络字节序之间的转换,h表示host,n表示net,s表示short,l表示long
//#include <netinet/in.h>
//uint16_t htons(uint16_t host16bitvalue);
//uint32_t htonl(uint32_t host32bitvalue);
//uint16_t ntohs(uint16_t net16bitvalue);
//uint32_t ntohl(uint32_t net32bitvalue);
addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);		//INADDR_ANY数值为0.0.0.0,表示任何地址都行

/*bind函数把一个本地协议地址赋予一个套接字,它只是把一个协议地址赋予一个套接字,至于协议地址的含义则取决于协议本身。
第二个参数指向协议地址结构的指针,第三个参数是协议地址的长度,对于TCP,调用bind函数可以指定一个端口号,或指定一个IP地址,或两者都指定,也可以两者都不指定。

bind函数绑定特定的IP地址必须属于其所在主机的网络接口之一,服务器在启动时绑定它们众所周知的端口,如果一个TCP客户端或服务端未曾调用bind绑定一个端口,
当调用connect或listen时,内核就要为响应的套接字选择一个临时端口。让内核选择临时端口对于TCP客户端来说是正常的额,然后对于TCP服务端来说确实罕见的,
因为服务端通过他们众所周知的端口被大家认识的
*/
int retval = bind(sockSrv, (LPSOCKADDR)&addr, sizeof(SOCKADDR_IN));
if (retval == SOCKET_ERROR)
{
printf("Failed bind:%d\n", WSAGetLastError());
return 0;
}

/*socket创建一个套接字时,它被假设为一个主动套接字,也就是说,它是一个将调用connect发起连接的一个客户套接字。
listen函数把一个未连接的套接字转换为一个被动套接字,指示内核应接受指向该套接字的连接请求,调用listen函数将导致套接字从CLOSEE状态转换到LISTEN状态。
第二个参数规定了内核应为相应套接字排队的最大连接个数。*/
if (listen(sockSrv,10) == SOCKET_ERROR)
{
printf("Failed to listen");
}

SOCKADDR_IN	addrClient;
int len = sizeof(SOCKADDR);

while (1)
{
//等待客户端请求
//accept函数由TCP服务器调用,用于从已完成队列中列头返回下一个已完成连接,如果已完成队列为空,则进程被投入睡眠(如果该套接字为阻塞方式的话)。
//如果accept成功,那么其返回值是由内核自动生成的一个全新套接字,代表与返回客户的TCP连接,函数的第一个参数为监听套接字,返回值为已连接套接字
SOCKET	sockConn = accept(sockSrv,(SOCKADDR*)&addrClient, &len);
//连接失败
if (sockConn == SOCKET_ERROR)
{
printf("Accept Failed");
break;
}

//连接成功
printf("Accept client IP:[%s]\n", inet_ntoa(addrClient.sin_addr));			//inet_addr函数是旧函数库的函数,编译时会报错,解决办法,打开项目->属性->配置属性->c/c++ ->SDL检测   将是改为否

//开始收发数据
//发数据
int iSend = send(sockConn, buf, sizeof(buf), 0);
if (iSend == SOCKET_ERROR)
{
printf("Send Failed");
break;
}

//接数据
char recvBuf[100] = {};
recv(sockConn,recvBuf,sizeof(recvBuf),0);
//输出接到的数据
printf("%s\n", recvBuf);

closesocket(sockConn);
}

//关闭开通的套接字接口
closesocket(sockSrv);
//释放资源
WSACleanup();
system("pause");

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息