TCP实现网络通信(客户端和服务端)
程序员文章站
2022-07-01 15:02:20
...
内容简介
主要通过TCP实现客户端和服务端的通信,代码分为客户端和服务端,主要用于在客户端输入文件名获取服务端存在的文件,并下载到客户端。执行流程为:先启动服务端,然后在启动客户端,客户端输入需要访问的服务端文件名,服务端收到消息后,先查看是否文件存在,如果存在则将内容发送给客户端,如果不存在则给客户端发送文件不存在,请求客户端重新输入文件名。
代码
Server.h
#ifndef __TCP__
#define __TCP__
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<WinSock2.h>
#define BUFFER_SIZE 1024
#pragma comment(lib,"ws2_32.lib")
#pragma warning(disable:4996)
//Windows相关环境初始化
int Init();
//创建服务端socket
void Create_Socket();
//绑定服务端socket
void Bind();
//通过服务端监听客户端
void Listen();
//接受客户端socket请求
void Accept();
//数据通讯
void Data_Communication();
#endif
Server.c
#include"Server.h"
SOCKADDR_IN server;
SOCKET server_socket;
SOCKADDR_IN clientMsg;
SOCKET client_socket;
int Init()
{
int res;
WORD wdVersion = MAKEWORD(2, 2);
WSADATA wdSockMsg;
res = WSAStartup(wdVersion, &wdSockMsg);
if (0 != res)
{
switch (res)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库");
break;
case WSAEINPROGRESS:
printf("请重新启动");
break;
case WSAEPROCLIM:
printf("请尝试关掉不必要的软件,以为当前网络运行提供充足资源");
break;
}
return -1;
}
//高位装的是副版本号,低位装的是主版本号
if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion))
{
WSACleanup();
return -2;
}
return res;
}
void Create_Socket()
{
int Code;
//这里如果使用sockaddr_in则需要加上struct
//如果使用 SOCKADDR_IN则不需要
server.sin_family = AF_INET;
server.sin_port = htons(8087);
server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == server_socket)
{
Code = WSAGetLastError();
printf("Create_Socket Failed,Code = %d\n", Code);
WSACleanup();
return;
}
}
void Bind()
{
int Code;
Code = bind(server_socket, (SOCKADDR*)&server, sizeof(SOCKADDR));
if (SOCKET_ERROR == Code)
{
//如果想看错误码则需要在可能出错的语句后面执行WASGetLastError、
Code = WSAGetLastError();
//一定要先关闭socket连接,然后在清理网络库
printf("Bind Failed,Code = %d\n", Code);
closesocket(server_socket);
WSACleanup();
return;
}
}
void Listen()
{
//listen用于监听客户端传来的链接,accept将客户端的信息绑定到一个socket上,也就是
//给客户端创建一个socket通道返回值返回给我们客户端的socket
//一次只能创建一个,有几个客户端链接,就调用几次
int Code;
Code = listen(server_socket, SOMAXCONN);
if (SOCKET_ERROR == Code)
{
Code = WSAGetLastError();
printf("Listen Failed,Code = %d\n", Code);
closesocket(server_socket);
WSACleanup();
return;
}
}
void Accept()
{
//创建客户端连接
SOCKADDR_IN clientMsg;
int len = sizeof(clientMsg);
int Code;
//下面语句为获取clientMsg里面的信息。如果不想获取则后两个则全为NULL
//如何不想通过accept接收客户端信息,则可以这样写accept(socketserver,NULL,NULL)
//getpeername(socketclient,(struct sockaddr*)&clientMsg,&len);
//将客户端信息通过getpeername获取
//getsockname用于获得本地的ip和端口
//getsockname(socketclient,(struct sockaddr*)&sockclientMsg,&len);
//无论第一个参数为socketclient
//还是socketserver端口都为8087
client_socket = accept(server_socket, (SOCKADDR*)&clientMsg, &len);
if (INVALID_SOCKET == client_socket)
{
//如果想看错误码则需要在可能出错的语句后面执行WASGetLastError、
Code = WSAGetLastError();
//一定要先关闭socket连接,然后在清理网络库
printf("Accept Failed,Code = %d\n", Code);
closesocket(client_socket);
closesocket(server_socket);
WSACleanup();
return;
}
printf("\t客户端连接成功!!!\n");
}
void Data_Communication()
{
int Code;
char buf[BUFFER_SIZE];
FILE* fp;
//注意不用考虑\0;
if (SOCKET_ERROR == send(client_socket, "我是服务端,我收到了你的消息!!!\n", strlen("我是服务端,我收到了你的消息!!!\n"), 0))
{
Code = WSAGetLastError();
printf("Send Failed,Code = %d\n", Code);
closesocket(client_socket);
closesocket(server_socket);
return;
}
while (1)
{
memset(buf, 0, sizeof(buf));
Code = recv(client_socket, buf, BUFFER_SIZE, 0);
if (0 == Code)
{
printf("客户端下线=%d\n",Code);
return;
}
else if (SOCKET_ERROR == Code)
{
Code = WSAGetLastError();
printf("Recv Failed,Code=%d",Code);
closesocket(client_socket);
closesocket(server_socket);
WSACleanup();
return;
}
else
{
printf("%d\t%s\n", Code, buf);
}
fp = fopen(buf, "rb+");
if (fp == NULL)
{
memset(buf, 0, sizeof(buf));
sprintf(buf, "%s","The file doesn't exist!!\n");
if (SOCKET_ERROR == send(client_socket, buf, strlen(buf), 0))
{
Code = WSAGetLastError();
printf("Send check file formation failed,Code = %d", Code);
closesocket(client_socket);
closesocket(server_socket);
WSACleanup();
return;
}
continue;
}
memset(buf, 0, sizeof(buf));
while (fgets(buf, sizeof(buf), fp) != NULL)
{
if (SOCKET_ERROR == send(client_socket, buf, strlen(buf), 0))
{
Code = WSAGetLastError();
printf("Send file content failed,Code= %d", Code);
closesocket(client_socket);
closesocket(server_socket);
WSACleanup();
return;
}
memset(buf, 0, sizeof(buf));
}
fclose(fp);
}
closesocket(client_socket);
closesocket(server_socket);
WSACleanup();
system("pause");
}
main.c(测试程序)
#include "Server.h"
int main()
{
int Code;
Code = Init();
printf("Init_Code = %d\n", Code);
Create_Socket();
Bind();
Listen();
Accept();
Data_Communication();
return 0;
}
Client.h
#ifndef __TCP__
#define __TCP__
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>
#include<WinSock2.h>
#define PORT 8087
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024
#define FILE_NAME_MAX_SIZE 512
#pragma warning(disable:4996)
#pragma comment(lib,"WS2_32")
//Windows相关环境初始化
int Init();
//创建客户端socket
void Create_Socket();
//接受客户端socket请求
void Connect();
//数据通讯
void Data_Communication();
#endif
Client.c
#include "Client.h"
SOCKET client_socket;
int Init()
{
int res;
WORD wdVersion = MAKEWORD(2, 2);
WSADATA wdSockMsg;
res = WSAStartup(wdVersion, &wdSockMsg);
if (0 != res)
{
switch (res)
{
case WSASYSNOTREADY:
printf("重启下电脑试试,或者检查网络库");
break;
case WSAVERNOTSUPPORTED:
printf("请更新网络库");
break;
case WSAEINPROGRESS:
printf("请重新启动");
break;
case WSAEPROCLIM:
printf("请尝试关掉不必要的软件,以为当前网络运行提供充足资源");
break;
}
return -1;
}
//高位装的是副版本号,低位装的是主版本号
if (2 != HIBYTE(wdSockMsg.wVersion) || 2 != LOBYTE(wdSockMsg.wVersion))
{
WSACleanup();
return -2;
}
return res;
}
void Create_Socket()
{
client_socket = socket(AF_INET, SOCK_STREAM, 0);
if (SOCKET_ERROR == client_socket)
{
printf("Create Socket Error!");
return;
}
}
void Connect()
{
//指定服务端的地址
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr(SERVER_IP);
server_addr.sin_port = htons(PORT);
if (SOCKET_ERROR == connect(client_socket, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)))
{
printf("Can Not Connect To Server IP!\n");
return;
}
}
void Data_Communication()
{
char file_name[FILE_NAME_MAX_SIZE];
size_t length = 0;
char buffer[BUFFER_SIZE];
FILE* fp;
//输入文件名
memset(buffer, 0, sizeof(buffer));
if ((length = recv(client_socket, buffer, BUFFER_SIZE, 0))>0)
{
printf("%s", buffer);
memset(buffer, 0, sizeof(buffer));
}
while (TRUE)
{
memset(file_name, 0, sizeof(file_name));
printf("Please Input File Name On Server: ");
scanf("%s", file_name);
memset(buffer, 0, sizeof(buffer));
if (send(client_socket, file_name, strlen(file_name), 0) < 0)
{
printf("Send File Name Failed\n");
return;
}
memset(buffer, 0, sizeof(buffer));
if ((length = recv(client_socket, buffer, BUFFER_SIZE, 0))>0)
{
if (strncmp(buffer, "The file doesn't", strlen("The file doesn't")) == 0)
{
printf("%s", buffer);
memset(buffer, 0, sizeof(buffer));
continue;
}
else
{
fp = fopen(file_name, "wb+");
if (NULL == fp)
{
printf("File:%s Can Not Open To Write\n", file_name);
return ;
}
else
{
if (fwrite(buffer, 1, length, fp) < length)
{
printf("File:%s Write Failed\n", file_name);
break;
}
printf("%s", buffer);
memset(buffer, 0, sizeof(buffer));
//文件中的内容可以通过一个recv去接收
length = recv(client_socket, buffer, BUFFER_SIZE, 0);
if (fwrite(buffer, 1, length, fp) < length)
{
printf("File:%s Write Failed\n", file_name);
break;
}
printf("%s", buffer);
memset(buffer, 0, sizeof(buffer));
printf("Receive File:%s From Server Successful!\n", file_name);
fclose(fp);
}
}
}
}
closesocket(client_socket);
WSACleanup();
}
main.c(测试程序)
#include "Client.h"
int main(int argc, const char* argv[])
{
int Code;
Code = Init();
printf("Init_Code = %d\n", Code);
Create_Socket();
Connect();
Data_Communication();
return 0;
}
运行结果:
成功读取文件内容
在客户端下载了服务端的文件
客户端输入的文件名在服务端无法找到