TCP网络通信流程:
1.创建套接字(socket) — 买手机
2.为套接字绑定地址信息(bind) — 插卡
3.服务端开始监听(listen) — 待机
4.客户端请求连接(connect) — 打电话
5.服务端获取连接请求(accept) — 接电话
6.发送数据(send)
7.接收数据(recv)
8.关闭套接字(close) — 先挂电话后关机
头文件:
#include <iostream>
#include <string>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
using std::cout;
using std::endl;
using std::cin;
using std::string;
inline void CHECK(bool operation) {
if (operation == false) {
exit(-1);
}
}
class TcpSocket {
public:
TcpSocket() : _sock(-1) {}
void setNewSockFd(int newsockfd) {
_sock = newsockfd;
}
// 创建套接字
bool Socket() {
_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sock < 0) {
perror("socket error");
return false;
}
return true;
}
// 为套接字绑定地址信息
bool Bind(string& ip, uint16_t& port) const {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
size_t len = sizeof(struct sockaddr_in);
int binding = bind(_sock, (struct sockaddr*)&addr, len);
if (binding < 0) {
perror("bind error");
return false;
}
return true;
}
// 服务端监听
bool Listen(int backlog = 5) const {
int listening = listen(_sock, backlog);
if (listening < 0) {
perror("listen error");
return false;
}
return true;
}
// 客户端请求连接
bool Connect(string& ip, uint16_t& port) const {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
size_t len = sizeof(struct sockaddr_in);
int connecting = connect(_sock, (struct sockaddr*)&addr, len);
if (connecting < 0) {
perror("connect error");
return false;
}
return true;
}
// 服务端接受客户端请求
bool Accept(TcpSocket& cli_sock, struct sockaddr_in* cli_addr = NULL) {
TcpSocket clisock;
size_t len = sizeof(struct sockaddr_in);
int newsockfd = accept(_sock, (struct sockaddr*)&clisock, &len);
if (newsockfd < 0) {
perror("accept error");
return false;
}
if (cli_addr != NULL) {
memcpy(cli_addr, &clisock, len);
cli_sock.setNewSockFd(newsockfd);
}
return true;
}
// 发送数据
bool Send(string& buf) {
size_t size = send(_sock, buf.c_str(), buf.size(), 0);
if (size < 0) {
perror("send error");
return false;
}
return true;
}
// 接收数据
bool Recv(string& buf) {
char buf_tmp[4096] = {0};
size_t size = recv(_sock, buf_tmp, sizeof(buf_tmp), 0);
if (size < 0) {
perror("recv error");
return false;
} else if (size == 0) {
perror("peer shutdown");
return false;
}
buf.assign(buf_tmp, size);
return true;
}
void Close() {
close(_sock);
_sock = -1;
}
private:
int _sock;
};
服务端:
#include "tcpsocket.hpp"
int main(int argc, char* argv[]) {
if (argc != 3) {
perror("./main.out ip port");
return -1;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSocket sock;
CHECK(sock.Socket());
CHECK(sock.Bind(ip, port));
CHECK(sock.Listen());
TcpSocket clisock;
struct sockaddr_in cliaddr;
CHECK(sock.Accept(clisock, &cliaddr));
while (1) {
string buf;
CHECK(clisock.Recv(buf));
cout << "client say:" << buf << endl;
buf.clear();
cout << "server say:";
fflush(stdout);
getline(cin, buf);
CHECK(clisock.Send(buf));
}
sock.Close();
return 0;
}
客户端:
#include "tcpsocket.hpp"
int main(int argc, char* argv[]) {
if (argc != 3) {
perror("./main.out ip port");
return -1;
}
string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSocket sock;
CHECK(sock.Socket());
CHECK(sock.Connect(ip, port));
while (1) {
string buf;
cout << "client say:";
fflush(stdout);
getline(cin, buf);
CHECK(sock.Send(buf));
buf.clear();
CHECK(sock.Recv(buf));
cout << "server say:" << buf << endl;
}
sock.Close();
return 0;
}
效果图:
总结
首先服务端和客户端创建套接字, 建立双方通信的两端, 服务端绑定地址信息而客户端无需手动绑定让系统分配一个合理的地址信息即可; 然后服务端开始监听, 告诉操作系统可以接收客户端的连接请求了, 客户端发送连接请求后在未完成连接队列中等待, 服务端准备接受客户端的连接请求, 建立成功后会创建一个新的套接字信息与指定客户端进行通信.