//网络编程服务器/*服务器端编程的步骤: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;}
新闻热点
疑难解答
图片精选