Linux网络编程之TCP编程

学习目标:

  • 1、编写TCP多线程服务器

  • 2、编写TCP多进程并发服务器

网络编程基础:

TCP:(Transfer Control protocol,传输控制协议)提供面向连接的,一对一的可靠传输的协议

数据无误,数据不丢失,数据无失序

适用场景:

适合对传输质量要求较高,以及传输大量数据的通信

在需要可靠数据传输的场合,通常适用TCP协议

MSN/QQ等即时通讯软件的用户登录账号管理相关的功能通常采用TCP协议

UDP:(user Datagram Protocol,用户数据报协议):提供不可靠,无连接的传输协议

适用场景发送小尺寸数据(如对DNS服务器进行IP地址查询时) 在接收数据,给出应答较困难的网络中适用UDP(如无线网络) 适合于广播/组播式通信中 MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通常采用UDP协议 流媒体,VOD,VoIP,IPTV等网络多媒体服务中通常采用UDP方式进行实时数据传输

字节转换函数:

主机字节序到网络字节序:

                        u_long htonl(u_long hostlong);

                        u_short htons(u_short short);

网络字节序到主机字节序:

                        u_long ntohl(u_long hostlong);

                        u_short ntohs(u_short short);

IP地址的转换:

inet_aton()

        将strptr所指的字符串转换成32位的网络字节序二进制值

inet_addr()

        功能同上,返回转换后的地址

        仅适用于IPV4,出错时返回-1。

        局限性:不能用于255.255.255.255的转换

inet_ntoa()将32位网络字节序二进制地址转换成点分十进制的字符串

inet_pton()

        int inet_pton(int af, const char* src ,void* dst)

        将IPV4/IPV6 的地址转换成binary格式

        使用于IPV4/IPV6

        能正确处理255.255.255.255的转换问题

参数:

        1.地址协议族(AF_INET或AF_INET6)

        2.src:是一个指针(填写分点形式的IP地址(主要指IPV4))

        3.dst:转换的结果给到dst

inet_ntop(int af,const void *src,char *dst,socklen_t size)

        把ipv4和ipv6的网络字节序变成本地的字符串形式的IP地址

参数:

        1.af:地址协议族(AF_INET或AF_INET6)

        2.src:是一个指针(32)

        3.dst:输出结果为32位点分形式的IP地址

        4.size:长度

熟悉TCP编程API

1.socket函数

int socket(int domain,int type,int protocol);

参数

domain:

                AF_INET

                AF_INET6

                AF_UNIX,AF_LOCAL

                AF_NETLINK

                AF_PACKET

type:

        SOCK_STREAM: 流式套接字,唯一对应于TCP

        SOCK_DGRAM:数据报套接字,唯一对应着UDPSOCK_RAW:原始套接字

protocol:

                一般填0,原始套接字编程时需填充

返回值:

        成功返回文件描述符

        出错返回-1

如果是IPV6编程,要使用struct sockddr_in6结构体(man 7 IPV6),通常使用struct sockaddr_storage来编程。

2.bind函数

int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)

参数:

        sockfd:通过socket()函数拿到的fd

        addr:采用struct sockaddr的结构体地址,通用结构体

        struct sockaddr{

        sa_family_t sa_family;

        char sa_data[4];

        }

// 基于Internel通信结构体

        struct sockaddr_in{

        as_family_t sin_family;

        in_port_t sin_port;

        struct in_addr sin_addr;

        sin_zero , //填充字节,需清零

        }

        struct in_addr{

        uint32_t s_addr;

        }

        addrlen:地址长度

3.listen()函数

int listen(int sockfd,int backlog);

参数:

        sockfd: 通过socket()函数拿到的fd;

        backLog:同时允许几路客户端和服务器进行正在连接的过程(正在三次握手),一般填5

        内核中服务器的套接字fd会维护2个链表

1.正在三次握手的客户端链表(数量=2*backlog+1)

2.已经建立好连接的客户端链表(已经完成三次握手分配好了的newfd)

返回值:

        成功返回0;   出错返回-1

listen(fd,5);//表示系统允许11(2*5+1)个客户端同时进行三次握手

4.accept()函数

阻塞等待客户端连接请求

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数:

        sockfd:经过前面socket()创建并通过bind(),listen()设置过的fd

        addr:指向存放地址信息的结构体的首地址

        获取客户端IP地址和端口号

        addrlen:存放地址信息的结构体的大小

