1.示例描述
按照第一篇文章的框架,使用相应的函数来编写一个基于TCP的socket示例,实现不同主机的通信,具体表现为:客户端主机运行程序,可以发送消息给服务器端;服务器端主机运行程序,能够接收到消息并打印出来,并且原封不动的将消息返还给客户端, 客户端能显示服务器端的响应。
下面分别从服务器端和客户端来实现socket程序。
2.服务器端
#include<iostream>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string>
#define BUFF_SIZE 1024
int main(){
//使用socket函数,创建套接字。对于TCP的服务器端,该套接字是服务器端的 看门狗
//PF_INET:IPv4协议簇 SOCK_STREAM:面向连接的
//IPv4协议簇下的SOCK_STREAM只有TCP协议,所以第三个参数可以填0
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
std::cout << "create socket error" << std::endl;
return -1;
}
//注册地址信息
sockaddr_in addr;//c++11 可以忽略struct关键字
addr.sin_family = AF_INET;//地址协议族:IPv4
//使用htonl将主机字节序转为网络字节序
addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY自动获取运行服务器端的主机IP地址
addr.sin_port = htons(8080);//使用htons函数,将端口号转为网络字节序
//使用bind函数,将地址绑定到套接字
if (bind(sockfd, (sockaddr*)&addr, sizeof(addr)) == -1)
{
std::cout << "bind socket error" << std::endl;
return -1;
}
//使用listen函数,开启套接字监听,队列长度为3
if (listen(sockfd, 3) == -1)
{
std::cout << "listen socket error" << std::endl;
return -1;
}
std::cout << "waiting for connecting..." << std::endl;
//注册一个新地址
sockaddr_in new_addr;
socklen_t new_len = sizeof(new_addr);
while (true)
{
//使用accept函数,将新地址注册到一个新的套接字上,该套接字才是用来通信的
int new_sockfd = accept(sockfd, (sockaddr*)&new_addr, &new_len);
if (new_sockfd == -1)
{
std::cout << "accept socket error" << std::endl;
return -1;
}
std::cout << "socket connect successfully..." << std::endl;
//使用read和write函数,实现数据交互
int size = 0;
char buff[BUFF_SIZE];//开辟缓冲区
//使用read函数,通过新的套接字从通道里面 读取数据到本地缓冲区
while ((size = read(new_sockfd, buff, BUFF_SIZE)) !=0 )
{
if (size == -1)
{
std::cout << "read socket error" << std::endl;
return -1;
}
std::cout << "收到来自客户端的消息: " << buff << std::endl;
//使用write函数,通过新的套接字将本地缓冲区的数据 写到通道里面
size = write(new_sockfd, buff, size);
if (size == -1)
{
std::cout << "write socket error" << std::endl;
return -1;
}
}
//客户端断开连接
std::cout << "connection exit..." << std::endl;
//使用close函数,关闭套接字
close(new_sockfd);
}
//关闭看门狗套接字
close(sockfd);
return 0;
}
3.客户端
#include<iostream>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<string>
#include<stdio.h>
#include<string.h>
//宏定义服务器端的IP地址
#define IP "192.168.120.100"
int main(){
//使用socket函数,创建套接字。对于TCP的客户端,该套接字是直接用来通信的
//PF_INET:IPv4协议簇 SOCK_STREAM:面向连接的
//IPv4协议簇下的SOCK_STREAM只有TCP协议,所以第三个参数可以填0
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
std::cout << "create socket error" << std::endl;
return -1;
}
//注册地址信息
sockaddr_in addr;//c++11 可以忽略struct关键字
addr.sin_family = AF_INET;//地址协议族:IPv4
//使用inet_addr,将点分十进制的IP地址字符串转为32位整型
addr.sin_addr.s_addr = inet_addr(IP);//服务器端的IP地址
addr.sin_port = htons(8080);//使用htons函数,转为网络字节序,与服务器端的端口号一致
//使用connect函数,使用要连接的服务器端地址信息通过套接字进行连接
if (connect(sockfd, (sockaddr*)&addr, sizeof(addr)) == -1)
{
std::cout << "connect socket error" << std::endl;
return -1;
}
std::cout << "connected..." << std::endl;
//数据交互
std::cout << "开始输入..." << std::endl;
while (true)
{
//使用C++风格的字符串输入
std::string msg;
std::cout << "(输入q结束连接): ";
getline(std::cin, msg);
if (msg == "q")
{
std::cout << "输入结束...." << std::endl;
break;
}
//使用write函数,通过套接字将本地缓冲区的数据 写到通道里面
int size = write(sockfd, msg.c_str(), sizeof(msg));
if (size == -1)
{
std::cout << "write socket error" << std::endl;
return -1;
}
int read_all_size = 0;
//读完写入大小的字节为止
char buff[BUFSIZ];//开辟接收缓冲区
while (read_all_size < size)
{
//使用read函数,通过套接字从通道里面 读取数据到本地缓冲区
int read_size = read(sockfd, &buff[read_all_size], BUFSIZ-1);
if (read_size == -1)
{
std::cout << "read socket error" << std::endl;
return -1;
}
read_all_size += read_size;
}
//添加终止符,使用C风格字符串
buff[read_all_size] = 0;
std::cout << "收到来自服务端的消息: " << buff <<std::endl;
}
//使用close函数,关闭套接字
close(sockfd);
return 0;
}