【实验题目】Echo实验
【实验目的】掌握套节字的基本使用方法。
【实验内容】
(1)编写TCP Echo程序
实验要求:
服务器把客户端发送来的任何消息都返回给客户端,返回的消息前面要加上服务器的当前时间。客户端把返回的消息显示出来。客户端每输入一条消息就建立TCP连接,并把消息发送给服务器,在收到服务器回应后关闭连接。
1、只运行客户端程序而不运行服务器程序会出现什么错误,截屏说明原因。
客户端调用connect()函数,向服务器进程发出连接请求,但服务器并没有运行,所以返回连接错误提示。
2、服务器如何可以退出循环?
客户端输入“exit”,结束服务器循环。如图:
客户端:此时客户端输入“exit
服务端:结束循环
2、 截屏(ctrl+alt+PrintScreen)服务器和客户端的运行结果(注明客户端和服务器):
客户端(两次运行)
服务端:
(2)、编写TCP Echo增强程序
实验要求:
在(1)的基础上,服务器在收到客户端的消息时显示服务器的当前时间、客户端的IP地址、客户端的端口号和客户端发来的信息,并把它们一并返回给客户端。
客户端在发送消息后把服务器发回给它的消息显示出来。*客户端程序与(1)同,不用修改
要求服务器直接从accept()的参数fsin中得到客户端的IP地址和端口号。注:服务器获取IP地址后要求直接使用s_un_b的四个分量得到IP地址,不能使用函数inet_ntoa()转换IP地址。
截屏服务器和客户端的运行结果(注明客户端和服务器):
客户端(两次)
服务端:
(3)编写UDP Echo增强程序
实验要求:
修改UDP例程,完成Echo功能,即当客户端发来消息时,服务器显示出服务器的当前时间、客户端的IP、客户端的端口号和客户发来的信息,并把它们一并发回给客户端,客户端然后把它们显示出来。
服务器可以直接从recvfrom()的参数from中得到客户端的IP地址和端口号,并且服务器用sendto()发回给客户端消息时可以直接用该参数from作为参数toAddr。可以使用inet_ntoa()转换客户端IP地址。
客户端程序的recvfrom()可以直接使用原来sendto使用的sock。该sock已经绑定了客户端的IP地址和端口号,客户端可以直接用来接收数据。
1、只运行客户端程序而不运行服务器程序会出现什么错误,截屏并说明原因。
服务器未运行,服务器没有把它们一并发回给客户端,客户端无法显示IP地址,且程序没有停止。
2、截屏服务器和客户端的运行结果(注明客户端和服务器):
客户端:
服务端:
源码:
TCP服务端:
#include
#include
#include
#include
#pragma comment(lib,"ws2_32.lib") //把ws2_32.lib加到Link页的连接库
#define PORT 15001 //通信的端口(指服务器端)
#define ERROR 0
#define BUFFER_SIZE 1024 //注意:此Server端数据接收缓冲区 >= Client端数据发送缓冲区 ,否则造成缓冲区溢出
/*
服务端原理:
1、服务器进程创建套接字
2、将本地地址绑定到所创建的套接字上,以三元组{<通信协议>,,<端口号>}在网络上标识该套接字
3、将套接字置入监听模式,并准备接受连接请求
*/
void tran(int ip1, int ip2, int ip3, int ip4, char dest[])
{
int cnt = 0;
char des[100];
if (ip4 == 0) {
des[cnt++] = '0';
}
while (ip4)
{
des[cnt++] = ip4 % 10 + '0';
ip4 /= 10;
}
des[cnt++] = '.';
if (ip3 == 0) {
des[cnt++] = '0';
}
while (ip3)
{
des[cnt++] = ip3 % 10 + '0';
ip3 /= 10;
}
des[cnt++] = '.';
if (ip2 == 0) {
des[cnt++] = '0';
}
while (ip2)
{
des[cnt++] = ip2 % 10 + '0';
ip2 /= 10;
}
des[cnt++] = '.';
if (ip1 == 0) {
des[cnt++] = '0';
}
while (ip1)
{
des[cnt++] = ip1 % 10 + '0';
ip1 /= 10;
}
for (int i = 0; i < cnt; ++i)
{
dest[cnt - 1 - i] = des[i];
}
dest[cnt] = '\0';
}
void tran2(unsigned short int a, char c[])
{
int i = 1;
int x = a / 10;
while (x)
{
i++;
x = x / 10;
}
for (int j = i - 1; j >= 0; j--)
{
int b = a % 10;
c[j] = b + '0';
a = a / 10;
}
c[i] = '\0';
}
int main()
{
char c[BUFFER_SIZE];
char *pts; /* pointer to time string */
time_t now;
WSADATA WSAData;
if (WSAStartup(MAKEWORD(2, 0), &WSAData) == SOCKET_ERROR) //启动winsock ,WSAStartup()函数对Winsock DLL进行初始化
{
printf("Socket initialize fail!\n");
exit(1);
}
else
{
printf("服务器已启动!\n\n");
}
SOCKET sock; //服务进程创建套接字句柄(用于监听)
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == ERROR) //调用socket()函数创建一个流套接字,参数(网络地址类型,套接字类型,网络协议)
{
printf("Socket create!\n");
WSACleanup();
exit(1);
}
struct sockaddr_in ServerAddr; //sockaddr_in结构用来标识TCP/IP协议下的地址,可强制转换为sockaddr结构
ServerAddr.sin_family = AF_INET; //sin_family字段必须设为AF_INET,表示该Socket处于Internet域
ServerAddr.sin_port = htons(PORT); //sin_port字段用于指定服务端口,注意避免冲突
ServerAddr.sin_addr.s_addr = INADDR_ANY; //sin_addr字段用于把一个IP地址保存为一个4字节的数,无符号长整型,根据不同用法还可表示本地或远程IP地址
if (bind(sock, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR) //调用bind()函数将本地地址绑定到所创建的套接字上,以在网络上标识该套接字
{
printf("Bind fail!\n");
closesocket(sock);
WSACleanup();
exit(1);
}
//printf("Server Socket Port:%d\n", ntohs(ServerAddr.sin_port));
if (listen(sock, 10) == SOCKET_ERROR) //调用listen()函数将套接字置入监听模式并准备接受连接请求,参数(已捆绑未连接的套接字描述字,正在等待连接的最大队列长度)
{
printf("Listen fail!\n");
closesocket(sock);
WSACleanup();
exit(1);
}
SOCKET msgsock; //创建一个新的套接字(用于接收accept函数的返回值,即表示已经接受的那个客户端的连接,进而接收Client发来的数据)
char buf[BUFFER_SIZE]; //数据接收缓冲区
while (1)
{
int alen = sizeof(struct sockaddr);
if ((msgsock = accept(sock, (struct sockaddr *)&ServerAddr, &alen)) == INVALID_SOCKET) //进入监听状态后,调用accept()函数接收客户端的连接请求,并把连接传给msgsock套接字,原sock套接字继续监听其他客户机连接请求
{
printf("Accept fail!\n");
continue;
}
memset(buf, 0, sizeof(buf)); //初始化数据接收缓冲区
recv(msgsock, buf, BUFFER_SIZE, 0); //接收客户端发送过来的数据
if (buf[0] == 'e' && buf[1] == 'x' && buf[2] == 'i' && buf[3] == 't') //"exit"命令,退出程序
{
printf("The End.\n");
break;
}
(void)time(&now); // 取得系统时间
pts = ctime(&now);
printf("收到消息:%s", buf);
printf("收到时间:%s", pts);
send(msgsock, buf, BUFFER_SIZE, 0);
send(msgsock, pts, BUFFER_SIZE, 0);
printf("客户端IP地址:%d.%d.%d.%d\n", ServerAddr.sin_addr.S_un.S_un_b.s_b1, ServerAddr.sin_addr.S_un.S_un_b.s_b2, ServerAddr.sin_addr.S_un.S_un_b.s_b3, ServerAddr.sin_addr.S_un.S_un_b.s_b4);
printf("客户端端口号:%d\n\n\n", ServerAddr.sin_port);
tran(ServerAddr.sin_addr.S_un.S_un_b.s_b1, ServerAddr.sin_addr.S_un.S_un_b.s_b2, ServerAddr.sin_addr.S_un.S_un_b.s_b3, ServerAddr.sin_addr.S_un.S_un_b.s_b4, c);
send(msgsock, c, BUFFER_SIZE, 0);
tran2(ServerAddr.sin_port, c);
send(msgsock, c, BUFFER_SIZE, 0);
printf("\n\n");
closesocket(msgsock);
}
closesocket(sock); //关闭套接字
WSACleanup(); //终止对Winsock DLL的使用,并释放资源
return 0;
}
TCP客户端:
#include
#include
#pragma comment(lib,"ws2_32.lib") //把ws2_32.lib加到Link页的连接库
//#define IP "172.18.68.243" //在两台计算机上测试,IP为Server端的IP地址
#define IP "127.0.0.1" //在一台计算机上测试,IP为本地回送地址
#define PORT 15001 //注意:客户端设置通信的端口 = 服务端的端口
#define BUFFER_SIZE 1024 //数据发送缓冲区大小
int main()
{
char buf1[BUFFER_SIZE];
char buf2[BUFFER_SIZE];
char buf3[BUFFER_SIZE];
char buf4[BUFFER_SIZE];
//const char *host = "127.0.0.1";
char buf[BUFFER_SIZE]; //buf数组存放客户端发送的消息
int inputLen; //用于输入字符自增变量
printf("输入要发送的信息:");
inputLen = 0;
memset(buf, 0, sizeof(buf));
while ((buf[inputLen++] = getchar()) != '\n') //输入以回车键为结束标识
{
;
}
WSADATA WSAData;
if (WSAStartup(MAKEWORD(2, 0), &WSAData) == SOCKET_ERROR) //WSAStartup()函数对Winsock DLL进行初始化
{
printf("Socket initialize fail!\n");
}
SOCKET sock; //客户端进程创建套接字
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR) //创建流套接字(与服务端保持一致)
{
printf("Socket create fail!\n");
WSACleanup();
}
struct sockaddr_in ClientAddr; //sockaddr_in结构用来标识TCP/IP协议下的地址,可强制转换为sockaddr结构
ClientAddr.sin_family = AF_INET; //指Internet域
ClientAddr.sin_port = htons(PORT); //指定服务端所预留的端口
ClientAddr.sin_addr.s_addr = inet_addr(IP); //指定服务端所绑定的IP地址
if (connect(sock, (struct sockaddr *)&ClientAddr, sizeof(ClientAddr)) == SOCKET_ERROR) //调用connect()函数,向服务器进程发出连接请求
{
printf("Connect fail!\n");
closesocket(sock);
WSACleanup();
}
else
{
send(sock, buf, BUFFER_SIZE, 0); //向服务器发送数据
if (buf[0] == 'e' && buf[1] == 'x' && buf[2] == 'i' && buf[3] == 't')
{
printf("The End.\n");
}
else {
recv(sock, buf1, BUFFER_SIZE, 0);
recv(sock, buf2, BUFFER_SIZE, 0);
recv(sock, buf3, BUFFER_SIZE, 0);
recv(sock, buf4, BUFFER_SIZE, 0);
printf("\n\n");
printf("收到信息:");
printf("%s", buf1);
printf("收到时间:");
printf("%s", buf2);
printf("客户端IP地址:");
printf("%s\n", buf3);
printf("客户端端口号:");
printf("%s\n", buf4);
printf("\n\n");
}
}
closesocket(sock); //关闭套接字
WSACleanup(); //终止对Winsock DLL的使用,并释放资源,以备下一次使用
return 0;
}
UDP客户端:
#include
#include
#include
#include
#define BUFLEN 2000 // 缓冲区大小
#define WSVERS MAKEWORD(2, 2) // 指明版本2.2
#pragma comment(lib,"ws2_32.lib") // 加载winsock 2.2 Llibrary
void main(int argc, char *argv[])
{
char *host = "127.0.0.1"; /* server IP to connect */
char *service = "50500"; /* server port to connect */
struct sockaddr_in toAddr; /* an Internet endpoint address */
int size = sizeof(toAddr);
char buf[BUFLEN+1]; /* buffer for one line of text */
char buf1[BUFLEN + 1];
char buf2[BUFLEN + 1];
char buf3[BUFLEN + 1];
SOCKET sock; /* socket descriptor */
int cc1;
int cc; /* recv character count */
char *pts; /* pointer to time string */
time_t now; /* current time */
WSADATA wsadata;
WSAStartup(WSVERS, &wsadata); /* 启动某版本Socket的DLL */
sock = socket(PF_INET, SOCK_DGRAM,IPPROTO_UDP);
memset(&toAddr, 0, sizeof(toAddr));
toAddr.sin_family = AF_INET;
toAddr.sin_port = htons((u_short)atoi(service)); //atoi:把ascii转化为int. htons:主机序(host)转化为网络序(network), s--short
toAddr.sin_addr.s_addr = inet_addr(host); //如果host为域名,需要先用函数gethostbyname把域名转化为IP地址
memset(buf,'\0',BUFLEN); //fill 1000 bytes with 'e'
//buf[BUFLEN] = '\0';
(void) time(&now); // 取得系统时间
pts = ctime(&now); // 把时间转换为字符串
memcpy(buf,pts,strlen(pts));
printf("时间:");
printf("%s", buf);
int inputLen = 0;
cc=sendto(sock, buf, BUFLEN, 0,(SOCKADDR *)&toAddr, sizeof(toAddr));
printf("输入消息:");
scanf("%s", buf1);
sendto(sock, buf1, BUFLEN, 0, (SOCKADDR *)&toAddr, sizeof(toAddr));
printf("客户端IP地址:");
recvfrom(sock, buf2, BUFLEN, 0, (SOCKADDR *)&toAddr, &size);
printf("%s\n", buf2);
printf("客户端端口号:");
recvfrom(sock, buf3, BUFLEN, 0, (SOCKADDR *)&toAddr, &size);
printf("%s\n\n", buf3);
closesocket(sock);
WSACleanup(); /* 卸载某版本的DLL */
printf("按任意键退出...");
getchar();
}
UDP服务端:
#include
#include
#include
#include "conio.h"
#define BUFLEN 2000 // 缓冲区大小
#define WSVERS MAKEWORD(2, 2) // 指明版本2.2
#pragma comment(lib,"ws2_32.lib") // 加载winsock 2.2 Llibrary
void tran2(unsigned short int a, char c[])
{
int i = 1;
int x = a / 10;
while (x)
{
i++;
x = x / 10;
}
for (int j = i - 1; j >= 0; j--)
{
int b = a % 10;
c[j] = b + '0';
a = a / 10;
}
c[i] = '\0';
}
void main(int argc, char *argv[])
{
char *host = "127.0.0.1"; /* server IP Address to connect */
char *service = "50500"; /* server port to connect */
struct sockaddr_in sin; /* an Internet endpoint address */
struct sockaddr_in from; /* sender address */
int fromsize = sizeof(from);
char buf[BUFLEN+1]; /* buffer for one line of text */
SOCKET sock; /* socket descriptor */
int cc; /* recv character count */
char c[BUFLEN+1];
WSADATA wsadata;
WSAStartup(WSVERS, &wsadata); /* 加载winsock library,WSVERS为请求版本,wsadata返回系统实际支持的最高版本。 */
sock = socket(PF_INET, SOCK_DGRAM,IPPROTO_UDP); // 创建UDP套接字, 参数:因特网协议簇(family),数据报套接字,UDP协议号, 返回:要监听套接字的描述符或INVALID_SOCKET
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY; // 绑定(监听)所有的接口。
sin.sin_port = htons((u_short)atoi(service)); // 绑定指定接口。atoi--把ascii转化为int,htons -- 主机序(host)转化为网络序(network), 为short类型。
bind(sock, (struct sockaddr *)&sin, sizeof(sin)); // 绑定本地端口号(和本地IP地址)
while(!_kbhit()){ //检测是否有按键
recvfrom(sock, buf, BUFLEN, 0, (SOCKADDR *)&from, &fromsize); //接收客户数据。返回结果:cc为接收的字符数,from中将包含客户IP地址和端口号。
printf("时间:");
printf("%s", buf);
recvfrom(sock, buf, BUFLEN, 0, (SOCKADDR *)&from, &fromsize);
printf("客户端的消息:" );
printf("%s\n", buf);
printf("客户端IP地址:%s\n", inet_ntoa(from.sin_addr));
printf("客户端端口号:%d\n\n\n", from.sin_port);
//tran(from.sin_addr.S_un.S_un_b.s_b1, from.sin_addr.S_un.S_un_b.s_b2, from.sin_addr.S_un.S_un_b.s_b3, from.sin_addr.S_un.S_un_b.s_b4, c);
sendto(sock, inet_ntoa(from.sin_addr), BUFLEN, 0, (SOCKADDR *)&from, sizeof(from));
tran2(from.sin_port, c);
sendto(sock, c, BUFLEN, 0, (SOCKADDR *)&from, sizeof(from));
}
closesocket(sock);
WSACleanup(); /* 卸载某版本的DLL */
getchar();
}