Linux C网络编程 ————1、TCP网络编程
  TEZNKK3IfmPf 2023年11月14日 21 0

TCP客户端/服务器端通信模型

由于个人手拙画不出较好的图案,所以借助以前教科书中的一个图案来描述tcp通信的过程:

Linux C网络编程 ————1、TCP网络编程

TCP服务器端代码实现

/**********************************************
*TCP 服务器端实现步骤
*(1)使用socket()函数创建套接字
*(2)为创建的套接字绑定到指定的地址结构
*(3)listen()函数设置套接字为监听模式,使服务器进入被动打开状态;
*(4)接受客户端的链接请求,建立连接
*(5)接受、应答客户端的数据请求
*(6)终止链接
*
*************************************************/

//定义头文件
#include <stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 1234		//定义使用的端口号
#define BACKLOG 1		//定义最大链接数

void main(){

	int listenfd,connectfd;		//socket文件描述符
	struct sockaddr_in server;	//服务器地址信息描述符
	struct sockaddr_in client;	//客户端地址信息描述符
	socklen_t addrlen;
/******************************************************************
*功能说明:调用socket函数产生套接字描述符
*函    数:int socket(int family,int type,int protocol)
*参数说明:family  指明协议簇,确定socket使用的协议类型,其值常为:
*			   (1)AF_INET :IPV4协议
*			   (2)AF_INET6:IPV6协议
*			   (3)AF_ROUTE:路由套接口
*		   type  指明产生套接字的类型,其值常为:
			(1)SOCK_STREAM:字节流套接口,TCP使用的是这种形式
			(2)SOCK_DGRAME:数据报套接口,UDP使用的是这种形式
			(3)SOCK_RAW:原始套接口
	   	Protocol 协议标志,一般在调用socket函数时将其置为0,当如果是原始套接字,就需要为protocol指定一个常值
*返回值说明:如果函数调用失败返回 “-1”,如果调用成功,将返回一个小的非负的整数值
*********************************************************************/
	if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1){
		perror("socket() error.");
		exit(1);
	}
/*****************************************************
*功能说明:设置套接字选项SO_REUSEADDR,即地址重用选项,此处这两行不可少!
*如果缺少则程序运行时会产生错误信息“Bind() error:Address already in use”
*******************************************************/
	int opt=SO_REUSEADDR;
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

/*******************************************************
*功能说明:初始化服务器的地址结构,并为地址结构赋值
*函数说明:extern void bzero(void *s,int n);
*参数说明:s  要置零的数据的起始地址
*		   n  要置零的数据字节个数
*******************************************************/
	bzero(&server,sizeof(server)); //将套接字地址结构server设置初始值为0
	server.sin_family=AF_INET;	  //设置使用的协议簇类型
	server.sin_port=htons(PORT);  //设置使用的端口,并且将主机字节序转换为网络字节序
	server.sin_addr.s_addr=htonl(INADDR_ANY); //对于IPV4统配地址由常数INADDR_ANY常数来指定,其值一般为0,它通知内核选择IP地址
/*******************************************************
*功能说明:将套接字和指定的协议地址绑定
*函数说明:int bind(int sockfd,const struct sockaddr *server,socklen_len addrlen);
*参数说明:sockfd  套接字函数返回的套接字描述符
*		   server  指向特定于协议的地址结构的指针,指定用于通信的本地协议地址
*		   addrlen 指定了该套接字地址结构的长度
*返回值说明:调用成功返回0,出错返回-1,并置错误号errno
*******************************************************/
	if(bind(listenfd,(struct sockaddr *)&server,sizeof(server))==-1){
		perror("Bind() error");
		exit(1);
	}
/*********************************************************
*功能说明:将套接字描述符转换成监听描述符,等待客户的连接
*函数说明:int  listen(int sockfd,int backlog);
*参数说明:sockfd要设置的描述符
*		   backlog规定了请求队列中的最大连接个数
*返回值说明:函数调用成功返回0,出错返回-1,并置errno值
**********************************************************/
	if(listen(listenfd,BACKLOG)==-1){
		perror("listen() error.\n");
		exit(1);
	}
	int len=sizeof(client);
/******************************************
*功能说明:接受客户链接,客户的地址信息存放在client地址结构中
*函数说明:int accept(int listenfd,struct sockaddr *clinet,socklen_t * addrlen);
*参数说明:listenfd参数是由socket函数产生的套接字描述符,在调用accept函数前,已经调用listen函数将次套接字变成了监听套接字
*          client 返回连接对方的套接字地址结构
*		   addrlen 返回链接对方的套接字对应的结构长度
*返回值说明:函数调用失败返回-1,至于调用成功参考P30
*******************************************/
	if((connectfd=accept(listenfd,(struct sockaddr *)&client,&addrlen))==-1){
		perror("accept() error\n");
		exit(1);
	}
	printf("You got a connetcion from cient's ip is %s,port is %d\n",inet_ntoa(client.sin_addr),htons(client.sin_port));
	
	send(connectfd,"Welcome\n",8,0);  //向客户端发送欢迎信息
	close(connectfd);				 //关闭链接
	close(listenfd);				//关闭监听状态
}

TCP客户端代码实现

