您的位置:首页 > 理论基础 > 计算机网络

Linux网络编程之[基于socket通信的tcp协议的编程模型]

2017-04-23 14:10 1286 查看

Linux网络编程之[基于socket通信的tcp协议的编程模型]

TCP客户端服务器的编程模型(傻瓜式通信流程)

TCP协议的相关函数

3 基于TCP协议的案例

TCP客户端服务器的编程模型(傻瓜式通信流程)

客户端的调用序列:
调用socket函数创建套接字
调用connect连接服务器端
调用I/O函数(read/write)函数与服务器端进行通讯
调用close关闭套接字

服务器端调用序列
调用socket函数创建套接字
调用bind绑定本地地址和端口
调用listen启动监听
调用accept从已连接队列中提取客户连接
调用(I/O)函数(read/write)与客户端进行通讯
调用close关闭套接字


TCP协议的相关函数

绑定地址

#include<sys/socket.h>
int bind(int sockfd,const struct sockaddr * addr,socklen_t len);
返回:成功返回0,出错返回-1

一台主机往往有多个网络接口和多个IP地址,
如果我们只去关心某个地址的连接请求,
我们可以指定一个具体的本地IP地址,如果要想用所有接口上的连接请求,就要使用一个特殊的INADDR_ANY
#define INADDR_ANY (uint32_t)0x00000000
//监听所有服务器上ip得到的连接请求
struct sockaddr_in servaddr;
memset(&servaddr,0,sizeof(servaddr));
servaddr.sin_addr.s_addr = INADDR_ANY


查找绑定到套接字的地址

#include<sys/socket.h>
int getsockname(int sockfd,struct sockaddr * restrict addr,socklen_t * restrict alenp);
返回:成功返回0,失败返回-1


获取对方地址

#include<sys/socket.h>
int getpeername(int sockfd,struct sockaddr *restrict addr,socklen_t * restrict alenp);
返回:成功返回0,失败返回-1


服务器端

#include<sys/socket.h>
int listen(int sockfd,int backlog);
返回:成功返回0,失败返回-1,backlog指定进行客户端连接排队的队列长度
int accept(int sockfd,struct sockaddr * restrict addr,socklen_t *restrict len);
addr :客户端连接上来的时候的地址信息
sockfd:传过来的sock的文件描述符


客户端

#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr * addr,socklen_t len);
返回:成功返回0,失败返回-1


基于TCP协议的案例

/*
* ===========================================================================
*
*       Filename:  socket_utils.h
*    Description:
*        Version:  1.0
*        Created:  2017年04月23日 11时26分40秒
*       Revision:  none
*       Compiler:  gcc
*         Author:   (),
*        Company:
*
* ===========================================================================
*/
#ifndef __SOCKET_UTILS_H_
#define __SOCKET_UTILS_H_

extern void readSocketAddr(struct sockaddr_in *addr);

extern void readSocket(int socketfd);

extern void writeSocket(int socketfd);

#endif


/*
* ===========================================================================
*
*       Filename:  socket_utils.c
*    Description:
*        Version:  1.0
*        Created:  2017年04月23日 11时28分45秒
*       Revision:  none
*       Compiler:  gcc
*         Author:   (),
*        Company:
*
* ===========================================================================
*/
#include<unistd.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netdb.h>
#include<memory.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<time.h>
//注意自定义的头文件需要放在系统的头文件之后
#include"socket_utils.h"

/* *
*读取连接过来的客户端地址的信息
* */
extern void readSocketAddr(struct sockaddr_in *addr){
//将网络字节序转换成本地字节序
int port = ntohs(addr->sin_port);
char ip[16];
memset(ip,0,sizeof(ip));
//将ip地址从网络字节序转换成点分十进制形式
inet_ntop(AF_INET,&addr->sin_addr.s_addr,ip,sizeof(ip));
printf("client:%s(%d) connected\n",ip,port);
}

/* *
*向对应的socket描述符中去读取数据
* */
extern void readSocket(int socketfd){
char buffer[1024];
memset(buffer,0,1024);
ssize_t size;
if((size= read(socketfd,buffer,sizeof(buffer))) < 0){
perror("read error\n");
exit(1);
}
if(write(STDOUT_FILENO,buffer,size) != size){
perror("write error");
exit(1);
}

}

