https://blog.csdn.net/github_39611196/article/details/107702377
本文主要介绍在Linux平台基于C++ socket实现TCP网络通信。
0初步构思
初步的构思是在Linux平台实现一个客户端和一个服务端,首先客户端和服务端建立TCP连接,然后客户端和服务端可以互发消息。
1、通信流程
2、服务端代码
#include<iostream>
#include<conio.h> //kbhit()所在的库
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>//我这里close函数需要此头文件
#include<arpa/inet.h>
using namespace std;
int main()
{
int server_sockfd;//服务端套接字
int client_sockfd;//客户端套接字
int len;
struct sockaddr_in my_addr;//服务器网络地址
struct sockaddr_in remote_addr;//客户端网络地址
socklen_t sin_size;//这里linux的类型是socklen_t
char recv_buf[BUFSIZ];//数据接收缓冲区
char send_buf[BUFSIZ];//数据传输缓冲区
memset(&my_addr,0,sizeof(my_addr)); //数据初始化--清零
my_addr.sin_family=AF_INET; //设置为IP通信
my_addr.sin_addr.s_addr=INADDR_ANY;//服务器IP地址--允许连接到所有本地地址上
my_addr.sin_port=htons(8000); //服务器端口号
//创建服务器端套接字--IPv4协议,面向连接通信,TCP协议
if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0)
{
cout<<"socket error";
return 1;
}
//套接字绑定在服务器的网络地址下
if(bind(server_sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0)
{
cout<<"bind error";
return 1;
}
//监听连接请求 监听队列长度5
if(listen(server_sockfd,5)<0)
{
cout<<"listen error";
return 1;
}
sin_size=sizeof(struct sockaddr_in);
//等待客户端连接请求
if((client_sockfd=accept(server_sockfd,(struct sockaddr *)&(remote_addr),&sin_size))<0)
{
cout<<"accept error";
return 1;
}
//连接成功 收到地址
cout<<"accept client"<<inet_ntoa(remote_addr.sin_addr)<<endl;
len=send(client_sockfd,"Welcome to server",21,0);
//接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,send返回发送的字节数
while(1)
{
//接收客户端输入
len=recv(client_sockfd,recv_buf,BUFSIZ,0);
if(len > 0){
recv_buf[len]='\0';
cout<<"Received:"<<recv_buf<<" ,Info Length:"<<len<<endl;
}
//发送消息给客户
cout<<"Enter string to send:";
cin>>send_buf;
if(!strcmp(send_buf, "quit")) break;
len=send(client_sockfd, send_buf, strlen(send_buf), 0);
}
}
//关闭套接字
close(server_sockfd);
close(client_sockfd);
return 0;
}
3、客户端代码
#include<iostream>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
using namespace std;
int main()
{
int client_sockfd;
int len;
struct sockaddr_in remote_addr;
char recv_buf[BUFSIZ];//数据接收缓冲区
char send_buf[BUFSIZ];//数据传输缓冲区
memset(&remote_addr, 0, sizeof(remote_addr));//数据初始化--清零
remote_addr.sin_family=AF_INET;//设置为IP通信
remote_addr.sin_addr.s_addr=inet_addr("127.0.0.1");//服务器IP地址
remote_addr.sin_port=htons(8000);//服务器端口号
//创建客户端套接字 IPv4 tcp
if((client_sockfd=socket(PF_INET, SOCK_STREAM, 0))<0)
{
cout<<"socket error";
return 1;
}
//绑定服务器网络地址
if(connect(client_sockfd, (struct sockaddr*)&remote_addr, sizeof(struct sockaddr))<0)
{
cout<<"connect error";
return 1;
}
cout<<"connected to server"<<endl;;
len=recv(client_sockfd, recv_buf, BUFSIZ, 0);//接受服务端消息
recv_buf[len] = '\0';
//循环的发送接受信息并打印接受信息(可以按需发送)--struct sockaddr))<0
while(1)
{
//发送消息给服务端
cout<<"Enter string to send:";
cin>>send_buf;
if(!strcmp(send_buf, "quit")) break;
len=send(client_sockfd, send_buf, strlen(send_buf), 0);
//从客户端接受消息
len=recv(client_sockfd,recv_buf,BUFSIZ,0);
if(len > 0){
recv_buf[len]='\0';
cout<<"Received:"<<recv_buf<<" ,Info Length:"<<len<<endl;
}
}
close(client_sockfd);
return 0;
}
4、优化方向
将输入设置为无阻塞的,即可以连续接收和发送。
注:在退出连接后,如何解除端口绑定?
1.查看800端口是否被占用:
netstat -anp | grep 8000
输出结果:tcp 4 0 127.0.0.1:41766 127.0.0.1:8000 CLOSE_WAIT 2972/./client
由上可知8000端口已经被占用,占用进程号为2972。
2、终止该进程
kill 2972
注意:
C++ Socket 端口被占用解决方法:
windows:
BOOL bReuseaddr=TRUE;
setsockopt (s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));
Linux
int opt = 1;
setsockopt(LisSockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof( opt ));
将套接字属性设置为 SO_REUSEADDR (允许套接口和一个已在使用中的地址捆绑),可以解决绑定失败问题!