Socket通信程序设计-网络实验4
3.实验指导
(1)什么是Socket
Socket(套接字)是当前最流行的网络编程接口(网络编程的API)。Socket使用网络层的IP地址和传输层的端口号进行数据交换
(2) 常用Socket类型
1、流式套接字(SOCK_STREAM),基于TCP协议,提供面向连接的、可靠的数据传输服务
2、数据报套接字(SOCK_DGRAM),基于UDP协议,提供无连接的、不可靠的数据传输服务
3、原始套接字(SOCK_RAW),提供对网络层协议的访问,可以保存IP数据包中完整的头部,前两者不保留,只是存储或转发
(3) TCP编程模式
(4) Server端程序设计要点
①相关对象的声明;
②启动监听;
③接受Client端连接请求,建立与Client端的连接;
④流式Socket上的数据接收;
⑤流式Socket上的数据发送;
⑥释放资源.
(5) Client端程序设计要点 Client端的程序设计与Server端有许多相似之处。差异主要在建立连接时,服务器端处于监听状态,而客户端要发起连接请求.一旦连接建立后,双方就将平等地发送与接收数据.
•根据代码注释,读懂示例代码,自行模仿设计Server端和Client端,要求能运用SOCK_STREAM,实现使用端口8888的Server端和Client端,能够进行最简单的聊天功能,你一句我一句交互即可。
参考与资料扩展:
•代码中注释
•谷歌搜索
•MSDN
客户端代码:
#include "stdafx.h" #include "winsock2.h" #pragma comment(lib,"ws2_32.lib") #define BUFFSIZE 80 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; WSADATA wsaData; SOCKET sockfd; SOCKADDR_IN servAddr; wVersionRequested = MAKEWORD(2,2); if (WSAStartup(wVersionRequested,&wsaData) != 0) { printf("Winsock init fail!\n"); return (-1); } sockfd = socket(AF_INET,SOCK_STREAM,0); if (sockfd == INVALID_SOCKET) { printf("Create socket fail!\n"); WSACleanup(); return (-1); } servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); servAddr.sin_family = AF_INET; servAddr.sin_port = htons(6000); // 向服务器发送连接请求 if(connect(sockfd, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) { printf("Connect fail!\n"); return (-1); } else printf("Connect success!\n"); // 和服务器进行通信 char buffSend[BUFFSIZE], buffRecv[BUFFSIZE]; int count; while (true) { printf("Send: "); gets(buffSend); if((send(sockfd, buffSend, strlen(buffSend), 0)) == SOCKET_ERROR) { printf("Send fail!\n"); return (-1); } if((count = recv(sockfd, buffRecv, BUFFSIZE, 0)) == SOCKET_ERROR) { printf("Recv fail!\n"); return (-1); } buffRecv[count] = '\0'; printf("Received :[%d bytes] %s \n", count, buffRecv); if(!strcmp(buffRecv, "88")) break; } if (closesocket(sockfd) == SOCKET_ERROR) { printf("Close fail!\n"); return (-1); } WSACleanup(); getchar(); return 0; }
服务器代码:
#include "stdafx.h" #include "winsock2.h" #pragma comment(lib,"ws2_32.lib") // 连接socket所需的库文件 #define BUFFSIZE 80 int _tmain(int argc, _TCHAR* argv[]) { WORD wVersionRequested; // 用于指定Winsock版本号(如早期的windsock1.1,现在的winsock2.x) WSADATA wsaData; // 用于返回winsock版本信息 SOCKET sockfd, connfd; // 套接字描述符、连接描述符 SOCKADDR_IN servAddr, cliAddr; // SOCKADDR_IN是一种通用Socket地址结构 // 使用MAKEWORD宏可以给一个WORD类型赋值。此处表示主版本号为2,副版本号为2,即winsock2.2 wVersionRequested = MAKEWORD(2,2); /////// 1. 加载Winsock的动态链接库,WSAStartup() // WSAStartup()会根据指定Winsock版本搜索相应的动态链接库并进行绑定,成功则返回0 if (WSAStartup(wVersionRequested,&wsaData) != 0) { printf("Winsock init fail!\n"); return (-1); } ///////// 2. 创建一个可用的套接字,socket() // 第一个参数为int af,用来指定使用的地址族,TCP/IP协议为AF_INET(比较常用)、Xerox协议为AF_NS、UNIX协议为AF_UNIX等 // 第二个参数为int type,用来指定创建的套接字类型,有SOCK_DGRAM、SOCK_STREAM、SOCK_RAW等 // 第三个参数为int protocol,用来指定使用的具体协议,根据地址格式和Socket类型自动选择,通常设为0 sockfd = socket(AF_INET, SOCK_STREAM, 0); // 返回值为套接字描述符SOCKET,若失败则返回INVALID_SOCKET if (sockfd == INVALID_SOCKET) { printf("Create socket fail!\n"); WSACleanup(); // 释放资源 return (-1); } ///////// 3. 将套接字与本地地址相互绑定,bind() // 将地址转为所需格式 // sin_family指定使用的地址族 servAddr.sin_family = AF_INET; // sin_addr指定使用的IP,设为INADDR_ANY表示分配给本机的所有IP地址 // htonl()将主机的无符号长整形数转换成网络字节顺序 servAddr.sin_addr.s_addr = htonl(INADDR_ANY); // sin_port指定使用的端口号 // htos()将一个无符号短整型数值转换为网络字节序 servAddr.sin_port = htons(6000); // 第一个参数为欲绑定套接字描述符,第二个参数为欲绑定本地地址,第三个参数为本地地址结构的长度 if (bind(sockfd, (SOCKADDR*)&servAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) { printf("Bind fail!\n"); return (-1); } else printf("Server running:\n"); // 4. 将套接字设置为监听模式等待连接请求,listen() // 第一个参数为套接字描述符,第二个参数为客户连接请求队列的最大长度 if(listen(sockfd, 5) == SOCKET_ERROR) { printf("Listen fail!\n"); return (-1); } // 5. 设置等待连接请求,处理请求accept(), recv(), send() int len = sizeof(SOCKADDR); char buff[BUFFSIZE]; int count; while (true) { // 阻塞式等待请求,接受连接 if((connfd = accept(sockfd, (SOCKADDR*)&cliAddr, &len)) == SOCKET_ERROR) { printf("Accept fail!\n"); return (-1); } printf("A client connect!\n"); while(true) { // 接收数据 if((count = recv(connfd, buff, BUFFSIZE, 0)) == SOCKET_ERROR) { printf("Recv fail!\n"); break; } buff[count] = '\0'; printf("Receive:[%d bytes] %s\n", count, buff); // 返回接收到的数据 if((send(connfd, buff, strlen(buff), 0)) == SOCKET_ERROR) { printf("Send fail!\n"); break; } if(!strcmp(buff, "88")) break; } // 关闭套接字 if (closesocket(connfd) == SOCKET_ERROR) { printf("Close fail!\n"); return (-1); } } if (closesocket(sockfd) == SOCKET_ERROR) { printf("Close fail!\n"); return (-1); } // 卸载Winsock的动态链接库 if(WSACleanup() == SOCKET_ERROR) { printf("Clean fail!\n"); return (-1); } return 0; }