/*****************************************************************
*     TCP客户端实现的步骤:
*
*(1)使用socket()函数创建套接字
*(2)调用connect函数建立一个与TCP服务器的链接
*(3)发送数据请求,接收服务器的数据应答
*(4)终止链接
*******************************************************************/

//头文件包含
#include <stdio.h>
#include<stdlib.h>
#include <unistd.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

//宏定义
#define PORT 1234			//定义端口
#define MAXDATASIZE 100		       //定义缓冲区大小

/********************************
*在main函数中有两个参数:
*argc        指命令行中参数的个数
*argv[]      指向字符串
********************************/
int main(int argc,char *argv[]){
	
	int sockfd,num;			 //sockfd是sock file descriptor的缩写,是套接字函数返回的套接字描述符 
	
	char buf[MAXDATASIZE];		 //定义用于存储数据的缓冲区的大小
	
	struct  hostent *he;		//定义指向hostent结构体的指针
	
	struct sockaddr_in server;	//定义结构体变量sockaddr_in变量
	
/*************************************************
*  检查用户的输入,如果输入不正确则提示错误信息
**************************************************/
	if (argc!=2){
		printf("Usage:%s<IP Address>\n",argv[0]);
		exit(1);
	}

/***********************************************************
*功能说明:通过用户输入的点分十进制形式的IP地址信息获取服务器的地址信息
*函    数:struct hostent * gethostbyname(const char * hostname)
*参    数:hostname 是主机的域名地址
*功    能:函数将查询的结果作为参数返回,如果失败返回空指针,如果成功此参
*		   数返回非空指针指向hostent结构
* hostent的结构实例:
*    struct hostent{
*		char * h_name;      //主机的正式名称
*		char **h_aliases;   //主机的别名列表
*		int    h_addrtype;  //主机地址类型
*		int    h_length;    //主机地址长度
*		char **h_addr_list; //主机IP地址的列表
*	}
************************************************************/
	if((he=gethostbyname(argv[1]))==NULL){
		printf("gethostbyname() error\n");
		exit(1);
	}
/******************************************************************
*功能说明:调用socket函数产生套接字描述符
*函    数:int socket(int family,int type,int protocol)
*参数说明:family  指明协议簇,确定socket使用的协议类型,其值常为:
*			   (1)AF_INET :IPV4协议
*			   (2)AF_INET6:IPV6协议
*			   (3)AF_ROUTE:路由套接口
*		   type  指明产生套接字的类型,其值常为:
			   (1)SOCK_STREAM:字节流套接口,TCP使用的是这种形式
			   (2)SOCK_DGRAME:数据报套接口,UDP使用的是这种形式
			   (3)SOCK_RAW:原始套接口
		   Protocol 协议标志,一般在调用socket函数时将其置为0,当如果是原始套接字,就需要为protocol指定一个常值
*返回值说明:如果函数调用失败返回 “-1”,如果调用成功,将返回一个小的非负的整数值
*********************************************************************/
	if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
		printf("socket() error\n");
		exit(1);
	}
/*******************************************************
*功能说明:初始化服务器的地址结构,并为地址结构赋值
*函数说明:extern void bzero(void *s,int n);
*参数说明:s  要置零的数据的起始地址
*		   n  要置零的数据字节个数
*******************************************************/
	bzero(&server,sizeof(server));   //将套接字地址结构server设置初始值为0
	server.sin_family=AF_INET;		//设置使用的协议簇类型
	server.sin_port=htons(PORT);	//设置使用的端口,并且将主机字节序转换为网络字节序
	server.sin_addr=*((struct in_addr *)he->h_addr);
/****************************************************
*函数说明:建立与服务器之间的链接
*函数说明:int connect(int sockfd,const struct sockaddr * addr,socklen_t addrlen)
*参数说明: sockfd  socket函数返回的套接字描述符
*			addr    指向服务器的套接字地址结构的指针
*			addrlen 该套接字地址结构的大小
*******************************************************/
	if(connect(sockfd,(struct sockaddr *)&server,sizeof(server))==-1){
		printf("connect() error\n");
		exit(1);
	}
/******************************************************
*功能说明:接受服务器端发来的字符串
*函数说明:ssize_t recv(int sockfd,void *buf,size_t len,int flags);
*参数说明:sockfd  套接字描述符
*		   buf     指向一个用于接收信息的数据缓冲区
*		   len     指明接收数据缓冲区的大小
*		   flag	   传输控制标识符,其值定义如下:
*				(1)0  常规操作  (常用,其余的借鉴书籍P32)
******************************************************/
	if((num==recv(sockfd,buf,MAXDATASIZE,0))==-1){
		printf("recv() error\n");
		exit(1);
	}
    buf[num-1]='\0';
	printf("server message:%s\n",buf);
	close(sockfd);
}

运行实例

服务器端:

Linux C网络编程 ————1、TCP网络编程

客户端:

Linux C网络编程 ————1、TCP网络编程

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月14日 0

暂无评论

推荐阅读
  TEZNKK3IfmPf   2024年05月31日   51   0   0 linux服务器
  TEZNKK3IfmPf   2024年05月31日   29   0   0 linux服务器centos
  TEZNKK3IfmPf   2024年05月31日   29   0   0 linuxbind
  TEZNKK3IfmPf   2024年05月31日   39   0   0 linuxshell
TEZNKK3IfmPf