返回值:

        成功,返回返回已经建立连接的新的newfd

        出错,返回-1

5.客户端连接函数connect()

int connect (int sockfd, struct sockaddr * serv_addr, int addrlen)

参数:

        sockfd:通过socket()函数拿到的fd

        addr:struct sockaddr的结构体变量地址

        addrlen:地址长度

返回值:

                成功,返回0

                失败,返回-1

TCP编程基本流程:

TCP实现并发服务器的基本流程:

TCP实现多进程并发服务器

服务端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<signal.h>
#include<sys/wait.h>
#define BUFSIZE   1024
#define  QUIT_STR "QUIT"
#define SERV_IP 5001
#define SERV_IP_ADDR  "192.168.224.132"
#define  BACKLOG  5


void *client_data_handle( void *arg)
{
	//强转为int型的指针,对它再取值
	int newfd=*(int *) arg;
	int ret = -1;
	char buf[BUFSIZE];
	printf(" child handle  progess : newfd =%d\n",newfd);
	while(1)
	{
		
		do
		{
			bzero(buf,BUFSIZE);
			ret=read(newfd,buf,BUFSIZE-1);
		}while(ret < 1);
		if ( ! ret)
		{
			break;
		}
		printf(" recevice data :%s\n",buf);
		//两个字符串做比较,不一样返回0,一样返回1,则执行下面的内容
		if( ! strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))	             		          {
			printf(" Client is exiting !\n");
			break;
		}
		
	}
	close(newfd);
	return NULL;
}


void child_data_handle(int signum)
{
	if( SIGCHLD  == signum)
	{
		waitpid(-1,NULL,WNOHANG);
	}
}
int main()
{
	printf(" this is service!\n");
	int fd = -1;
	signal(SIGCHLD,child_data_handle);
	struct sockaddr_in sin;
	//1.socket
	fd=socket(AF_INET,SOCK_STREAM,0);
	
	if( fd <0)
	{

		perror("socket");
		exit(1);
	}
	//把结构体清零
	bzero(&sin,sizeof(sin));
	sin.sin_family=AF_INET;

	sin.sin_port=htons(SERV_IP);//两个字节
/*
inet_addr(),返回转换后的地址,仅适用于IPV4,出错时返回-1
将IPV4/IPV6 的地址转换成binary格式
inet_pton(AF_INET,SERV_IP_ADDR ,(void*) sin.sin_addr.s_addr);
两种方法都可以
*/

	sin.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);
	//优化,可以在任何的服务器中运行,自动获取当前的ip地址
	//sin.sin_addr.s_addr=INADDR_ANY;
	//2.bind
	if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))<0)
	{
		perror("bind");
		exit(1);
	}
	//3.listen
      if(listen(fd,BACKLOG)<0)
	{
		perror("listen");
		exit(1);
	}
	//4.acccept

//进程
	int newfd =  -1;
	pid_t  pid;
	struct sockaddr_in cin;
	socklen_t  addrlen =sizeof(cin);
	while(1)
	{
		 newfd=accept(fd,NULL,NULL);
		if( newfd <0)
		{
			perror("acccept");
			exit(1);
		}
	pid =fork();
	if( pid <0)
	{
		perror("fork");
		break;
	}
	if ( pid == 0)
	{
		char  ipv4_addr[16];
	     if(! inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
	{
		perror("inet_ntop");
		exit(1);
	}
	//获取端口号和ip地址
	printf("client : (%s , %d) is connect !\n", ipv4_addr,ntohs(cin.sin_port));
	
	client_data_handle(&newfd);
	close(fd);
	}
	if( pid > 0)
	{
		close(newfd);
	}
		
	}
		close(fd);
			
	return  0;

}