/* *
*向对应的socket描述符中去写数据
* */
void writeSocket(int socketfd){
time_t t = time(0);
char *s = ctime(&t);
size_t size = strlen(s) * sizeof(char);

//char result[] = "server connected success\n";

//连接成功后会先给客户端返回一个时间
if(write(socketfd,s,size) != size){
perror("write error\n");
exit(1);
}
}


/*
* ===========================================================================
*
*       Filename:  server.c
*    Description:  TCP的server端的开发
*        Version:  1.0
*        Created:  2017年04月22日 22时46分58秒
*       Revision:  none
*       Compiler:  gcc
*         Author:   (),
*        Company:
*
* ===========================================================================
*/

#include<netdb.h>
#include<unistd.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<signal.h>
#include<time.h>
#include"socket_utils.h"

//创建socket的描述符号
int sockfd;

void sig_handler(int sig){
if(sig ==SIGINT){
printf("server close\n");
close(sockfd);
exit(1);
}
}

int main(int argc,char *argv[]){
//从参数中传入端口号
if(argc < 2){
printf("参数不齐");
exit(1);
}

if(signal(SIGINT,sig_handler) == SIG_ERR){
perror("signal sigint error");
exit(1);
}
/* *
*步骤一:创建socket,TCP协议是sock_strem(创建在内核中的)
* */
sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd < 0){
perror("socket create error\n");
exit(0);
}

/* *
*配置socket的参数,绑定ip和端口
* */
struct sockaddr_in addr_in;
memset(&addr_in,0,sizeof(addr_in));

addr_in.sin_family = AF_INET;//IP_V4
addr_in.sin_port = htons(atoi(argv[1]));//将本地字节序号转化成网络字节序号
addr_in.sin_addr.s_addr = INADDR_ANY;//服务器端监听所有服务器上ip得到的连接请求,转换成同时也是需要将其转换成网络字节序
if(bind(sockfd,(struct sockaddr*)&addr_in,sizeof(addr_in)) < 0){
perror("bind error\n");
exit(1);
}

/* *
*启动对listen监听,通知系统去接受来自客户端的连接请求操作
*将接受到的客户端连接请求放置到对应的队列中去
* */
if(listen(sockfd,100) < 0){
perror("listen error\n");
exit(1);
}

/**
* 使用while死循环来让server端一直等待客户端的连接,并且从连接队列中提取客户连接
*/
struct sockaddr_in clientaddr;
memset(&clientaddr,0,sizeof(clientaddr));
socklen_t len = sizeof(clientaddr);
while(1){
//拿到客户端的sock描述符好,读写操作
int clientfd = accept(sockfd,(struct sockaddr*)&clientaddr,&len);
if(clientfd < 0){
perror("recieve clientfd error");
continue;
}
readSocketAddr(&clientaddr);
readSocket(clientfd);
writeSocket(clientfd);
}

return 0;
}


/*
* ===========================================================================
*
*       Filename:  tcp_client.c
*    Description:
*        Version:  1.0
*        Created:  2017年04月23日 12时49分30秒
*       Revision:  none
*       Compiler:  gcc
*         Author:   (),
*        Company:
*
* ===========================================================================
*/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<memory.h>
#include<netdb.h>
#include"socket_utils.h"

int main(int argc,char * argv[]){

if(argc < 3){
perror("缺少参数");
exit(1);
}
/* *
*创建socket
* */

int clientsockfd = socket(AF_INET,SOCK_STREAM,0);
if(clientsockfd < 0){
perror("socket error");
exit(1);
}

/* *
*往serveraddr中填入ip,port和地址族类型(ipv4)connect的过程
* */

struct sockaddr_in socketaddr;
memset(&socketaddr,0,sizeof(socketaddr));
socketaddr.sin_family = AF_INET;
socketaddr.sin_port = htons((short)atoi(argv[2]));

if(inet_pton(AF_INET,argv[1],&socketaddr.sin_addr.s_addr) <= 0){
perror("pton change error\n");
exit(1);
}

if(connect(clientsockfd,(struct sockaddr*)&socketaddr,sizeof(socketaddr)) < 0){
perror("connect error");
exit(1);
}

/* *
*调用IO函数(read/write)和服务器端进行双向通信
* */
//readSocket(clientsockfd);
close(clientsockfd);
return 0;
}


以上的代码都是可以直接进行run的,这只是一个单连接的小demo,如果server端支持多端连接的并且分别通信的话,需要加入线程等操作.在这里就没有展示了,相对于java中的socket调用,在C上显得稍微复杂了点,但是原理上其实都是一样的

欢迎持续访问博客

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