C语言TCP编程流程
C语言TCP编程流程
服务器:
创建套接字socket()
将套接字与服务器网络信息结构体绑定bind()
将套接字设置为监听状态listen()
阻塞等待客户端的连接请求accept()
进行通信recv()/send()
关闭套接字close()
客户端:
创建套接字socket()
发送客户端连接请求connect()
进行通信send()/recv()
关闭套接字close()
一、创建TCP套接字
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
int main()
{
//通过socket函数创建一个TCP套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
printf("sockefd = %d\n",sockfd);
}
二、TCP客户端-connect、send、recv
connect函数
int connect(int sockfd,const struct sockaddr*addr,socklen_t len);
功能
- 主动跟服务器建立连接
参数
- sockfd: 套接字
- addr: 连接的服务器地址结构
- len:地址结构体长度
返回值
- 成功:0
- 失败:其他
注意:
- connect建立连接之后不会产生新的套接字
- 连接成功之后才可以传输TCP数据
- 头文件:#include <sys/socket.h>
send函数
ssize_t send(int sockfd,const void*buf,size_t nbytes,int flags);
功能
- 用于发送数据
参数
- sockfd: 已建立连接的套接字
- buf: 发送数据的地址
- nbytes:发送缓冲区数据的大小(以字节为单位)
- flags:套接字标志(常为0)
返回值
- 成功:返回发送的字节数
- 失败:其他
头文件
-
include <sys/socket.h>
注意:
- 不能用TCP协议发送0长度的数据包
recv函数
ssize_t recv(int sickfd,void*buf,size_t nbytes,int flags);
功能
- 用于接收网络数据
参数
- sockfd:套接字
- buf:接收网络数据缓冲区的地址
- nbytes:接收缓冲区的大小(以字节为单位)
- flags:套接字标志(0表示阻塞,MSG_DONTWAIT表示非阻塞)
返回值
- 成功:返回接收到的套接字
- 失败:-1
- 如果发送端关闭文件描述符或者关闭进程,则recv函数会返回0
头文件
-
include <sys/socket.h>
client实例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define N 128
int main(int argc,char *argv[])
{
if(argc < 3)
{
fprintf(stderr,"Usage:%s [ip] [port]\n",argv[0]);
exit(1);
}
//第一步:通过socket函数创建一个TCP套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
printf("sockefd = %d\n",sockfd);
//第二步:发送客户端连接请求
struct sockaddr_in serveraddr;
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
socklen_t addr_len = sizeof(serveraddr);
if(connect(sockfd,(struct sockaddr*)&serveraddr,addr_len) == -1)
{
perror("fail to connect");
exit(1);
}
while(1)
{
//第三步:进行通信
//发送数据
char buf[N] = "";
fgets(buf,N,stdin);
buf[strlen(buf) -1] = '\0';
if(send(sockfd,buf,N,0) == -1)
{
perror("fail to send");
exit(1);
}
//接收数据
char text[N] = "";
if(recv(sockfd,text,N,0) == -1)
{
perror("fail to recv");
exit(1);
}
printf("from server:%s\n",text);
}
//第四步:关闭套接字文件描述符
close(sockfd);
}
三、TCP服务器-bind、listen、accept
服务器需要具备条件
- 具备一个可以确知的地址
- 让操作系统知道是一个服务器,而不是客户端
- 等待连接的到来
对于面向连接的TCP协议来说,连接的建立才真正意味着数据通信的开始
bind()函数的使用
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen);
功能:
- 将本地协议地址与sockfd绑定
参数
- sockfd: 套接字
- my_addr: 指向特定协议的地址结构指针
- addrlen: 该地址结构的长度
返回值
- 成功: 返回0
- 失败: -1
listen函数
int listen(int sockfd,int backlog);
功能
- 将套接字主动修改为被动
- 使操作系统为该套接字设置一个连接队列,用来记录所有连接到该套接字的连接
参数
- sockfd:监听套接字
- backlog:能同时服务器的套接字连接队列长度
返回值
- 成功:返回0
- 失败:-1
头文件
-
include <sys/socket.h>
accept函数
int accept(int sockfd,struct sockaddr* cliaddr,socklent_t *addrlen);
功能
- 从已连接的队列中取出一个已经建立的连接,如果没有任何连接作用,则进入睡眠等待(阻塞)
参数
- sockfd:socket监听套接字
- cliaddr:用于存放客户端套接字地址结构
- addrlen:套接字地址结构体长度的地址
返回值
- 成功:返沪已连接的套接字(只要有客户端连接,就会产生新的文件描述符,这个新的文件描述符专门与指定的客户端通信的)
- 失败:-1
注意
- 返回的是已连接的套接字,这个套接字代表当前这个连接
server示例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define N 128
int main(int argc,char *argv[])
{
if(argc < 3)
{
fprintf(stderr,"Usage:%s [ip] [port]\n",argv[0]);
exit(0);
}
//第一步:创建套接字
int sockfd;
if((sockfd = socket(AF_INET,SOCK_STREAM,0)) == -1)
{
perror("fail to socket");
exit(1);
}
//第二部:将套接字与服务器网络信息结构绑定
struct sockaddr_in serveraddr;
socklen_t addrlen = sizeof(serveraddr);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
serveraddr.sin_port = htons(atoi(argv[2]));
if(bind(sockfd,(struct sockaddr*)&serveraddr,addrlen) == -1)
{
perror("fail to bind");
exit(1);
}
//第三步:将套接字设置为被动监听状态
if(listen(sockfd,10) == -1)
{
perror("fail to listen");
exit(1);
}
//第四步:阻塞等待客户端的连接请求
int acceptfd;
struct sockaddr_in clientaddr;
if((acceptfd = accept(sockfd,(struct sockaddr*)&clientaddr,&addrlen)) == -1)
{
perror("fail to accept");
exit(1);
}
//打印连接的客户端信息
printf("ip:%s,port:%d\n",inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
//第五步:进行通信
//tcp服务器与客户端通信时,需要使用accept函数的返回值
char buf[N] = "";
if(recv(acceptfd,buf,N,0) == -1)
{
perror("fail to recv");
exit(1);
}
printf("from client:%s\n",buf);
strcat(buf,"*_*");
if(send(acceptfd,buf,N,0) == -1)
{
perror("fail to send");
exit(1);
}
close(acceptfd);
close(sockfd);
return 0;
}