linux c编程:网络编程

mac2022-06-30  79

在网络上,通信服务都是采用C/S机制,也就是客户端/服务器机制。流程可以参考下图:

服务器端工作流程:

使用socket()函数创建服务器端通信套接口

使用bind()函数将创建的套接口与服务器地址绑定

使用listen()函数使服务器套接口做好接收连接请求准备

使用accept()接收来自客户端由connect()函数发出的连接请求

根据连接请求建立连接后,使用send()函数发送数据,或者使用recv()函数接收数据

使用closesocket()函数关闭套接口(可以先用shutdown()函数先关闭读写通道)

客户端程序工作流程:

使用socket()函数创建客户端套接口

使用connect()函数发出也服务器建立连接的请求(调用前可以不用bind()端口号,由系统自动完成)

连接建立后使用send()函数发送数据,或使用recv()函数接收数据

使用closesocet()函数关闭套接口

下面介绍几个函数的用法:

socket函数:int socket(int domain,int type,int protocol)

参数说明: domain:指明协议族,也称为协议域,是一个常值。 AF_INET:IPv4 协议 AF_INET6:IPv6 协议 AF_LOCAL/AF_UNIX:Unix协议域 AF_ROUTE: 路由套接字 AF_KE:密匙套接字 type:指明套接字的类型。 SOCK_STREA:字节流套接字 SOCK_DGRA:数据报套接字 SOCK_SEQPACKE:有序分组套接字 SOCK_RAW:原始套接字 protocol: 指明协议类型。一般为0,以选择给定的domaintype组合的系统默认值。 IPPROTO_TCP:TCP传输协议 IPPROTO_UDP:UDP传输协议 IPPROTO_SCTP:SCTP传输协议

函数描述: socket 函数在成功时返回一个小的非负整数值,与文件描述符类似,我们称它为套接字描述符,简称 sockfd。为了得到这个套接字描述符,我们只是指定了协议族(IPv4IPv6Unix)和套接字类型(字节流、数据报或原始套接字)。我们并没有指定本地跟远程的协议地址

bind函数:int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

将一个本地协议地址赋予一个套接字。对于网际网协议,协议地址是32位的IPv4地址和128位的IPv6地址与16位的TCPUDP端口号的组合。bind 函数主要用于服务器端,用来指定本地主机的哪个网络接口(IP,可以是INADDR_ANY,表示本地主机的任一网络接口)可以接受客户端的请求,和指定端口号(即开启的等待客户来连接的进程)。

参数说明:

sockfd: socket 函数返回的套接字描述符。

myaddraddrlen:指向一个套接字地址结构的指针和该结构的大小。

地址结构的一般采用sockadr_in结构体

struct sockaddr_in{

short int sin_family; #地址族

unsigned short int sin_port; #端口号

struct n_addr sin_addr; #IP地址

unsigned char sin_zeor[8]; #填充0保持与struct sockaddr同样大小

}

 

listen函数:

int listen(int sockfd,int backlog)

listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen函数将socket变为被动类型的,等待客户的连接请求。

Connetc函数:

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接

accept函数:

int accept(int sockfd,struct sockaddr *addr, socketen_t *add_len)

参数 sockfd 参数 sockfd 就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。 参数 addr 这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为 NULL 参数 len 如同大家所认为的,它也是结果的参数,用来接受上述 addr 的结构的大小的,它指明 addr 结构所占有的字节个数。同样的,它也可以被设置为 NULL

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。

服务器测的代码如下:对于服务端来说,首先是建立socket然后绑定IP地址到socket上。然后开始监听。

当接收到请求的时候,通过recv函数存储在buf数组里面。并且通过send函数向对端发送消息

int server_function(){    char *sendbuf="thanks";    char buf[256];    int s_fd,c_fd;    int s_len,c_len;    struct sockaddr_in s_addr;    struct sockaddr_in c_addr;    s_fd=socket(AF_INET,SOCK_STREAM,0);    s_addr.sin_family=AF_INET;    s_addr.sin_addr.s_addr=htonl(INADDR_ANY);    s_addr.sin_port=PORT;    s_len=sizeof(s_addr);    bind(s_fd,(struct sockaddr *)&s_addr,s_len);    listen(s_fd,10);    while(1){        printf("please wait a moment\n");        c_len=sizeof(c_addr);        c_fd=accept(s_fd,(struct sockaddr *)&c_addr,(socklen_t *__restrict)&c_len);        recv(c_fd,buf,256,0);        buf[strlen(buf)+1]='\0';        printf("receve message:\n%s\n",buf);        send(c_fd,sendbuf,strlen(sendbuf),0);        close(c_fd);    }}

 

 

客户端的代码如下:设置好连接的IP地址和端口后,通过connect发起连接。并通过sendrecv函数进行发送和接收消息

int client_function(){    char *buf="come on";    char rebuf[250];    int sockfd,len,newsockfd,len2;    struct sockaddr_in addr;    sockfd=socket(AF_INET,SOCK_STREAM,0);    addr.sin_family=AF_INET;    addr.sin_addr.s_addr=htonl(INADDR_ANY);    addr.sin_port=PORT;    len=sizeof(addr);    newsockfd=connect(sockfd,(struct sockaddr *)&addr,len);    len2=strlen(buf);    send(sockfd,buf,len2,0);    sleep(5);    recv(sockfd,rebuf,40,0);    printf("the length of the rebuf is %d",strlen(rebuf));    rebuf[strlen(rebuf)+1]='\0';    printf("receive message:\n%s\n",rebuf);    close(sockfd);    return 0;}

 

打开2个终端,分别运行服务器和客户端的代码。执行结果如下:

 

转载于:https://www.cnblogs.com/zhanghongfeng/p/7802728.html

相关资源:linux C语言 网络编程教程及源码
最新回复(0)