面向连接的Socket编程

实验二 面向连接的Socket编程

一、 实验目的

1.了解Socket的工作原理、服务方式和类型。
2.了解Socket应用程序的框架结构及相关数据结构。
3.理解面向连接的Socket通信流程。
4.掌握使用WinSock函数编写面向连接的网络应用程序的方法。

二、实验内容和要求

1.编写Windows平台上的基于TCP套接字的服务器应用程序TcpServer。服务器端接收客户端发送的消息,显示收到消息的来源、时间和消息内容,并发客户端回送信息进行接收确认。
2.编写Windows平台上的基于TCP套接字的客户端应用程序TcpClient。客户端程序主动发起与服务器程序的连接,读取用户输入的信息并向服务器发送,并接收服务器端的回送信息。
3.在同一台或不同计算机上分别运行TcpServer和TcpClient进行消息的发送和接收测试。要求建立连接后客户端可连续多次向服务器端发送消息。
4.使用Windows命令分别查看TcpServer和TcpClient的TCP连接信息和进程信息。

三、实验原理

在server端,server首先启动,调用socket()创建套接字;然后调用bind()绑定server的地址(IP%+port);再调用listen()让server做好侦听准备,并规定好请求队列长度,然后server进入阻塞状态,等待client的连接请求;最后通过accept()来接收连接请求,并获得client的地址。当accpet接收到一个client发来的connet请求时,将生成一个新的socket,用于传输数据。
在client端,client在创建套接字并指定client的socket地址,然后就调用connect()和server建立连接。一旦连接建立成功,client和server之间就可以通过调用recv和send来接收和发送数据。一旦数据传输结束,server和client通过调用close()来关闭套接字。
面向连接的Socket通信流程。
服务器
①头文件和常量定义
②声明变量
③初始化Socket环境
④创建用于监听的Socket
⑤设置服务器Socket地址
⑥绑定Sockets Server到本地地址
⑦在Socket Server上进行监听
⑧接受来自客户端的请求
⑨在服务器与客户端之间发送和接收数据⑩释放资源
客户端
①头文件和常量定义
②声明变量
③初始化Socket环境
④创建用于通信的Socket
⑤设置服务器Socket地址
⑥连接到服务器
⑦在服务器与客户端之间发送和接收数据
⑧释放资源

四、实验步骤

面向连接的Socket编程

五、实验小结

附:程序源代码

客户端

#include "stdafx.h"

#include <string>
#include<iostream>
#include<winsock2.h>
#pragma comment(lib,"WS2_32.lib")
#define BUF_SIZE 64  //缓冲区大小

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsd;
	SOCKET sHost;
	SOCKADDR_IN servAddr;
	char buf[BUF_SIZE];
	int retVal;

	//初始化Windows Socket
	if(WSAStartup(MAKEWORD(2,2),&wsd)!=0)
	{
	printf("WSAStartup failed!\n");
	return 1;
	}

	//创建套接字
	sHost = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == sHost)
	{
		printf("socket failed!\n");
		WSACleanup();
		return -1;
	}

	//设置服务器地址
	servAddr.sin_family = AF_INET;
	servAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //用户要根据实际情况修改
	servAddr.sin_port = htons(9990);  //在实际应用中,建议将服务器的IP地址和端口号保存在配置文件中
	int sServerAddlen = sizeof(servAddr); //计算地址的长度
	//连接服务器
	printf("TcpClient trying to connect to TcpServer...\n");
	retVal = connect(sHost,(LPSOCKADDR)&servAddr,sizeof(servAddr));
	if(SOCKET_ERROR == retVal)
	{
	printf("Connect to TcpServer failed !\n");
	closesocket(sHost);
	WSACleanup();
	system("pause");
	return -1;
	}
	else
	{
		printf("Connected to TcpServer success...\n");
	}

	向服务器发送数据
	//ZeroMemory(buf,BUF_SIZE);
	//sprintf(buf,"Data form TcpClient!");
	//retVal = send(sHost,buf,strlen(buf),0);
	//if(SOCKET_ERROR == retVal)
	//{
	//printf("send failed!\n");
	//closesocket(sHost);
	//WSACleanup();
	//return -1;
	//}
	//printf("客户端数据发送完成!\n");

	//在客户端与服务器之间发送和接收数据
	//循环向服务器发送字符串,并显示反馈信息
	//发送“quit”将服务器程序退出,同时客户端程序自身也将退出
	while (true)
	{
		//向服务器发送数据
		printf("Please input a string to send :");
		std::string str;
		//接收输入的数据
		std::getline(std::cin,str);
		//将用户输入的数据复制到buf中
		ZeroMemory(buf,BUF_SIZE);
		strcpy(buf,str.c_str());
		//向服务器发送数据
		retVal = send(sHost,buf,strlen(buf),0);
		if(SOCKET_ERROR == retVal)
		{
			printf("send failed!\n");
			closesocket(sHost);
			WSACleanup();
			return -1;
		}
		//接收服务器回传的数据
		retVal == recv(sHost,buf,sizeof(buf)+1,0);
		printf("Recv Form Server:%s\n",buf);
		//如果收到“quit”,则退出
		if(strcmp(buf,"quit") == 0)
		{
			printf("quit!\n");
			break;
		}
	}

	//释放资源
	closesocket(sHost);
	WSACleanup();
}

