TCP网络编程基础

1.套接字编程基础知识

1.1套接字地址结构

1.通用套接字数据结构

struct sockaddr{
    
                                  //套接字地址结构
	sa_family sa_family;                      //协议族
	char      sa_data[14];                    //协议族数据
}

1.2实际使用的套接字数据结构

在网络程序设计中所使用的函数中几乎所有套接字函数都用struct sockaddr结构作为参数

int bind(int sockfd,                        //套接字文件描述符
const struct sockaddr *my_addr,             //套接字地址结构
socklen_t addrlen);                         //套接字地址结构长度

但由于结构struct sockaddr不方便进行设置,因此,在以太网中,一般采用struct sockaddr_in进行设置

struct sockaddr_in{
    
                    //以太网套接字地址结构
	u8	           sin_len;        //结构的长度,16
	u8	           sin_family;     //通常为AF_INET
	u16            sin_port;       //16位的端口号,网络字节序
	struct in_addr sin_addr;       //IP地址32位
	char           sin_zero[8];    //未用
}

调用时需强转成sockaddr

struct sockaddr_in addr;
bind(fd,(struct sockaddr *)&addr, size);

2.TCP网络编程流程

在这里插入图片描述

2.2 创建网络插口函数

  1. 函数介绍
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);  
                                                  成功:新套接字所对应的文件描述符,失败-1 errno

domain:用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族

AF_INET :IPv4协议
AF_INET6 :IPv6协议
AF_UNIX :本地通信

type:用于设置套接字通信的类型

SOCK_STREAM:流式套接字(TCP)
SOCK_DGRAM:数据包套接字(UDP)

protocol:用于指定某个协议的特定类型,通常某个协议只有一种特定类型,因此protocol仅能设置为0

2.3 绑定一个地址端口对bind()函数

  1. 函数介绍
    bind()函数将长度为addlen()的struct sockadd类型的参数my_addr与sockfd绑定一起,将sockfd绑定到某个端口上,如果使用connect()函数则没有绑定的必要。
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);   
                                                                       成功:0,失败-1 errno

sockfd是用socket()函数创建的文件描述符
my_addr是指向一个结构为sockaddr参数的指针

扫描二维码关注公众号,回复: 13300266 查看本文章
struct sockaddr_in addr;
addr.sin_family = AF_INET
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = hton1(INADDR_ANY);
my_addr : (struct sockaddr *)&addr   //需要强制转换

addrlen是my_addr结构的长度,可以设置成sizeof(addr)

2.4监听本地端口listen()

函数listen()用来初始化服务器可连接队列。设置监听上限,服务器处理客户端连接请求的时候是顺序处理的,同一时间仅能处理一个客户端的连接请求同时到来的时候,服务器并不是同时处理,而是将不能处理的客户端连接请求放到等待队列中,这个队列长度由listen()函数来定义。

  1. 函数介绍
#include <sys/socket.h>
int listen(int sockfd, int backlog);
                                      成功:0,失败-1 errno

sockfd是用socket()函数创建的文件描述符
backlog:上限数值,最大128.

2.5接受一个网络请求accept()函数

阻塞等待客户端建立连接,通过accept()函数可以得到客户端的IP地址、端口和协议族等信息。

  1. 函数介绍
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
                               成功 返回能与服务器进行数据通信的socket对应的文件描述符  失败 -1 errno

sockfd:socket()函数的返回值
addr:传入参数。成功与服务器建立连接的那个客户端的地址结构(IP+port)
addrlen:表示addr的长度,可以使用addr_len=sizeof(struct sockaddr_in)来获得,输入**&addr_len**。

2.6连接目标网络服务器connect()函数

用来连接服务器

  1. 函数介绍
#include <sys/types.h> 					
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
	                                                          成功返回0,失败返回-1,设置errno

sockdf:socket文件描述符
addr:传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen:传入参数,传入sizeof(addr)大小

3.简单例子

server

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>

#define SERV_PORT 9527     //服务器端口地址

void sys_err(const char *str){
    
    
    perror(str);
    exit(1);
}


int main(int argc, char *argv[])
{
    
    
    int lfd = 0 , cfd = 0;       //lfd为服务器的socket描述符 ,cfd是客户端的socket描述符
    int err;                     //返回值
    int ret;                     //读取数据的长度
    char buf[BUFSIZ]  ,client_IP[1024];    //buf缓存读取的数据 ,client_IP缓存客户端IP

    struct sockaddr_in serv_addr , clit_addr; //服务器和客户端的地址结构

    serv_addr.sin_family      = AF_INET;               //协议族
    serv_addr.sin_port        = htons(SERV_PORT);      //服务器端口
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);     //本地地址

    socklen_t clit_addr_len;

    /*建立一个流失套接字*/
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1){
    
    
        sys_err("socket error");
    }

    /*绑定地址结构到套接字描述符*/
    err = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
    if(err == -1){
    
    
        sys_err("bind error");
    }

    /*设置侦听*/
    err = listen(lfd,128);
    if(err == -1){
    
    
        sys_err("listen error");
    }

    /*接受网络请求*/
    clit_addr_len = sizeof(clit_addr);
    cfd = accept(lfd,(struct sockaddr *)&clit_addr, &clit_addr_len);

    /*打印客户端地址*/
    printf("client ip:%s port:%d\n",
            inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),
            ntohs(clit_addr.sin_port));
    if(cfd == -1){
    
    
        sys_err("accept error");
    }
    
    /*将读取的数据改为大写返回客户端*/
    while(1){
    
    

        ret = read(cfd , buf ,sizeof(buf));
    
        write(STDOUT_FILENO,buf,ret);

        for (int i = 0 ;i< ret ; i++)
        {
    
    
            buf[i] = toupper(buf[i]);   //小写转大写
        }

        write(cfd,buf,ret);
    }

    close(lfd);
    close(cfd);

    return 0;
} 

client

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>

#define SERV_PORT 9527   //客户端地址

void sys_err(const char *str)
{
    
    
    perror(str);
    exit(1);
}

int main(int argc,char *argv[]){
    
    
    int cfd;            
    int err;            //返回值
    int conter = 10;
    char buf[BUFSIZ];

    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port   = htons(SERV_PORT);
   // inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr.s_addr);
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    //建立流式套接字
    cfd = socket(AF_INET,SOCK_STREAM,0);

    if(cfd == -1)
        sys_err("socket error");
    /*连接服务器*/
    err = connect(cfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr));

    if (err == -1){
    
    
        printf("err = %d",err);
        sys_err("connect error");
    }
    while(-- conter){
    
    
        write(cfd,"hello\n",6);
       int ret = read(cfd,buf,sizeof(buf));
        write(STDOUT_FILENO,buf,ret);
        sleep(1);
    }

    close(cfd);

    return 0;
}

结果

在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41256768/article/details/121435903