TCP通信的基本思路是服务端监听一个端口,然后客户端连接这个端口(三次握手),建立连接后连接的信息保存在套接字(socket)中,使用send和recv进行数据的传输,因为TCP底层实现有错误校验,所以对比UDP方式传输稳定性,准确性较好,同时速度也较慢。
C/C++实现TCP服务端示例:
#include <stdio.h>
#ifdef WIN32
//Windows下使用的头文件
#include <WinSock2.h>
//引用静态库
#pragma comment(lib,"ws2_32.lib")
#else
//linux下的头文件
#include <sys/types.h>
#include <sys/socket.h>
#endif // WIN32
//线程函数
DWORD WINAPI ThreadFunc(void* param)
{
//连接套接字,创建线程的时候传进来的
SOCKET connSocket = (SOCKET)param;
char szRecvBuf[1024];
//先把接收缓冲区清零,防止客户端传过来的字符串没有结束符识别不出来
memset(szRecvBuf, 0, 1024);
//接收客户端传过来的数据,如果客户端一直没有传数据就会一直等待,返回值是接收的数据的字节数
int nRecv = recv(connSocket, szRecvBuf, 1024, 0);
if (nRecv <= 0)
{
//错误提示输出。。。
return -1;
}
printf("recv: %s\n", szRecvBuf);
char szSendBuf[1024];
sprintf_s(szSendBuf, "hello client!");
//像客户端发送数据
send(connSocket, szSendBuf, strlen(szSendBuf) + 1, 0);
//关闭连接
closesocket(connSocket);
return 0;
}
int main()
{
//初始化工作,固定写法
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
//AF_INET表示使用IPv4,SOCK_STREAM和IPPROTO_TCP表示使用TCP协议
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
//错误提示输出。。。
return -1;
}
//绑定监听地址和端口
sockaddr_in addrServ;
addrServ.sin_family = AF_INET;//表示使用IPv4
addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//表示监听本机所有网卡的IP地址
addrServ.sin_port = htons(6666);//监听6666端口
if (bind(s, (SOCKADDR*)&addrServ, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
//错误提示输出。。。
return -1;
}
//开始监听
if (listen(s, 5))
{
//错误提示输出。。。
return -1;
}
printf("服务端初始化完成!等待连接...\n");
//等待连接
sockaddr_in clientAddr;//用来保存连接上来的客户端的信息
int nLen = sizeof(SOCKADDR);
while (true)
{
//每当有一个客户端连接上来就会创建一个新的套接字
//这个套接字才是真正用来跟客户通信的
//前面的那个s只是负责监听而已
//如果不需要保存客户端的信息(IP,端口等)第二第三个参数可以直接填NULL
SOCKET connSocket = accept(s, (SOCKADDR*)&clientAddr, &nLen);
printf("客户端%s连接上了\n", inet_ntoa(clientAddr.sin_addr));
//为了在跟客户端1通信的时候其他客户端也能连接上来
//一般跟客户端的通信都是放在一个单独的线程里面
//如果不需要同时跟多个客户端通信那通信的代码也可以放在这里不用创建单独的线程
//这里演示创建线程的方法
//第三个参数是线程函数的函数名
//第四个参数是线程函数是入参,把连接套接字穿进去我们在线程函数里面就能拿到了
CreateThread(NULL, 0, ThreadFunc, (void*)connSocket, 0, NULL);
}
}
客户端示例:
#include <stdio.h>
#ifdef WIN32
//Windows下使用的头文件
#include <WinSock2.h>
//引用静态库
#pragma comment(lib,"ws2_32.lib")
#else
//linux下的头文件
#include <sys/types.h>
#include <sys/socket.h>
#endif // WIN32
int main()
{
//初始化工作,固定写法
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
//创建套接字
//AF_INET表示使用IPv4,SOCK_STREAM和IPPROTO_TCP表示使用TCP协议
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
//错误提示输出。。。
return -1;
}
//设置服务端的地址和端口
sockaddr_in addrServ;
addrServ.sin_family = AF_INET;//表示使用IPv4
addrServ.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//服务端地址
addrServ.sin_port = htons(6666);//连接6666端口
//连接
if (connect(s, (SOCKADDR*)&addrServ, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
//错误信息输出...
return 0;
}
char szBuf[1024];
sprintf_s(szBuf, "hello server!");
//发送
send(s, szBuf, strlen(szBuf) + 1, 0);
memset(szBuf, 0, 1024);
//接收
int nRecv = recv(s, szBuf, 1024, 0);
printf("recv: %s\n", szBuf);
closesocket(s);
return 0;
}
其他
TCP协议很多语言都支持,不是C++写的客户端也能连
例如Python:
#!/usr/bin/python3
# 文件名:client.py
# 导入 socket、sys 模块
import socket
import sys
# 创建 socket 对象
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务,指定主机和端口
s.connect(("127.0.0.1", 6666))
msg = "hello server!"
s.send(msg.encode("GBK"))
# 接收小于 1024 字节的数据
msg = s.recv(1024)
s.close()
print (msg.decode('GBK'))