TCP客户/服务器模型
服务器端:
/*
回射客户/服务器应用程序
功能:客户端输入字符,发送给服务器,服务器不对该字符串做任何处理,又反回客户端
*/
//服务器端函数
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
int main()
{
int listenfd;
int run;
//首先调用socket()函数创建套接字
/*
socket()函数
包含头文件:<sys/socket.h>
功能:创建一个套接字用于通信
原型:int socket (int domain , int type, int protocol);
参数:
domain: 指定通信协议族
type: 指定socket类型,流式套接字SOCK_STREAM,数据报套接字SOCK_DGRAM,原始套接字SOCK_RAW
protocol: 协议类型
返回值:成功反回非负数,称为套接字
失败返回-1,
被动套接字:接受连接accept()
主动套接字(默认):发起连接 connect()
*/
listenfd =socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); //在LINUX 下可以用man socket 查询该函数的具体描述
if(listenfd < 0)
{
printf("ERROR\n");
return -1;
}
struct sockaddr_in servaddr; //IPV4的地址结构
memset(&servaddr,0,sizeof(servaddr));
// 初始化地址
servaddr.sin_family = AF_INET; //地址的家族
servaddr.sin_port = htons(5188); //端口号,2个字节,这里需要的是网络字节序(大端),需要将5188转换为网络字节序
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); //设置地址,INADDR_ANY表示本地的任意地址
//servaddr.sin_addr.s_addr =inet_addr("127.0.0.1"); //显示指定地址,inet_addr()将点分式地址转换为10进制
// inet_aton("127.0.0.1",&servaddr.sin_addr);
//第二步为套接字绑定一个本地地址
/*
bind()
包含头文件<sys/socket.h>
功能:绑定一个本地地址到套接字
原型:int bind(int sockfd ,const struct sockaddr *addr, socklen_t);
参数: sockfd :socket()函数返回的套接字
addr: 要绑定的地址,(接受的为一个通用地址,使用时可能需要强制类型转换)
addrlen: 要绑定的地址长度,可以用sizeof()计算
返回值:成功返回0,失败返回-1
*/
run=bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(run < 0)
{
printf("error\n");
return -2;
}
//第三步:使套接字处于监听状态
/*
listen()
包含头文件;<sys/socket.h>
功能:使套接字从close状态,设置为监听状态,转换为监听状态才能接受连接
原型: int listen(int sockfd, int backlog);
参数:
sockfd: socket()函数返回的套接字
backlog: 设置内核为此套接字排队的最大连接个数
返回值:成功返回0,失败返回-1
一般来说,listen函数应该在调用socket和bind函数之后,在调用accept函数之前,将套接字由主动变为被动套接字
对于给定的监听套接口,内核要维护两个队列:
1.已由客户发出并送达到服务器,服务器正在等待完成相应的TCP三次握手过程
2. 已完成连接的队列
两个队列之和不超过backlog
*/
run = listen(listenfd,SOMAXCONN);
if(run < 0)
{
printf("error\n");
return -3;
}
struct sockaddr_in peeraddr; //定义对方的地址
socklen_t peerlen =sizeof(peeraddr); //定义对方的地址长度大小,注意需要有初始值,负责 accept()会失败
int conn;
//第四步:从已完成连接队列中返回第一个连接,如果没有连接过来一直处于阻塞状态
/*
头文件:<sys/socket.h>
功能: 从已完成连接队列中返回第一个连接,如果已完成队列为空,则阻塞
原型: int accept(int sockfd , struct sockaddr * addr , socklen_t *addrlen);
参数: sockfd:服务器套接字
addr :返回对等方的套接字地址
addrlen:返回对等方的套接字地址长度
返回值:成功返回非负正数(新得到的套接字,将这个套接字称为已连接套接字),失败返回-1
*/
conn = accept(listenfd,(struct sockaddr *)&peeraddr,&peerlen);
if(conn < 0)
{
printf("error\n");
return -4;
}
char recvbuf[1024];
//实现回射通信
while(1)
{
memset(recvbuf,0,sizeof(recvbuf));
int ret=0;
//接受客户端的请求
ret = read(conn,recvbuf,sizeof(recvbuf)); //返回实际接受到的字节数
//对请求进行处理
printf("%s",recvbuf);
//对客户端进行数据应答
write(conn,recvbuf,ret);
}
//关闭套接口
close(conn);
close(listenfd);
return 0;
}
客户端:
#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
int main()
{
int sock;
int error=0;
//第一步 创建客户端套接字
sock=socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sock < 0)
{
printf("error socket();\n");
return -1;
}
//设置连接地址
struct sockaddr_in client;
memset(&client,0,sizeof(client));
client.sin_family=AF_INET;
client.sin_port=htons(5188);
client.sin_addr.s_addr =inet_addr("127.0.0.1");
//客户端发起连接
error=connect(sock,(struct sockaddr *)&client,sizeof(client));
if(error<0)
{
printf("error connect();%d\n",error);
return -2;
}
char sendbuff[1024];
char recvbuff[1024];
while(fgets(sendbuff,sizeof(sendbuff),stdin)!=NULL )
{
//传送数据
write(sock,sendbuff,strlen(sendbuff));
//读取返回数据
read(sock,recvbuff,sizeof(recvbuff));
memset(sendbuff,0,sizeof(sendbuff));
fputs(recvbuff,stdout);
memset(recvbuff,0,sizeof(recvbuff));
}
//关闭套接口
close(sock);
return 0;
}