int …int connect(int socket, struct sockaddr* address, int addr_len);
connect: 向服务器发起连接请求
数据传输 建立连接后,应用程序可以调用以下操作发送和接收数据:
int send(int socket, char *message, int msg_len, int flags);
int recv(int socket, char *message, int *buffer, int buf_len, int flags);
send: 发送数据recv: 接收数据
关闭套接字 最后需要关闭套接字以释放资源:
int close(int socket);
环境准备
在 macOS 系统下进行 TCP 套接字编程,需要使用 C++ 编译器(如 g++)和系统提供的网络编程头文件。主要包含以下头文件:
sys/socket.h:套接字相关函数netinet/in.h:网络地址结构定义arpa/inet.h:IP 地址转换函数unistd.h:系统调用函数stdio.h:标准输入输出函数string.h:字符串处理函数
服务器端代码
服务器端程序负责监听客户端连接请求,并与客户端进行数据交互。
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
#define SERVER_PORT 5050 //端口号
#define SERVER_IP "127.0.0.1" //服务器 ip
#define QUEUE_SIZE 5 //所监听端口队列大小
int main(int argc, char *argv[])
{
//创建一个套接字,并检测是否创建成功
int sockSer;
sockSer = socket(AF_INET, SOCK_STREAM, 0);
if(sockSer == -1){
perror("socket");
}
//设置端口可以重用,可以多个客户端连接同一个端口,并检测是否设置成功
int yes = 1;
if(setsockopt(sockSer, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1){
perror("setsockopt");
}
struct sockaddr_in addrSer,addrCli; //创建一个记录地址信息的结构体
addrSer.sin_family = AF_INET; //所使用 AF_INET 协议族
addrSer.sin_port = htons(SERVER_PORT); //设置地址结构体中的端口号
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //设置其中的服务器 ip
//将套接字地址与所创建的套接字号联系起来。并检测是否绑定成功
socklen_t addrlen = sizeof(struct sockaddr);
int res = ::bind(sockSer,(struct sockaddr*)&addrSer,addrlen);
if(res == -1)
perror("bind");
listen(sockSer, QUEUE_SIZE); //监听端口队列是否由连接请求,如果有就将该端口设置位可连接状态,等待服务器接收连接
printf("Server Wait Client Accept......\n");
//如果监听到有连接请求接受连接请求。并检测是否连接成功,成功返回 0,否则返回 -1
int sockConn = accept(sockSer, (struct sockaddr*)&addrCli, &addrlen);
if(sockConn == -1)
perror("accept");
else
{
printf("Server Accept Client OK.\n");
printf("Client IP:> %s\n", inet_ntoa(addrCli.sin_addr));
printf("Client Port:> %d\n",ntohs(addrCli.sin_port));
}
char sendbuf[256]; //申请一个发送缓存区
char recvbuf[256]; //申请一个接收缓存区
while(1)
{
// 先显示服务器提示符,再等待用户输入
printf("Ser:>");
// 使用 fgets 读取整行输入,包括空格
if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) {
printf("读取输入失败\n");
break;
}
// 移除换行符
sendbuf[strcspn(sendbuf, "\n")] = 0;
if(strncmp(sendbuf,"quit",4) == 0) //如果所要发送的数据为"quit",则直接退出。
break;
// 发送数据给客户端
if (send(sockConn, sendbuf, strlen(sendbuf)+1, 0) <= 0) {
printf("发送数据失败\n");
break;
}
// 接收客户端发送的数据
memset(recvbuf, 0, sizeof(recvbuf));
int recv_len = recv(sockConn, recvbuf, sizeof(recvbuf)-1, 0);
if (recv_len > 0) {
printf("Cli:> %s\n",recvbuf);
} else if (recv_len == 0) {
printf("客户端已断开连接\n");
break;
} else {
perror("recv");
break;
}
}
close(sockSer); //关闭套接字
return 0;
}
客户端代码
客户端程序负责连接到服务器,并与服务器进行数据交互。
#include<iostream>
#include<unistd.h>
#include<stdio.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
#define SERVER_PORT 5050
#define SERVER_IP "127.0.0.1"
int main(int argc, char *argv[])
{
//创建客户端套接字号,并检测是否创建成功
int sockCli;
sockCli = socket(AF_INET, SOCK_STREAM, 0);
if(sockCli == -1)
perror("socket");
//创建一个地址信息结构体,并对其内容进行设置
struct sockaddr_in addrSer,addrCli;
addrSer.sin_family = AF_INET; //使用 AF_INET 协议族
addrSer.sin_port = htons(SERVER_PORT); //设置端口号
addrSer.sin_addr.s_addr = inet_addr(SERVER_IP); //设置服务器 ip
bind(sockCli,(struct sockaddr*)&addrCli, sizeof(struct sockaddr)); //将套接字地址与所创建的套接字号联系起来
//创建一个与服务器的连接,并检测连接是否成功
socklen_t addrlen = sizeof(struct sockaddr);
int res = connect(sockCli,(struct sockaddr*)&addrSer, addrlen);
if(res == -1)
perror("connect");
else
printf("Client Connect Server OK.\n");
char sendbuf[256]; //申请一个发送数据缓存区
char recvbuf[256]; //申请一个接收数据缓存区
while(1)
{
// 先显示客户端提示符,再等待用户输入
printf("Cli:>");
// 使用 fgets 读取整行输入,包括空格
if (fgets(sendbuf, sizeof(sendbuf), stdin) == NULL) {
printf("读取输入失败\n");
break;
}
// 移除换行符
sendbuf[strcspn(sendbuf, "\n")] = 0;
if(strncmp(sendbuf,"quit", 4) == 0) //如果客户端发送的数据为"quit",则退出。
break;
send(sockCli, sendbuf, strlen(sendbuf)+1, 0); //发送数据
// 接收来自服务器的数据
memset(recvbuf, 0, sizeof(recvbuf));
int recv_len = recv(sockCli, recvbuf, sizeof(recvbuf)-1, 0);
if (recv_len > 0) {
printf("Ser:> %s\n",recvbuf);
} else if (recv_len == 0) {
printf("服务器已断开连接\n");
break;
} else {
perror("recv");
break;
}
}
close(sockCli); //关闭套接字
return 0;
}
编译和运行
在 macOS 系统下,可以使用以下命令编译这两个程序:
g++ -o TcpServer Tcpserver.cpp
g++ -o TcpClient TcpClient.cpp
然后分别运行服务器和客户端程序:
./TcpServer
./TcpClient
运行 ./TcpServer
Documents/tmp$ ./TcpServer
Server Wait Client Accept......
Server Accept Client OK.
Client IP:> 127.0.0.1
Client Port:> 54109
Ser:>
运行 ./TcpClient
:~/Documents/tmp$ ./TcpClient
Client Connect Server OK.
Cli:>
然后就可以进行简单的交互了:
收起