网络编程——C/S模型

目录

TCP介绍

C/S模型框架

Server端

1.创建套接字

2.绑定IP地址和端口号

3.使用该套接字监听连接请求

4.当请求来到时,调用accept函数复制该套接字处理请求

5.数据通信

client端

        1.创建套接字

        2.使用创建好的套接字向服务端发送连接请求

        3.利用套接字进行数据的通信

server端简单示例代码

client端简单示例代码

存在的问题


TCP介绍

TCP:传输控制协议,提供面向连接的,一对一的可靠数据传输协议。

TCP特点:能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。

TCP适用情况:

  1. 适合于对传输质量要求比较高,以及传输大量数据的通信。
  2. 在需要可靠数据传输的场合,通常适用TCP协议。
  3. MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议。

C/S模型框架

Server端

1.创建套接字

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

        该套接字是后面用来监听客户端请求的套接字。

2.绑定IP地址和端口号

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

        该函数在填写addr参数时,要注意,我们先填写struct sockaddr_in这个结构体,再传递参数时,要将sockaddr_in类型的指针强转成sockaddr类型的指针。

        注意:网络字节序和本地字节序的切换。

端口:

        uint32_t htonl(uint32_t hostlong);

       uint16_t htons(uint16_t hostshort);

       uint32_t ntohl(uint32_t netlong);

       uint16_t ntohs(uint16_t netshort);

ip地址:

        in_addr_t inet_addr(const char *cp);

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

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

3.使用该套接字监听连接请求

        int listen(int sockfd, int backlog);

        socket创建的套接字是一个主动套接字,也就是说是主动连接别人的一个套接字。listen函数将一个未连接的套接字转换成了一个被动套接字,等待连接。调用listen使套接字从closed状态转换成了listen状态。

4.当请求来到时,调用accept函数复制该套接字处理请求

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

        一般我们将函数第一个参数称为监听套接字,将函数返回的套接字称为已连接套接字。一个服务器只有一个监听套接字,监听客户端的连接请求。服务器内核为每一个客户端的TCP连接维护1个已连接套接字,用它实现数据双向通信。

5.数据通信

        利用accept返回的套接字,进行数据的通信。也就是IO操作了。

client端

        1.创建套接字

                同TCP

        该套接字是用来向服务器发送请求的套接字。

        2.使用创建好的套接字向服务端发送连接请求

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

        该函数第二个参数是一个结构体,该结构体中保存着发送连接请求对象的ip地址和端口号。

        3.利用套接字进行数据的通信

server端简单示例代码

int main(int argc, const char *argv[])
{
	int fd = -1;
	struct sockaddr_in sin;
	int ret ;
	/*1.创建socket套接子*/
	if((fd = socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		perror("socket");
		exit(1);
	}
	/*2.bind绑定,绑定之前填充sockaddr_in这个结构体*/
			/*2.1   sin_zero 后八位要全部置零*/
	bzero(&sin,sizeof(sin));
			/*2.2   域指定IPV4编程*/
	sin.sin_family = AF_INET;
			/*2.3   填写端口号*/
	sin.sin_port = htons(5001);
			/*2.4   将网络地址填写到sin.sin_addr.s_addr中*/
	sin.sin_addr.s_addr = inet_addr("192.168.1.3"); /*inet_addr函数只适用于IPV4*/
	
#if 0
    inet_pton(AF_INET,"127.0.0.1",(void *)&sin.sin_addr.s_addr);/*inet_pton可以使用IPV6*/
#endif	

#if 0	
    sin.sin_addr.s_addr = htonl(INADDR_ANY);   
    /*只需绑定INADDR_ANY,管理一个套接字就行,不管数据是从哪个网卡过来的,只要是绑定的端口号过来的数据,都可以接收到。*/
#endif

            /*2.5 最后进行绑定*/
	if(bind(fd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		perror("bind");
		exit(1);
	}
 	
	/*3.listen 把主动套接子变成被动套接子*/
	if(listen(fd,5) < 0)
	{
		perror("listen");
		exit(1);
	}
	/*4.阻塞等待客户端链接请求*/
	int newfd = -1;
	newfd = accept(fd,NULL,NULL);   /*两个NULL 不关心客户端的ip和端口号,所以就NULL*/
	if(newfd < 0)
	{
		perror("accept");
		exit(1);
	}
	/*5.读写*/
	ret = -1;
	char buf[255];
	while(1)
	{
		bzero(buf,255);
		do
		{
			ret = read(newfd,buf,255 - 1);
			}while(ret < 0 && errno != EINTR);
		if(ret < 0)
		{	
			perror("read");
			exit(1);
		}
		if(!ret)  //对方以关闭
		{
			break;
		}
		printf("Receive data :%s\n",buf);
		if(!strncasecmp(buf,"quit",4))
		{
			break;
		}
	}
	close(newfd);
	close(fd);
	return 0;
}

client端简单示例代码

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>

int main(int argc, const char *argv[])
{
	int fd = -1;
	struct sockaddr_in sin;
	int ret ;
	/*1.创建socket套接子*/
	if((fd = socket(AF_INET,SOCK_STREAM,0)) == -1)
	{
		perror("socket");
		exit(1);
	}
  	/*2.链接服务器*/
	bzero(&sin,sizeof(sin));
	sin.sin_family = AF_INET;
	sin.sin_port = htons(5001);
	sin.sin_addr.s_addr = inet_addr("192.168.1.3"); /*只适用于IPV4*/
	/*inet_pton(AF_INET,"127.0.0.1",(void *)&sin.sin_addr.s_addr);*/
	/*最后进行发送连接请求*/
	if(connect(fd,(struct sockaddr*)&sin,sizeof(sin)) < 0)
	{
		perror("connect");
		exit(1);
	}
 	
	/*3.读写*/
	ret = -1;
	char buf[255];
	while(1)
	{
		bzero(buf,255);
		if(fgets(buf,256,stdin) == NULL)
		{
			continue;
		}
		ret = write(fd,buf,strlen(buf));
		if(!strncasecmp(buf,"quit",4))
		{
			break;
		}
	}
	/*关闭套接字*/
	close(fd);
	return 0;
}

存在的问题

        在服务端read函数处是具有一个阻塞功能,当一个客户端连接服务器,服务器进行读的处理,程序一直阻塞在read处,如果又有一个客户端请求连接服务器,这时的服务器accept函数没有执行,所以不能连接心的客户端。这就意味着不能实现并发。改进方法,可以使用多线程,多进程编程等方法解决。

上一篇:相关的结构体


下一篇:直角三角形中三边的幂的关系