套接字编程:基于TCP协议的通信编程
TCP协议
- 传输控制协议(TCP协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
服务端server的工作
- 创建套接字:在内核中创建socket结构体
- 套接字绑定地址:告诉操作系统发往哪个端口和地址的数据应该交给当前套接字进行处理,以及发送数据的时候指定源端地址信息。
- 开始监听:开始受理客户端的连接请求
- 获取新建连接:获取客户端连接请求后,产生一个新的套接字,用于与该客户端通信。
- 收发数据:使用这个获取的新建连接,与指定的客户端进行数据通信发送数据 ,接收数据
- 关闭套接字
客户端client的工作
- 创建套接字
- 为套接字绑定地址信息(不推荐)
- 向服务端发起连接请求
- 连接建立成功后,与服务端进行数据通信发送数据,接收数据
- 关闭套接字
实现中存在的问题
- 为了能够在通过tcp协议进行通信时能够多次获取到不同客户端的连接,则需要把获取新连接的接口也放在循环体里。
- 导致的问题就是,每当服务端获取到一个客户端连接后,会通过新产生的套接字给该客户端进行一次数据的收发。
- 在此之后当再次进入循环体里时,则又会进入到获取新连接的过程,则之前的客户端发送的数据,不再会被服务端接收,服务端会与下一个新建立连接的客户端进行下一次通信,反复循环。
- 这也就导致每一个客户端与服务端之间只能进行一次通信,局限性太大。
tcp_server.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>//地址结构
#include<arpa/inet.h>//字节序转换接口
#include<sys/socket.h>//套接字接口
#define MAX_LISTEN_NUM 5
int main()
{
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0){
perror("socket error");
return -1;
}
//2.绑定地址信息
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(9000);
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
socklen_t len=sizeof(addr);
int ret = bind(sockfd,(struct sockaddr*)&addr,len);
if(ret<0){
perror("bind error");
return -1;
}
//3.开始监听
ret =listen(sockfd,MAX_LISTEN_NUM);
if(ret<0){
perror("listen error");
return -1;
}
//4.获取新连接
while(1){
struct sockaddr_in cliaddr;
int newfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len);
if(newfd<0){
perror("accept error");
continue;
}
//5.使用新连接与客户端通信(收发数据)
char buf[1024]={0};
//接收数据
ret=recv(newfd,buf,1023,0);
if(ret<0){
perror("recv error");
close(newfd);
continue;
}else if(ret==0){//返回值为0,表示该客户端已断开连接
printf("peer shutdown\n");
close(newfd);
continue;
}
printf("%s:%d say:%s\n",inet_ntoa(cliaddr.sin_addr),ntohs(cliaddr.sin_port),buf);
//发送数据
memset(buf,0x00,1024);
printf("server say:");
fflush(stdout);
scanf("%s",buf);
ret=send(newfd,buf,strlen(buf),0);
if(ret<0){
perror("send error");
close(newfd);
continue;
}
}
//6.关闭套接字
close(sockfd);
return 0;
}
tcp_client.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>//地址结构
#include<arpa/inet.h>//字节序转换接口
#include<sys/socket.h>//套接字接口
int main()
{
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0){
perror("socket error");
return -1;
}
//2.绑定地址信息(不推荐)
//3.向服务端发起连接请求
struct sockaddr_in srv_addr;
srv_addr.sin_family=AF_INET;
srv_addr.sin_port=htons(9000);
srv_addr.sin_addr.s_addr=inet_addr("192.168.204.144");
socklen_t len=sizeof(srv_addr);
int ret=connect(sockfd,(struct sockaddr*)&srv_addr,len);
if(ret<0){
perror("connect error");
return -1;
}
//4.收发数据
while(1){
//发送数据
printf("client say:");
fflush(stdout);
char buf[1024]={0};
scanf("%s",buf);
ret=send(sockfd,buf,strlen(buf),0);
if(ret<0){
perror("send error");
return -1;
}
//接收数据
memset(buf,0x00,1024);
ret=recv(sockfd,buf,1023,0);
if(ret<0){
perror("recv errror");
return -1;
}else if (ret==0){
printf("peer shutdown");
return -1;
}
printf("server say:%s\n",buf);
}
//5.关闭套接字
close(sockfd);
return 0;
}
通信演示