1.tcp特點
①傳輸層協議
②有連接
③可靠傳輸
④面向字節流
2.幾個常用接口
// 創建 socket 文件描述符 (TCP/UDP, 客戶端 + 服務器)
int socket(int domain, int type, int protocol);
// 綁定端口號 (TCP/UDP, 服務器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 開始監聽socket (TCP, 服務器)
int listen(int socket, int backlog);
// 接收請求 (TCP, 服務器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立連接 (TCP, 客戶端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
tcp通用服務端(.hpp)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
class TcpSvr
{
public:
TcpSvr()
{
_sock = -1;
}
~TcpSvr()
{
_sock = -1;
}
//創建套接字
bool CreateSock()
{
_sock = socket(AF_INET, SOCK_STREAM, 6);
if (_sock < 0) {
perror("create");
return false;
}
return true;
}
//綁定端口信息(都可以,建議客戶端不調用)
//客戶端調用後就寫死了,就只能與綁定端口進行通信
bool Bind(std::string& ip, uint16_t port)
{
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
ssize_t ret = bind(_sock, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
perror("bind");
return false;
}
return true;
}
//監聽(服務端)
bool Listen(int Backlog)
{
ssize_t ret = listen(_sock, Backlog);
if (ret < 0) {
perror("listen");
return false;
}
return true;
}
//接收請求(服務端)
bool Accept(TcpSvr& ts, struct sockaddr_in* addr = NULL)
{
struct sockaddr_in peeraddr;
socklen_t len = sizeof(struct sockaddr_in);
int ret = accept(_sock, (struct sockaddr*)&peeraddr, &len);
if (ret < 0) {
perror("accept");
return false;
}
ts._sock = ret;
if (addr != NULL){
memcpy(addr, &peeraddr, len);
}
return true;
}
//建立連接(客戶端調用)
bool Connect(std::string& ip, uint16_t port)
{
struct sockaddr_in destaddr;
destaddr.sin_family = AF_INET;
destaddr.sin_port = htons(port);
destaddr.sin_addr.s_addr = inet_addr(ip.c_str());
int ret = connect(_sock, (struct sockaddr*)&destaddr, sizeof(destaddr));
if (ret < 0) {
perror("connect");
return 0;
}
return true;
}
//發送數據
bool Send(std::string& buf)
{
int sendSize = send(_sock, buf.c_str(), buf.size(), 0);
if (sendSize < 0) {
perror("send");
return false;
}
return true;
}
//接收數據
bool Recv(std::string& buffer)
{
char buf[1024] = {0};
//0:阻塞接收
//MSG_PEEK:探測接收
int recvSize = recv(_sock, buf, sizeof(buf) - 1, 0);
if (recvSize < 0) {
perror("recv");
}
else if (recvSize == 0) {
//如果recvSize等於0,表示對端將連接關閉了
printf("connect error\n");
return false;
}
buffer.assign(buf, recvSize);
return true;
}
void Close()
{
close(_sock);
_sock = -1;
}
private:
int _sock;
};
服務端(.cpp)
#include "tcpsvr.hpp"
int main(int argc, char* argv[])
{
if (argc != 3) {
printf("./ser [ip] [port]");
return 0;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if (!ts.CreateSock()) {
return 0;
}
if (!ts.Bind(ip, port)) {
return 0;
}
if (!ts.Listen(5)) {
return 0;
}
//while (1) {
TcpSvr peerts;
if (!ts.Accept(peerts)) {
return 0;
}
while (1){
std::string buf;
peerts.Recv(buf);
printf("cli say: %s\n", buf.c_str());
printf("svr say: ");
fflush(stdout);
std::cin >> buf;
peerts.Send(buf);
}
peerts.Close();
return 0;
}
客戶端
#include "tcpsvr.hpp"
int main(int argc, char* argv[])
{
if (argc != 3){
printf ("./cli [ip] [port]");
return 0;
}
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if (!ts.CreateSock()) {
return 0;
}
if (!ts.Connect(ip, port)) {
return 0;
}
while (1) {
printf("cli say: ");
fflush(stdout);
std::string buf;
std::cin >> buf;
ts.Send(buf);
ts.Recv(buf);
printf ("svr say: %s\n", buf.c_str());
}
ts.Close();
return 0;
}
上面的服務端只能實現同時與單個客戶端進行通信,不能與多個客戶端通信,因爲他是單進程單線程的。
下面在服務端添加多進程,實現與多個客戶端同時通信
#include "tcpsvr.hpp"
#include <sys/wait.h>
#include <signal.h>
//回收子進程
void sigcd(int signo)
{
while (1) {
(void)signo;
int ret = waitpid(-1, NULL, WNOHANG);
if (ret == 0) {
break;
}
}
}
int main(int argc, char* argv[])
{
if (argc != 3) {
printf("./svr [ip] [port]\n");
return 0;
}
signal(SIGCHLD, sigcd);
std::string ip = argv[1];
uint16_t port = atoi(argv[2]);
TcpSvr ts;
if (!ts.CreateSock()) {
return 0;
}
if(!ts.Bind(ip, port)) {
return 0;
}
if (!ts.Listen(5)) {
return 0;
}
while (1) {
//接收連接
TcpSvr peerts;
struct sockaddr_in peeraddr;
if (!ts.Accept(peerts, &peeraddr))
{
continue;
}
printf("new connect: %s:%d\n", inet_ntoa(peeraddr.sin_addr), ntohs(peeraddr.sin_port));
//創建子進程
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
else if (pid == 0) {
//子進程服務
std::string buf;
while (1) {
peerts.Recv(buf);
printf("cli say: %s\n", buf.c_str());
printf("svr say:");
fflush(stdout);
std::cin >> buf;
peerts.Send(buf);
}
peerts.Close();
exit(1);
}
peerts.Close();
}
return 0;
}