TCP的socket和UDP大同小异,基本的代码结构都是相同的。一些相同的接口本文就不赘述了,例如,socket,bind,有需要看这篇文章UDP socket
服务端server
两步:初始化服务端,运行服务端
初始化服务端
创建socket,bind IP和端口号,listen监听
socket的第二个参数使用SOCK_STREAM,因为与UDP不同的是TCP面向字节流。其余的与UDP无异。
listen接口
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
void init()
{
// socket
sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd_ < 0)
{
exit(1);
}
// bind
struct sockaddr_in local;
local.sin_family = AF_INET;
inet_pton(AF_INET, ip_.c_str(), &local.sin_addr);
local.sin_port = htons(port_);
int n = bind(sockfd_, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
exit(2);
}
// listen
if (listen(sockfd_, 10) < 0)
{
exit(3);
}
}
运行服务端
四步:accept接收请求,接收数据,处理数据,最后发送数据
accept接口
初始化socket得到的文件描述符是用来listen的,而accept获得的文件描述符才是用来传输数据的,read和write接口就是用这个描述符的。
read和write很简单,第一个参数是文件描述符,第二个参数是读取/发送的数据地址,第三个参数是数据的大小
void run()
{
while (1)
{
// accept
struct sockaddr_in client;
socklen_t len = sizeof(client);
int lis_fd = accept(sockfd_, (struct sockaddr *)&client, &len);
// 接收数据
char buf[1024] = {0};
int n = read(lis_fd, buf, sizeof(buf));
if (n < 0)
{
break;
}
else if (n == 0)
{
cout << "quit" << endl;
break;
}
else
{
buf[n] = 0;
std::cout << "server get msg:" << buf << std::endl;
// 处理数据
std::string msg = "server say: I have get msg >_";
msg += buf;
write(lis_fd, msg.c_str(), msg.size());
}
}
}
客户端client
五步:创建socket(同服务端)->bind(由OS完成)->connect请求连接->发送数据write->接收数据read
connect接口
第一个参数是socket获得的文件描述符,第二个是要连接的服务端的属性结构体(记得类型转换),TCP的结构体数据类型是struct sockaddr_in。第三个是结构体大小
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cout << argv[0] << "[ip]" << "[port]" << std::endl;
return -1;
}
// 获取命令行参数
uint16_t port = stoi(argv[2]);
string ip = argv[1];
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
while (1)
{
// socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cout << "client create fail" << endl;
exit(1);
}
// bind由os随机分配端口完成
// connect
int n = connect(sockfd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
if (n < 0)
{
std::cout << "client connect fail" << std::endl;
exit(2);
}
std::string msg;
std::cout << "Please Enter>_";
getline(std::cin, msg);
// 发送数据
n = write(sockfd, msg.c_str(), msg.size());
if (n < 0)
{
std::cout << "write fail" << std::endl;
continue;
}
char buf[1024] = {0};
// 接收数据
n = read(sockfd, buf, sizeof(buf));
if (n < 0)
{
std::cout << "read fail" << std::endl;
continue;
}
buf[n] = 0;
cout << buf << endl;
}
}
序列化和反序列化
序列化是将数据结构或对象转换为一种可以存储或传输的格式,反序列化是序列化的逆过程,它将序列化后的数据重新还原为原始的对象或数据结构。
其实这本质和协议是一样的,都是一种约定。服务端和客户端都按照一个相同的规则将收发的数据做处理,以便于数据的传输。
序列化和反序列化的应用场景
-
网络编程:
客户端与服务器之间传输数据时,往往通过序列化和反序列化的方式来进行。例如,在你写的 TCP 客户端/服务器代码中,客户端需要将数据(如数学表达式)序列化后发送给服务器,服务器再反序列化处理并返回结果。 -
数据持久化:
将内存中的数据或对象序列化后保存到数据库或文件中,以便稍后可以重新加载数据并继续使用。 -
跨进程通信:
当两个不同的进程需要交换数据时,序列化可以将复杂数据结构转换为字节流,方便在进程之间传递。
源码
//tcpserver.hpp
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
enum
{
SocketFail = 1,
BindFail,
ListenError
};
class tcpServer
{
public:
tcpServer(std::string ip = "0.0.0.0", uint16_t port = 8888)
: ip_(ip), port_(port)
{
}
~tcpServer()
{
}
void init()
{
// socket
sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd_ < 0)
{
exit(SocketFail);
}
// bind
struct sockaddr_in local;
local.sin_family = AF_INET;
inet_pton(AF_INET, ip_.c_str(), &local.sin_addr);
local.sin_port = htons(port_);
int n = bind(sockfd_, (struct sockaddr *)&local, sizeof(local));
if (n < 0)
{
exit(BindFail);
}
// listen
if (listen(sockfd_, 10) < 0)
{
exit(ListenError);
}
}
void run()
{
while (1)
{
// accept
struct sockaddr_in client;
socklen_t len = sizeof(client);
int lis_fd = accept(sockfd_, (struct sockaddr *)&client, &len);
// 接收数据
char buf[1024] = {0};
int n = read(lis_fd, buf, sizeof(buf));
if (n < 0)
{
break;
}
else if (n == 0)
{
cout << "quit" << endl;
break;
}
else
{
buf[n] = 0;
std::cout << "server get msg:" << buf << std::endl;
// 处理数据
std::string msg = "server say: I have get msg >_";
msg += buf;
write(lis_fd, msg.c_str(), msg.size());
}
}
}
private:
int sockfd_;
std::string ip_;
uint16_t port_;
};
//tcpclient.cpp
#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
if (argc != 3)
{
std::cout << argv[0] << "[ip]" << "[port]" << std::endl;
return -1;
}
// 获取命令行参数
uint16_t port = stoi(argv[2]);
string ip = argv[1];
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip.c_str());
while (1)
{
// socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
std::cout << "client create fail" << endl;
exit(1);
}
// bind由os随机分配端口完成
// connect
int n = connect(sockfd, (struct sockaddr *)&server, (socklen_t)sizeof(server));
if (n < 0)
{
std::cout << "client connect fail" << std::endl;
exit(2);
}
std::string msg;
std::cout << "Please Enter>_" << std::endl;
getline(std::cin, msg);
// 发送数据
n = write(sockfd, msg.c_str(), msg.size());
if (n < 0)
{
std::cout << "write fail" << std::endl;
continue;
}
char buf[1024] = {0};
// 接收数据
n = read(sockfd, buf, sizeof(buf));
if (n < 0)
{
std::cout << "read fail" << std::endl;
continue;
}
buf[n] = 0;
cout << buf << endl;
}
}
//main.cpp
#include "tcpServer.hpp"
#include <memory>
int main()
{
std::unique_ptr<tcpServer> server(new tcpServer);
server->init();
server->run();
return 0;
}