大家好,又见面了,我是你们的朋友全栈君。
基础知识
TCP
传输控制协议(TCP)是为了在不可靠的互联网络上提供可靠的端到端字节流而专门设计的一个传输协议,位于传输层,旨在通过Internet发送报文。
Socket(套接字)
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作,实际上是一个指向传输提供者的句柄。在WinSock中,就是通过操作该句柄来实现网络通信和管理的。
IP
IP是Internet Protocol(网际互连协议)的缩写,是TCP/IP体系中的网络层协议。为了使网络上的计算机能够彼此识别对方,每台计算机都需要一个IP来标识自己。
端口
端口可以认为是设备与外界通讯交流的出口。在网络上,计算机通过IP来找到对方,而应用程序则通过绑定端口来标识通讯的应用程序。
TCP通讯的基本流程
代码编写
服务端
tcp_server.c
#include <stdio.h>
#include <WinSock2.h>
#define version_h 2
#define version_l 2
#define DEFAULT_BUFLEN 512
int main()
{
//1.初始化套接字库
//高位字节指定次要版本号;低位字节指定主版本号。
WORD wVersionRequested = MAKEWORD(version_h, version_l);
WSADATA wsaData;
int error = WSAStartup(wVersionRequested, &wsaData);
if (errno != 0)
{
printf("Cant initiates use of the Winsock DLL by a process!\n");
return 1;
}
//确认WinSock DLL支持2.2,如果DLL支持更高版本,它依然返回2.2
if (LOBYTE(wsaData.wVersion) != version_l ||
HIBYTE(wsaData.wVersion) != version_h)
{
/* 找不到可用的WinSock DLL */
printf("could not find a usable WinSock DLL!\n");
WSACleanup();
return 1;
}
//2.创建套接字
SOCKADDR_IN server_address;
//The address family for the transport address. This member should always be set to AF_INET.
server_address.sin_family = AF_INET;
//An IN_ADDR structure that contains an IPv4 transport address.
server_address.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//A transport protocol port number.
server_address.sin_port = htons(5000);
//申请套接字
SOCKET socket_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket_server)
{
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
WSACleanup();
return 1;
}
//3.绑定套接字到本地的某个地址和端口
if (SOCKET_ERROR == bind(socket_server, (SOCKADDR *)&server_address, sizeof(server_address)))
{
wprintf(L"bind failed with error %u\n", WSAGetLastError());
closesocket(socket_server);
WSACleanup();
return 1;
}
wprintf(L"bind returned success\n");
//4.监听客户端
if (SOCKET_ERROR == listen(socket_server, 1))
wprintf(L"listen function failed with error: %d\n", WSAGetLastError());
wprintf(L"Listening on socket...\n");
//5.接受客户端连接
SOCKADDR_IN client_address;
int address_length=sizeof(client_address);
SOCKET socket_client = accept(socket_server, (SOCKADDR *)&client_address, &address_length);
if (INVALID_SOCKET == socket_client)
wprintf(L"accept failed with error: %ld\n", WSAGetLastError());
wprintf(L"connect client success\n");
//6.进行会话
char send_buff[DEFAULT_BUFLEN] = "I receive your message!";
char recv_buff[DEFAULT_BUFLEN] = {
0};
while (1)
{
int ret = recv(socket_client, recv_buff, DEFAULT_BUFLEN, 0);
if (ret > 0)
printf("Receive from Client: %s\n", recv_buff);
else
{
if (0 == ret)
{
printf("Connection closed\n");
break;
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
closesocket(socket_client);
closesocket(socket_server);
WSACleanup();
return 1;
}
}
ret = send(socket_client, send_buff,DEFAULT_BUFLEN, 0);
if (SOCKET_ERROR == ret)
{
wprintf(L"send failed with error: %d\n", WSAGetLastError());
closesocket(socket_client);
closesocket(socket_server);
WSACleanup();
return 1;
}
}
//7.关闭套接字
if (SOCKET_ERROR == closesocket(socket_client))
{
wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
if (SOCKET_ERROR == closesocket(socket_server))
{
wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
客服端
tcp_client.c
#include <stdio.h>
#include <WinSock2.h>
#define version_h 2
#define version_l 2
#define DEFAULT_BUFLEN 512
int main()
{
//1.初始化套接字库
//高位字节指定次要版本号;低位字节指定主版本号。
WORD wVersionRequested = MAKEWORD(version_h, version_l);
WSADATA wsaData;
int error = WSAStartup(wVersionRequested, &wsaData);
if (errno != 0)
{
printf("Cant initiates use of the Winsock DLL by a process!\n");
return 1;
}
//确认WinSock DLL支持2.2,如果DLL支持更高版本,它依然返回2.2
if (LOBYTE(wsaData.wVersion) != version_l ||
HIBYTE(wsaData.wVersion) != version_h)
{
/* 找不到可用的WinSock DLL */
printf("could not find a usable WinSock DLL!\n");
WSACleanup();
return 1;
}
//2.创建套接字
SOCKADDR_IN server_address;
//The address family for the transport address. This member should always be set to AF_INET.
server_address.sin_family = AF_INET;
//An IN_ADDR structure that contains an IPv4 transport address.
server_address.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//A transport protocol port number.
server_address.sin_port = htons(5000);
//申请套接字
SOCKET socket_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == socket_server)
{
wprintf(L"socket function failed with error: %ld\n", WSAGetLastError());
getchar();
WSACleanup();
return 1;
}
//3.连接服务器
if(SOCKET_ERROR==connect(socket_server, (SOCKADDR *)&server_address, sizeof(server_address)))
{
wprintf(L"connect function failed with error: %ld\n", WSAGetLastError());
getchar();
closesocket(socket_server);
WSACleanup();
return 1;
}
wprintf(L"Connected to server.\n");
//4.进行会话
int recv_ret;
int send_ret;
char send_buff[DEFAULT_BUFLEN] ={
0};
char recv_buff[DEFAULT_BUFLEN] = {
0};
while (1)
{
printf("You send:");
gets(send_buff);
send_ret = send(socket_server, send_buff,DEFAULT_BUFLEN, 0);
if (SOCKET_ERROR == send_ret)
{
wprintf(L"send failed with error: %d\n", WSAGetLastError());
getchar();
closesocket(socket_server);
WSACleanup();
return 1;
}
int ret = recv(socket_server, recv_buff, DEFAULT_BUFLEN, 0);
if (ret > 0)
printf("Receive from Server: %s\n", recv_buff);
else
{
if (0 == ret)
{
printf("Connection closed\n");
break;
}
else
{
printf("recv failed: %d\n", WSAGetLastError());
getchar();
closesocket(socket_server);
WSACleanup();
return 1;
}
}
}
//5.关闭套接字
if (SOCKET_ERROR == closesocket(socket_server))
{
wprintf(L"closesocket function failed with error %d\n", WSAGetLastError());
getchar();
WSACleanup();
return 1;
}
WSACleanup();
return 0;
}
windows编译脚本
因为本人是用gcc编译的
complie.bat
gcc -g .\tcp_client.c -o .\tcp_client -lws2_32
gcc -g .\tcp_server.c -o .\tcp_server -lws2_32
pause
执行效果
总结
错误判断可能有点多,不过出错后可以更容易的发现错误并解决
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/158482.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...