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;
}
client客户端与server服务器运行结果图

                    
                
                
            
        
浙公网安备 33010602011771号