客户端:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define BUFSIZE   1024
#define  QUIT_STR "QUIT"
#define SERV_PORT 5001
#define SERV_IP_ADDR  "192.168.224.132"
int main(int argc , char **argv)
{
	

	int fd = -1;
	if( argc != 3)
	{
		exit(1);
	}
	int port = -1;
	port=atoi(argv[2]);
	struct sockaddr_in sin;
	//1.socket
	fd=socket(AF_INET,SOCK_STREAM,0);
	
	if( fd <0)
	{
		perror("socket");
		exit(1);
	}
	//把结构体清零
	bzero(&sin,sizeof(sin));
	sin.sin_family=AF_INET;
	sin.sin_port=htons(port);
	sin.sin_addr.s_addr=inet_addr(argv[1]);

	if(connect(fd,(struct sockaddr *)&sin,sizeof(sin)) < 0)
	{
		perror("connect");
		exit(1);
	}
	char buf[BUFSIZE];
	while(1)
	{
		bzero(buf,BUFSIZE);
		if(fgets(buf ,BUFSIZE-1,stdin) == NULL)
		{
			continue;
		}
		write(fd,buf,strlen(buf));
	if( !strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))	
		{
			
			break;
		}

	}
		return 0;
}

实现效果:

TCP实现多线程并发服务器:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<strings.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<pthread.h>
#include<signal.h>
#include<sys/wait.h>
#define BUFSIZE   1024
#define  QUIT_STR "QUIT"
#define SERV_IP 5001
#define SERV_IP_ADDR  "192.168.224.132"
#define  BACKLOG  5

void *client_data_handle( void *arg)
{
	//强转为int型的指针,对它再取值
	int newfd=*(int *) arg;
	int ret = -1;
	char buf[BUFSIZE];
	printf(" pthread  progess : newfd =%d\n",newfd);
	while(1)
	{
		
		do
		{
			bzero(buf,BUFSIZE);
			ret=read(newfd,buf,BUFSIZE-1);
		}while(ret < 1);
		if(ret < 0)
		{
			exit(1);
		}
		if ( ! ret)
		{
			break;
		}
		printf(" recevice data :%s\n",buf);
		//两个字符串做比较
		if( ! strncasecmp(buf,QUIT_STR,strlen(QUIT_STR)))	             		          {
			printf(" Client is exiting !\n");
			break;
		}
		
	}
	close(newfd);
	return NULL;
}

void child_data_handle(int signum)
{
	if( SIGCHLD  == signum)
	{
		waitpid(-1,NULL,WNOHANG);
	}
}
int main()
{
	printf(" this is service!\n");
	int fd = -1;
	signal(SIGCHLD,child_data_handle);
	struct sockaddr_in sin;
	//1.socket
	fd=socket(AF_INET,SOCK_STREAM,0);
	
	if( fd <0)
	{

		perror("socket");
		exit(1);
	}
	//把结构体清零
	bzero(&sin,sizeof(sin));
	sin.sin_family=AF_INET;

	sin.sin_port=htons(SERV_IP);//两个字节

	sin.sin_addr.s_addr=inet_addr(SERV_IP_ADDR);
	//优化,可以在任何的服务器中运行,自动获取当前的ip地址
	//sin.sin_addr.s_addr=INADDR_ANY;
	//2.bind
	if(bind(fd,(struct sockaddr *)&sin,sizeof(sin))<0)
	{
		perror("bind");
		exit(1);
	}
	//3.listen
      if(listen(fd,BACKLOG)<0)
	{
		perror("listen");
		exit(1);
	}
	//4.acccept
	/*
	int newfd =  -1;
           newfd=accept(fd,NULL,NULL);
	if( newfd <0)
	{
		perror("acccept");
		exit(1);
	}
	*/
	//优化,服务端知道哪个客户端连接了,并可以获取端口号和ip地址
	/*
	每接收一个就打印它的客户信息,并创建一个线程,并跳转到线程这个函数里面去
	*/
	
	int newfd =  -1;
	pthread_t  tid;
	struct sockaddr_in cin;
	socklen_t  addrlen =sizeof(cin);
	while(1)
	{
	newfd=accept(fd,(struct sockaddr *)&cin,&addrlen);
	         if( newfd <0)
                {
                        perror("acccept");
                        exit(1);
                }

	char  ipv4_addr[16];
	if(! inet_ntop(AF_INET,(void *)&cin.sin_addr,ipv4_addr,sizeof(cin)))
	{
		perror("inet_ntop");
		exit(1);
	}
	//获取端口号和ip地址
	printf("client : (%s , %d) is connect !\n", ipv4_addr,ntohs(cin.sin_port));
	pthread_create(&tid,NULL,client_data_handle,(void *)&newfd);
	}	
	close(fd);			
	return  0;

}

服务端和TCP实现多进程并发服务器的服务端代码一致

实现效果:

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值