服务器

#include "stdafx.h"

#include<iostream>
#include<winsock2.h>
#pragma comment(lib,"WS2_32.lib")
#define BUF_SIZE 64  //缓冲区大小

int _tmain(int argc, _TCHAR* argv[])
{
	WSADATA wsd;
	SOCKET sServer;
	SOCKET sAccept;
	SOCKET sClient;
	int retVal;
	char buf[BUF_SIZE];  //用于接受客户端数据的缓冲区

	//初始化套接字动态库
	if(WSAStartup(MAKEWORD(2,2),&wsd)!=0)
	{
		printf("WSAStartup failed !\n");
		return 1;
	}
	printf("服务器初始化成功!\n");

	//创建用于监听的套接字
	sServer = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
	if(INVALID_SOCKET == sServer)
	{
		printf("scoket failed!\n");
		WSACleanup();
		return -1;
	}
	printf("服务器创建监听套接字成功!\n");

	//设置服务器套接字地址
	SOCKADDR_IN addrServ;
	addrServ.sin_family = AF_INET;
	addrServ.sin_port = htons(9990);   //监听端口为9990
	addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	//绑定套接字sServer到本地地址,端口9990
	retVal = bind(sServer,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN));
	if(SOCKET_ERROR == retVal)
	{
		printf("bind failed !\n");
		closesocket(sServer);
		WSACleanup();
		return -1;
	}
	printf("服务器套接字地址绑定成功!\n");

	//监听套接字
	retVal = listen(sServer,3);
	if(SOCKET_ERROR == retVal)
	{
		printf("listen failed !\n");
		closesocket(sServer);
		WSACleanup();
		return -1;
	}
	printf("服务器已处于监听状态!\n");

	//接受客户请求
	sockaddr_in addrAccept;  //客户端地址
	int addrAcceptlen = sizeof(addrAccept);
	sAccept = accept(sServer,(sockaddr FAR*)&addrAccept,&addrAcceptlen);
	if(INVALID_SOCKET == sAccept)
	{
		printf("accept failed:%d!\n",GetLastError());
		closesocket(sServer);
		WSACleanup();
		return -1;
	}
	//printf("服务器已与一个客户端建立连接...\n");

	//printf("服务器开始接收客户端数据...\n");
	//ZeroMemory(buf,BUF_SIZE);  //清空接收数据的缓冲区
	//retVal=recv(sAccept,buf,BUF_SIZE,0);  //注意不是BUF_SIZE,而是BUFSIZE
	//if (SOCKET_ERROR == retVal)
	//{
	//	printf("recv failed !\n");
	//	closesocket(sAccept);
	//	closesocket(sServer);
	//	WSACleanup();
	//	return -1;
	//}
	//printf("Recv Form Client [%s:%d]:%s\n",inet_ntoa(addrAccept.sin_addr),addrAccept.sin_port,buf);

	//服务器与客户端之间发送和接收数据
	//循环接收客户端的数据,直接客户端发送quit命令后退出
	while (true)
	{
		ZeroMemory(buf,BUF_SIZE);  //清空接收数据的缓冲区
		retVal = recv(sClient,buf,BUFSIZ,0);
		if(SOCKET_ERROR == retVal)
		{
		printf("recv failed!\n");
		closesocket(sServer);
		closesocket(sClient);
		WSACleanup();
		return -1;
		}
		//获取当前系统时间
		SYSTEMTIME st;
		GetLocalTime(&st);
		char sDateTime[30];
		sprintf(sDateTime,"%4d-%2d-%2d %2d:%2d:%2d",st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond);
		//打印输出信息
		printf("%s,Recv Form Client [%s:%d]:%s\n",sDateTime,inet_ntoa(addrAccept.sin_addr),addrAccept.sin_port,buf);
		//如果客户端发送"quit"字符串,则服务器退出
		if(strcmp(buf,"quit") == 0)
		{
		retVal = send(sClient,"quit",strlen("quit"),0);
		break;
		}
		//否则向客户端发送回显字符串
		else{
		char msg[BUF_SIZE];
		sprintf(msg,"Message received - %s",buf);
		retVal = send(sClient,msg,strlen(msg),0); //向客户端发送回显字符串
		if(SOCKET_ERROR == retVal)
		{
		printf("send failed!\n");
		closesocket(sServer);
		closesocket(sClient);
		WSACleanup();
		return -1;
		}
		}
	}
//释放套接字
	closesocket(sAccept);
	closesocket(sServer);
	WSACleanup();
	system("pause");
	return 0;

}
上一篇:关于C语言二级指针的一个问题


下一篇:Linux 系统编程学习笔记 - 标准输入输出之缓冲