网络编程(一)

目录

网络编程

internet历史

TCP/IP协议

网络体系结构:

**TCP/IP体系结构:

***TCP 协议和UDP协议

1、tcp协议

2、udp协议

网络预备知识:

1、socket

2、套接字类型

3、IP地址

5、子网掩码

6、计算网络号

7、ip地址的转换

8、字节序:数据存储顺序

9、端口号

客户端

服务器

tcp服务器编程流程

数据交互

笔试面试:

1、网络粘包(网络风暴)

2、客户端连接服务器成功过程(三次过程)

3、客户端或者服务器断开时(四次挥手)

网络抓包

UDP服务器、客户端

服务器

客户端

linux中的IO模型

io多路复用

select()

poll()

设置套接字属性

广播和主播

tcp轮循服务器

udp轮循服务器

并发服务器

多进程

多线程

io多路复用

select

poll

广播

组播


网络编程

internet历史

1957年,前苏联卫星上天
1958年,米国建立ARPA(国防部高级研究计划署)
1968年,ARPA提出 资源共享计算机网络,简称ARPAnet,“阿帕网”。实现互联不同的计算机。
早期arpanet使用的网络控制协议NCP(network control procotol)
ncp不能互联不同类型的操作系统以及不同类型计算机,也没有纠错功能。

TCP/IP协议

internet中的世界语
TCP 协议:主要实现纠错能力:用来检测网络传输中的出错,并具备一定的纠错能力
IP协议:主要实现不同类型操作系统、计算机互联。
UDP协议:用户数据协议
ICMP:互联网控制信息协议
HTTP:超文本传输协议
ARP:地址解析协议

网络体系结构:

由于网络是非常复杂的,因此采用分而治之的方法涉及。

因此网络体系结构是指 网络的层次结构和每层使用的协议的集合。

OSI体系结构:七层模型,理想化的模型,尚未完全实现。

应用层:主要是一些应用程序,获得要传输的数据(qq键盘输入)          HTTP/FTP/DNS歇息 
表示层:对数据进程 格式定义、加密或者转换(格式定义、转换数据)      MD5 加密协议
会话层:建立通信进程的逻辑名字和 物理名字之间的联系。(端口号)       
传输层:提供可靠数据传输,差错处理及恢复(TCP)。流量控制(UDP)。   TCP/UDP
网络层:数据分组、路由选择。(数据分组筛选,由路由分发到目的)        IP/ICMP
数据链路层:数据组成可发送 和 接受的帧                            
物理层:传输物理信号等。
​
两路交换机:数据链路层的交换(硬件)
三路交换机:网络层是交换(软件)

**TCP/IP体系结构:

internet 事实上的工业标准
应用层:常用协议(http:超文本传输协议、ftp:文件传输、dns:域名解析协议、smtp:邮件传输协议)
传输层:确定数据包交给主机那个任务 :tcp 、udp
网络层:实现端对端的传输:ip(互联网络)、icmp(互联网络控制管理协议,--》ping命令)、igmp(广播、组播)
网络接口物理层:屏蔽硬件的差异,向上一层提供统一接口。ARP:地址解析协议(ip地址-》MAC地址)、RARP:逆地址解析协议(MAC地址-》IP地址)。
​
MSS(max data message ):最大报文长度:1460
MTU(max transmission unit):最大传输单元:1500
​

***TCP 协议和UDP协议

1、tcp协议

    传输控制协议,面向链接,数据传输安全可靠。(数据无失序、无重复、无丢失)。常用场景:对安全性要求较高的场合(密码传输):qq、微信的登陆与注册;大量数据传输
​
•       特点:由于面向链接,建立链接管道,因此数据传输效率相对较低

2、udp协议

    传输控制协议,无连接,保证数据安全可靠性。使用场景:少量图片数据传输;媒体流数据传输;qq、微信聊天;无线网络传输;广播、组播。
​
•       特点:由于无链接,不用建立管道,因此数据传输效率相对较高。
​
​

网络预备知识:

1、socket

1、是一种编程接口
2、也是一个文件描述符
3、可用于TCP、UDP、IPX

2、套接字类型

1、流式套接字:SOCK_STREAM:提供一种面向链接的服务,用于TCP传输控制协议
2、数据报套接字:SOCK_DGRAM:提供无连接服务,用于UDP通信
3、原始套接字:SOCK_RAW: 提供底层通信(IP、TCMP、IGMP)

3、IP地址

网络中标识主机的编号,常以点分形式存在。192.168.2.177
	IPV4地址:整个IP占32位,共四字节,每段8位表示(0~255)  
	IPV6地址:整个ip占128位
	IP地址由 网络号 + 主机号组成:192.168.2 + 177
			其中 ”2“ 表示网段号,也就是那个路由网络中(192.168.2.1,所有路由器主机号都是”1。网关
	
	网络IP分类:主要看IP(第一段)前8位组成
		A类网:0- 127,       		子网掩码:255.0.0.0
		B	:128-191,		 	 子网掩码:255.255.0.0
		C	:192-223,			 子网掩码:255.255.255.0
		D	:224-239,组播地址
		E	:240-255,保留测试地址
	

5、子网掩码

255.255.255.255

6、计算网络号

已知ip(c类网)
网络号:IP&(umask)192.168.2.177 &(255.255.255.0) =192.168.2
主机号:ip&(~umask)192.168.2.177 &(~(255.255.255.0)) =177

7、ip地址的转换

由于网络中只能识别二进制所以必须转换:将代码字符串的IP地址转换成 网络识别的二进制

主机字节序转网络字节序:
in_addr_t inet_addr(const char * strptr)
#include<arpa/inet.h>
strptr:代码中字符串ip(主机字节序的IP地址)
返回:
	返回网络字节序二进制首地址

网络字节序转主机字节序:
char* inet_ntoa(stuct in_addr inaddr)
	#include<arpa/inet.h>
	inaddr:主机字节序 IP地址
	返回值:
			主机字节序 IP字符串 首地址

8、字节序:数据存储顺序

小端序:低位存低地址 ,ubuntu采用小段
大端序:地位存高地址,网络采用大端序

9、端口号

在主机中标识处理网络数据的进程,也是进程的id号
1-1023:总所周知的端口号,用户一般不能使用
1024-49151:已登记的端口号,
49152-65535 动态端口号,

主机字节序转网络字节序:
	u_long htonl( int long )
	u_short htons(int post)
	
网络字节序转主机字节序
	u_long int ntohl( u_long hodtlong)
	u_short ntohs(u_short hostshort)
	

客户端

与用户进行数据交互的界面程序即上位机程序。

服务器

为客户端提供数据服务的后台软件。

tcp服务器编程流程

1、创建套接字(socket())
2、绑定 bind()
3、监听 listen()
4、接受连接 accept()
5、数据交互 recv()/send()  read()/write()
6、关闭套接字 close()

int socket(int domain, int type,int procotol)
		功能:打开网卡设备,设置套接字类型
domain:IP协议地址族,IPV4(AF_INET) , IPV6(AF_INET6)
type:套接字类型  SOCK_STREAM
procotol: 通常为 0
返回值:
	成功:返回套接字(网卡描述符)
	失败:-1
	
int bind(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
	功能:将IP地址、端口号与套接字进行绑定,将IP和端口共享到网络中。
socketfd:socket返回值(成功)
addr:结构体指针(通用协议地址结构体的首地址
		
			struct sockaddr {
               sa_family_t sa_family; //AF_INET
               char        sa_data[14];//
           		}
	internet协议地址结构体:
		  struct sockaddr_in {
               sa_family_t    sin_family;//AF_INET
               in_port_t      sin_port;  //网络字节序端口号
               struct in_addr sin_addr;   
           };   
           struct in_addr {
               uint32_t       s_addr;     //网络字节序ip地址
           };

	结构体头文件: #include<arpa/inet.h>

addrlen:结构体大小
        struct sockaddr_in addr;
addr.sin_fmily = AF_INET;
addr.sin_port = htons(9527);

INADDR_ANY(0.0.0.0)
addr.sin_addr.s_addr = inet_addr("0.0.0.0");



int listen(int sockfd,int backlog)
    功能:设置监听套接字,设置 客户端连接请求队列长度,也就是设置同一时刻能接受的最大请求数
    
    监听完成就是启动服务器,套接字变成监听套接字
    backlog:请求队列长度,在内核中开辟
    返回值:
    成功:0
    失败:-1

    
   
  int accpet(int sockfd,struct sockaddr* addr,socklen_t *len)
    功能:阻塞等待,接受客户端连接,建立通信管道
    如果 请求队列中没有客户端的请求,该函数就组设。有就立即接受连接,建立通道。
    
    
    

数据交互

函数原型:
				ssize_t read(int connfd, void *buf, size_t size);
				参数:
					connfd:连接套接字(通信管道id)
					
				ssize_t recv(int connfd, void *buf, size_t len, int flags);
				参数:
					flags:阻塞与非阻塞模式,通常0表示阻塞。
				
				注意:
					1、read读数据,默认为阻塞模式,如果读缓冲区有数据立即返回,无数据就等待
					2、recv读数据可以设置 模式
					3、当 连接断开时,read或recv立即返回 0值
					
				ssize_t write(int connfd, void *buf, size_t size);
				参数:
					connfd:连接套接字(通信管道id)
				
				ssize_t send(int connfd, void *buf, size_t len, int flags);
				参数:
					flags:阻塞与非阻塞模式,通常0表示阻塞。
                    
关闭套接字
			int close(int sockfd);
                    
客户端交互过程中,服务器意外结束,如果客户端继续发送数据,第一次成功,但二次会出现管道破裂终止程序运行。

笔试面试:

1、网络粘包(网络风暴)

数据的读写数据不匹配,就会导致多条数据包粘连在缓存区中。

调整读写速率
调整读取的每条数据包大小

2、客户端连接服务器成功过程(三次过程)

第一次:客户端向服务器发送 SYN=j 请求

第二次:服务器向客户端发 syn+ack

第三次:客户端向服务器 syn

为什么三次握手而不是两次:解决网络信道不可靠问题

3、客户端或者服务器断开时(四次挥手)

第一次挥手:客户端向 服务器发 fin
第二次挥手:服务器向客户端  fin 
第三次:服务器向客户端 ack
第四次:客户端向服务器 ack

数据交互中,客户端突然断电。服务器如何判断客户端情况。
心跳包机制(每隔一段时间服务器发送一个包,没回应)

网络抓包

wir

UDP服务器、客户端

服务器

1、创建套接字 

socket()

2、绑定 


bind()

while(1){
	3、接收数据 

	recvfrom()

	4、发送数据
	sendto()
}

5、关闭

客户端

1、创建套接字socket

2、绑定(可选)bind

while(1)
{
	3、发送 sendto()
	
	
	4、接受数据 recvfrom()


}

1、函数原型:
			ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
			功能:
				阻塞等待,接受数据,保存数据发送者的ip、端口信息
			头文件:
				#include <sys/types.h>
				#include <sys/socket.h>
			参数:
				sockfd	:套接字
				buf		:存储数据的缓冲区首地址
				len		:要读取的数据字节数
				flags	:0(阻塞模式)
				src_addr:保存数据发送端 ip和端口号的 结构体首地址
				addrlen	:存储结构体的长度
			返回值:
				成功:实际读取的字节数
				失败:-1,并设置错误信息
		2、函数原型:
			ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
			功能:
				将buf地址上的前len个字节数据,写入 sockfd网络中,
				发送到 dest_addr对应的目标主机中。
			头文件:
				#include <sys/types.h>
				#include <sys/socket.h>
			参数:
				sockfd	:套接字
				buf		:待发送的数据缓冲区首地址
				len		:要发送的数据字节数
				flags	:0(阻塞模式)
				dest_addr:目标主机的ip和port存储的结构体首地址
				addrlen	:结构体的长度
			返回值:
				成功:发送的实际字节数
				失败:-1,并设置错误信息
				
	

linux中的IO模型

1、阻塞io:最常用、最简单,但效率最低(read,write)
2、非阻塞io:防止进程阻塞在io操作上,但必须使用轮询机制(浪费cpu资源)
3、io多路复用,同时对多个io进行操作,效率比阻塞高。浪费的cpu资源比非阻塞少
4、信号驱动io

io多路复用

基本思想:
1、先构造一张有关文件描述符表
2、使用一个函数,检测该表中哪一个或多个文件描述符可进行 io操作,该函数就立即返回 该文件描述符的标志
3、根据该函数 返回的文件描述符,就可以进行io操作

select()

#include<stdio.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
	int fd = open("/dev/input/mouse0", O_RDONLY);
	if (fd < 0){
		perror("open");
		return -1;
	}
	//创建 文件描述符集合
	fd_set rfd1, rfd2;
	
    //清空集合
	FD_ZERO(&rfd1);
	//将带操作的文件描述符加入 集合中
	FD_SET(0, &rfd1);          //加入键盘的文件描述符
	FD_SET(fd, &rfd1);		   //加入鼠标的文件描述符
	
    int maxfd = fd + 1;
	while(1){
		// 由于select检测时,如果集合中有可读文件描述符,则该集合会被设置标志,立即返回(一次只能检测一个)
		// 使用完成不会自动清除标志,如果不做任何处理,则下次循环,就不会设置新的标志
		rfd2 = rfd1;   //使用新的结集合覆盖 集合中的所有数据。
		//select检测集合中哪个文件描述符可操作,原理:函数内部自动遍历 0~maxfd之间的所有文件描述符,加入集合的文件描述符才能返回标志
		// 第2参数:描述符可读的集合     
        //参数3:描述符可写集合,NULL表示不检测;
        //参数4:NULL不检测; 
        //参数5:NULL一直阻塞,0(不用)
		// select函数在参数 5 规定时间内检测集合中,是否有可操作文件描述符。
		// 如果没有,则阻塞;如果有则返回可操作描述符的数量,并设置标志
		// 如果时间到了,则返回 0
		int ret = select(maxfd, &rfd2, NULL, NULL, NULL);
		if(ret < 0){
			perror("select");
			continue;
		}
		printf("ret: %d\n", ret);
		if ( FD_ISSET(0, &rfd2) ){//检测判断 集合中被置位的文件描述符是谁(检测判断哪个文件描述符可操作)
			char buf[32] = {0};
			read(0, buf, sizeof(buf));
			printf("keyBoard: %s\n", buf);
		} else if( FD_ISSET(fd, &rfd2) ){
			char buf[32] = {0};
			read(fd, buf, sizeof(buf));
			printf("mouse: %s\n", buf);
		}
	}
    return 0;
}

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

int server_init(int port)
{
	//1、创建套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0){
		perror("socket");
		return -1;
	}
	printf("sockfd: %d\n", sockfd);
	//2、绑定
	struct sockaddr_in addr; //定义Internet协议地质结构体变量,用来存储ip和port
	addr.sin_family		= AF_INET;		//Internet 协议 IPV4
	addr.sin_port		= htons(port);	//端口号
										//addr.sin_addr.s_addr= inet_addr("0.0.0.0"); //0地址,绑定当前系统任意ip
	addr.sin_addr.s_addr= htonl(INADDR_ANY);
	if(  bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
		perror("bind");
		return -1;
	}
	//3、监听
	if( listen(sockfd, 5) < 0){
		perror("listen");
		return -1;
	}
	printf("server init success\nserver listening.....\n");
	return sockfd;
}
int waitForClient(int listenfd)
{
	struct sockaddr_in caddr;   //定义结构体变量,用来保存成功的客户端信息
	socklen_t len = sizeof(caddr);
	int connfd = accept(listenfd, (struct sockaddr *)&caddr, &len);
	if (connfd < 0){
		perror("accept");
		return -1;
	}
	printf("client ip: %s port: %u\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));
	return connfd;
}

int main(int argc, char *argv[])
{
	int sockfd = server_init(9526);
	if(sockfd < 0){
		return -1;
	}
	// 创建描述符集
	fd_set fd1, fd2;
	FD_ZERO(&fd1);
	FD_SET(sockfd, &fd1);

	int maxfd = sockfd;
	//4、接受连接
	while(1){
		fd2 = fd1;
		struct timeval val = {5, 0};
		if( select(maxfd + 1, &fd2, NULL, NULL, &val) <= 0){
			printf("select error or time out\n");
			continue;
		}
		for(int i = 0; i < maxfd+1; i++){ //将集合中0~maxfd中每个文件描述符都进行遍历判断
			if ( FD_ISSET(i, &fd2) ){ //判断fd= i,的文件是否可用
				if (i == sockfd){ //判断当前 集合中产生变化的 i是否是  sockfd
					int connfd = waitForClient( i );
					if(connfd < 0){
						continue;
					}
					FD_SET(connfd, &fd1);  //将客户端连接套接字加入集合,下次循环进行检测
					maxfd = maxfd > connfd ? maxfd : connfd;  //更新集合中最大的 文件描述符
				} else {	//只要集合中发生变化的不是 sockfd,那就一定是 connfd(连接套接字)
					char buf[32] = {0};
					int ret = read(i, buf, 32);
					if(ret < 0){
						perror("read");
						continue;
					} else if (ret == 0){
						printf("client leaved\n");
					} else {
						printf("recv: %s\n", buf);
					}
				}	
			}
		}
	}
	close(sockfd);
    return 0;
}

poll()

#include<stdio.h>
#include <sys/select.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <poll.h>
int main(int argc, char *argv[])
{
	int fd = open("/dev/input/mouse0", O_RDONLY);
	if (fd < 0)
	{
		perror("open");
		return -1;
	}
	//由于要检测的 文件描述符只有 2个,因此数组元素 2个
	struct pollfd pfd[2];

	//向数组元素中装填 要检测的文件描述符和 事件
	pfd[0].fd = 0;				//文件描述符
	pfd[0].events = POLLIN;      //带检测的事件:数据可读

	pfd[1].fd = fd;
	pfd[1].events = POLLIN;

	while(1){

		// 参数1:要检测的文件描述符和事件的结构体数组 首地址
		// 参数2:要检测的文件描述符个数
		// 参数3:超时设置, 单位 毫秒
		// 成功:返回 非负值(可操作文件描述的元素的个数);超时:返回 0; 失败:返回-1
		int ret = poll(pfd, 2, 5000);
		if(ret < 0){
			perror("poll");
			continue;
		} else if (ret == 0)
		{
			printf("time out\n");
			continue;
		}
		for(int i = 0; i < 2; i++)
		{
			if( pfd[i].revents & pfd[i].events )
			{ //判断数组中哪个元素的 revents 被置位
				if( pfd[i].fd == 0)
				{			  //判断数组中产生事件的元素中 文件描述符为 谁
					char buf[32] = {0};
					read(pfd[i].fd, buf, 32);
					printf("keyBoard: %s\n", buf);
				} else if ( pfd[i].fd == fd){
					char buf[32] = {0};
					read(pfd[i].fd, buf, 32);
					printf("mouse: %s\n", buf);
				}
			}
		}
	}
	return 0;
}

以太网头:14个字节:目标mac地址(6字节)、源mac地址(6字节),两字节(0x0800:ipv4,)。

ip头(20字节):源IP、目标ip。

tcp头(20字节):源端口、目标端口

设置套接字属性

setsockopt()

广播和主播

同时给局域网中所有主机发报叫广播。

tcp轮循服务器

#include<stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
int main(int argc, char *argv[])
{
	//1、创建套接字
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd < 0){
		perror("socket");
		return -1;
	}
	printf("sockfd: %d\n", sockfd);
	//2、绑定
	struct sockaddr_in addr; //定义Internet协议地质结构体变量,用来存储ip和port
	addr.sin_family		= AF_INET;		//Internet 协议 IPV4
	addr.sin_port		= htons(9526);	//端口号
										//addr.sin_addr.s_addr= inet_addr("0.0.0.0"); //0地址,绑定当前系统任意ip
	addr.sin_addr.s_addr= htonl(INADDR_ANY);
	if(  bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
		perror("bind");
		return -1;
	}
	//3、监听
	if( listen(sockfd, 5) < 0){
		perror("listen");
		return -1;
	}
	printf("server init success\nserver listening.....\n");

	//	while(1);

	//4、接受连接
	while(1){
		struct sockaddr_in caddr;   //定义结构体变量,用来保存成功的客户端信息
		socklen_t len = sizeof(caddr);
		int connfd = accept(sockfd, (struct sockaddr *)&caddr, &len);
		if (connfd < 0){
			perror("accept");
			return -1;
		}
		printf("client ip: %s port: %u\n", inet_ntoa(caddr.sin_addr), ntohs(caddr.sin_port));

		char buf[32] = {0};
		while(1){
#if 0
			bzero(buf, sizeof(buf));
			int ret = read(connfd, buf, sizeof(buf) );
			if (ret < 0){
				perror("read");
				return -1;
			} else if (ret == 0){
				printf("client is leaved\n");
				break;
			} else {
				printf("ret: %d %s\n", ret, buf);
				if( !strncmp(buf, "quit", 4) ){
					break;
				} else if ( !strncmp(buf, "exit", 4)){
					close(connfd);
					close(sockfd);
					return -1;
				}
			}
#endif
#if 0
			char buf[32] = {0};
			fgets(buf, 32, stdin);
			int ret = write(connfd, buf, strlen(buf)-1);
			printf("send: %d\n", ret);
#endif
			write(connfd, "hello world", 11);
		}
	}
	close(sockfd);
    	return 0;
}

udp轮循服务器

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<arpa/inet.h>

int main(int argc, char *argv[])
{
	int fd = socket(AF_INET,SOCK_DGRAM,0); 
	if(-1 == fd)
	{
		perror("socket:");
		return -1;		
	}

	struct sockaddr_in addr;
	addr.sin_port = htons(atoi(argv[1]));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr("0.0.0.0");
	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0)
	{
		perror("bind:");
		return -1;
	}

	while(1)
	{
		struct sockaddr_in caddr;
		socklen_t len = sizeof(caddr);
		char buf[130] = {0};
		int ret = recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
		if(ret == - 1)
		{
			perror("recvfrom:");
			return -1;
		}else{
			printf("information from : ip= %s, post =%d, ret = %d, buf = %s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),ret,buf);
			if(strncmp(buf,"quit",4) == 0)
			{
				printf("client quit\n");
			}else if(strncmp(buf,"exit",4) == 0)
			{
				close(fd);
				return 0;
			}
		}
		sendto(fd,buf,strlen(buf),0,(struct sockaddr*)&caddr,len);
	}
	return 0;
}

并发服务器

多进程

#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>


int accept_client(int fd);//接收客户端连接 
int init_socket(char *port);//初始化套接字
void handler(int no);//信号处理函数   
int talk_to_client(int fd1);//接收客户端发出的信息
int main(int argc, char* argv[])
{
	int fd1,pid,ret;
	int fd = init_socket(argv[1]);
	if(fd < 0)
	{
		printf("init_socket failed.\n");
		return -1;
	}

	signal(SIGCHLD,handler);//捕捉自进程信号,进行处理 

	while(1)
	{
		fd1 = accept_client(fd);
		if(fd1 == -1)
		{
			return -1;
		}
		pid = fork();
		if(pid  == -1)
		{
			perror("fork:");
		}else if(pid == 0 )
		{
			talk_to_client(fd1);
			exit(0);
		}
	}
		close(fd);
		exit(0);
		return 0;
}

int init_socket(char *port)//初始化套接字
{
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == fd)
	{
		perror("socket:");
		return -1;
	}else{
		printf("socket success.\n");
	}

	struct sockaddr_in addr;
	addr.sin_family 	= AF_INET;
	addr.sin_port 		= htons(atoi(port));
	addr.sin_addr.s_addr    = inet_addr("0.0.0.0");

	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
	{
		perror("bind:");
		return -1;
	}else{
		printf("bind success.\n");
	}

	if(listen(fd,13) < 0)
	{
		perror("listen:");
		return -1;
	}else{
		printf("listen........\n");
	}
	return fd;
}


int accept_client(int fd)//接收客户端连接 
{
	struct sockaddr_in addr;
	socklen_t len;
	int acce_fd = accept(fd,(struct sockaddr*)&addr,&len);
	if(acce_fd == -1)
	{
		perror("accept:");
		return -1;
	}else{
		printf("accept success.\n");
		printf("ip:%s,port:%d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
	}
	return acce_fd;
}
void handler(int no)//信号处理函数  
{

	int pid = waitpid(-1,NULL,WNOHANG);//非阻塞等待子进程推出 
	if(-1 == pid)
	{
		perror("waitpid :");
	}else{
		printf("signal: child quit,pid:%d\n",pid);
	}
}
int talk_to_client(int fd1)
{
	char buf[128] = {0};	
	int ret;
	while(1)
	{
		ret = recv(fd1,buf,sizeof(buf),0);
		if(ret == -1)
		{
			perror("recv:");
			 break;
		}else if(ret == 0){
			printf("client error quit");
			break;
		}else{
			printf("recv:%s\n",buf);
			if(strncmp(buf,"quit",4) == 0)
				break;
		}
	}
	close(fd1);
	exit(-1);
}

多线程

#include<sys/types.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<pthread.h>


int accept_client(int fd);//接收客户端连接 
int init_socket(char *port);//初始化套接字
void handler(int no);//信号处理函数   
void* talk_to_client(void* fd1);//接收客户端发出的信息
int main(int argc, char* argv[])
{
	int fd1,pid,ret;
	int fd = init_socket(argv[1]);
	if(fd < 0)
	{
		printf("init_socket failed.\n");
		return -1;
	}else{
		printf("fd = %d\n",fd);
	}

//	signal(SIGCHLD,handler);//捕捉自进程信号,进行处理 

	while(1)
	{
		fd1 = accept_client(fd);
		if(fd1 == -1)
		{
			 break;
		}
		pthread_t tid;
		ret = pthread_create(&tid,NULL,talk_to_client,(void *)&fd1);
		if(ret < 0)
		{
			perror("pthread_create:");
			break;
		}
		pthread_detach(tid);
	}
	close(fd);
	return 0;
}

int init_socket(char *port)//初始化套接字
{
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(-1 == fd)
	{
		perror("socket:");
		return -1;
	}else{
		printf("socket success.\n");
	}

	struct sockaddr_in addr;
	addr.sin_family 	= AF_INET;
	addr.sin_port 		= htons(atoi(port));
	addr.sin_addr.s_addr    = inet_addr("0.0.0.0");

	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
	{
		perror("bind:");
		return -1;
	}else{
		printf("bind success.\n");
	}

	if(listen(fd,13) < 0)
	{
		perror("listen:");
		return -1;
	}else{
		printf("listen........\n");
	}
	return fd;
}


int accept_client(int fd)//接收客户端连接 
{
	printf("fd :%d\n",fd);
	struct sockaddr_in addr;
	socklen_t len = sizeof(addr);
	int acce_fd = accept(fd,(struct sockaddr*)&addr,&len);
	if(acce_fd == -1)
	{
		perror("accept:");
		return -1;
	}else{
		printf("accept success.\n");
		printf("ip:%s,port:%d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));
	}
	return acce_fd;
}
void handler(int no)//信号处理函数  
{

	int pid = waitpid(-1,NULL,WNOHANG);//非阻塞等待子进程推出 
	if(-1 == pid)
	{
		perror("waitpid :");
	}else{
		printf("signal: child quit,pid:%d\n",pid);
	}
}
void* talk_to_client(void* fd1)
{
	int ret;
	int fd = *(int*)fd1;
	while(1)
	{
	char buf[128] = {0};	
		ret = read(fd,buf,sizeof(buf));
		if(ret == -1)
		{
			perror("recv:");
			break;
		}else if(ret == 0){
			printf("client error quit");
			break;
		}else{
			printf("recv:%s\n",buf);	
		}
	}
	close(fd);
	pthread_exit(NULL);
}

io多路复用

select

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<string.h>
#include<stdlib.h>




int init_sock(char* port);//初始化套接字.

int accpet_fd(int fd);//接受链接

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

	int fd = init_sock(argv[1]);
	if(fd == -1)
	{
		printf("init_sock failed.\n");
		return -1;
	}

	fd_set fid1,fid2;//创建两个文描述表

	FD_ZERO(&fid1);//第一个文件描述符表清空
	FD_SET(fd,&fid1);//套接字加入文件描述符

	int maxfd = fd;
	//struct timeval val = {5};//时间结构赋值为5秒
	while(1)
	{
		fid2 = fid1;
	struct timeval val = {5};//时间结构赋值为5秒
		ret =  select(maxfd+1,&fid2,NULL,NULL,&val); 
		if(ret == -1)
		{
			perror("select:");
			return -1;
		}else if(ret == 0)
		{
			printf("time out\n");

		}else{
			printf("select_ret= %d\n",ret);
		}

		for(int i =0 ; i <= maxfd; i++ )
		{

			if(FD_ISSET(i,&fid2))//检测可用文件描述符是否是i
			{
				if(i == fd)//判断文件描述符是否是套接字
				{
					int connetc_fd = accpet_fd(fd);
					if(connetc_fd == -1)
					{
						printf("accpet_fd failed\n");
					}else{
						FD_SET(connetc_fd,&fid1);//将新客户端加入文件集合
						maxfd = (maxfd > connetc_fd)?maxfd:connetc_fd;
					}
					printf("i = %d, maxfd= %d\n",i,maxfd);
					break;
				}else{//不是套接字,就需要读取
					char buf[128] = {0};
					 ret = recv(i,buf,sizeof(buf),0);
					if(ret == -1)
					{
						perror("recv:");
					}else if(ret == 0)
					{
						printf("client error quit:\n");
						FD_CLR(i,&fid1);
						close(i);
						maxfd = (maxfd > i)?maxfd:i;

					}else{
						printf("ret:%d, recv:%s\n",ret,buf);
						if(strncmp(buf,"quit",4) == 0)
						{
							printf("clientd quit\n");
							FD_CLR(i,&fid1);
							close(i);
							maxfd = (maxfd > i)?maxfd:i;
						}else if(strncmp(buf,"exit",4) == 0)
						{
							printf("service quit\n");
							return 0;
						}
					}
					break;
				}
			}
		}
	}

	return 0;
}

int init_sock(char* port)//初始化套接字.
{
	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd == -1)
	{
		perror("socket:");
		return -1;
	}else{
		printf("socket success.\n");
	}

	struct sockaddr_in addr;
	addr.sin_addr.s_addr 	= inet_addr("0.0.0.0");
	addr.sin_family 	= AF_INET;
	addr.sin_port 		= htons (atoi(port));
	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) == -1)
	{
		perror("bind");
		return -1;
	}else{

		printf("bind success\n");
	}

	if(listen(fd,13) == -1 )
	{
		perror("listen");
		return -1;
	}else{

		printf("listen success.\n");
	}
	return fd;
}


int accpet_fd(int fd)
{
	struct sockaddr_in addr;

	socklen_t len ;
	int fd_accept = accept(fd,(struct sockaddr*)&addr,&len);
	if(fd_accept  == -1)
	{
		perror("accept:");
		return -1;
	}else{
		printf("accept success.\n");
	}


	return fd_accept;

}

poll

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include<unistd.h>
#include<poll.h>
#include<string.h>
#include<stdlib.h>

int init_socket(char *port);//初始化套接字
int accept_fd(int fd);//等待接受链接

int main(int argc, char* argv[])
{
	int ret,i,con_fd;
	int connect = 1;
	struct pollfd fid[13] ;
	char buf[123] = {0};
	
	int fd = init_socket(argv[1]);
	if(fd == -1)
	{
		printf("init_socket failed.\n");
		return -1;
	}

	fid[0].events 	= POLLIN;
	fid[0].fd 	= fd; 
	
	while(1)
	{
		ret = poll(fid,connect,3000);//检测是否有文件描述符可用
		if(ret == -1)
		{
			perror("poll:");
			return -1;
		}else if(ret == 0 )
		{
			printf("time out\n");
		}else{
			printf("poll_ret = %d\n",ret);
		}

		for( i=0 ; i < connect; i++ )
		{
			if(fid[i].revents & POLLIN)//检测第i个文件的revents是否被置位 ,并自动还原置位以便下次检测 
			{
				if(fid[i].fd == fd)
				{
					 con_fd = accept_fd(fd);
					if(con_fd == -1)
					{
						printf("accept_fd failed\n");
						return -1;
					}else{
						fid[connect].fd = con_fd;//新的客户端加入数组 
						fid[connect].events = POLLIN;
						connect++;
					}
					
				}else{
					bzero(buf,sizeof(buf));

					ret = read(fid[i].fd,buf,sizeof(buf));
					if(ret == -1)
					{
						perror("read:");
						continue;
					}else if(ret == 0)
					{
						printf("client error quit\n");
						continue;
					}else{
					
						printf("red:%s\n",buf);
					}
				}
			}
		}
	}

	return 0;
}

int init_socket(char *port)//初始化套接字
{

	int fd = socket(AF_INET,SOCK_STREAM,0);
	if(fd == -1)
	{
		perror("socket:");
		return -1;
	}else{
		printf("socket success.\n");
	}

	struct sockaddr_in addr;
	addr.sin_family 	= AF_INET;
	addr.sin_port           = htons(atoi(port));
	addr.sin_addr.s_addr    = inet_addr("0.0.0.0");

	if(bind(fd,(struct sockaddr*)&addr,sizeof(addr)) == -1)
	{
		perror("bind");
		return -1;
	}else{
		printf("bind success.\n");
	}

	if(listen(fd,13) == -1)
	{
		perror("listen:");	
	}else{
	
		printf("listen.....\n");
	}

	return fd;
}



int accept_fd(int fd)
{

	struct sockaddr_in addr;

	socklen_t len;
	int fd1 = accept(fd,(struct sockaddr*)&addr,&len);
	if(fd == -1)
	{
		perror("acept:");
		return -1;
	}

	return fd1;
}

广播、组播只能用UDP

广播

接收

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>

#include <netinet/in.h>
#include <arpa/inet.h>


int main(int argc, char* argv[])
{
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket:");
		return -1;
	}
	struct sockaddr_in addr;
	addr.sin_family 	= AF_INET;
        addr.sin_addr.s_addr    = inet_addr("192.168.2.255");	
	addr.sin_port 		= htons(8888);
	
	if(bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
	{
		perror("bind:");
		return -1;
	}

	struct sockaddr_in caddr;
	socklen_t len = sizeof(caddr);
	while(1)
	{
		char buf[128] = {0};
	int ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
	if(ret == -1)
	{
		perror("recvfrom:");
		return -1;
	}
	printf("ip:%s,port:%d,recv:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buf);


	}


	return 0;
}

发送

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>



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

	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket:");
		return -1;
	}


	int on = 1;
	if(setsockopt(socketfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)) < 0)
	{
		perror("setsockopt:");
		return -1;
	}
	struct sockaddr_in addr;
	addr.sin_family  	= AF_INET;
	addr.sin_port		= htons(8888);
	addr.sin_addr.s_addr 	= inet_addr("192.168.2.255"); 

	while(1)
	{
	
		char buf[128] = {0};
		fgets(buf,sizeof(buf),stdin);
		if(sendto(socketfd,buf,strlen(buf)-1,0,(struct sockaddr*)&addr,sizeof(addr))<0)
		{
			perror("sedto");
		}
	}



	return 0;
}

组播

接收

#include<stdio.h>
#include<sys/socket.h>
#include<sys/types.h>

#include <netinet/in.h>
#include <arpa/inet.h>


int main(int argc, char* argv[])
{
	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket:");
		return -1;
	}
	struct sockaddr_in addr;
	addr.sin_family 	= AF_INET;
        addr.sin_addr.s_addr    = inet_addr("192.168.2.255");	
	addr.sin_port 		= htons(8888);
	
	if(bind(socketfd,(struct sockaddr*)&addr,sizeof(addr)) < 0 )
	{
		perror("bind:");
		return -1;
	}

	struct sockaddr_in caddr;
	socklen_t len = sizeof(caddr);
	while(1)
	{
		char buf[128] = {0};
	int ret = recvfrom(socketfd,buf,sizeof(buf),0,(struct sockaddr*)&caddr,&len);
	if(ret == -1)
	{
		perror("recvfrom:");
		return -1;
	}
	            printf("ip:%s,port:%d,recv:%s\n",inet_ntoa(caddr.sin_addr),ntohs(caddr.sin_port),buf);

	}

	return 0;
}

发送

#include<stdio.h>
#include<sys/socket.h>
#include<string.h>
#include<arpa/inet.h>

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

	int socketfd = socket(AF_INET,SOCK_DGRAM,0);
	if(socketfd == -1)
	{
		perror("socket:");
		return -1;
	}

	int on = 1;
	if(setsockopt(socketfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on)) < 0)
	{
		perror("setsockopt:");
		return -1;
	}
	struct sockaddr_in addr;
	addr.sin_family  	= AF_INET;
	addr.sin_port		= htons(8888);
	addr.sin_addr.s_addr 	= inet_addr("192.168.2.255"); 

	while(1)
	{
	
		char buf[128] = {0};
		fgets(buf,sizeof(buf),stdin);
		if(sendto(socketfd,buf,strlen(buf)-1,0,(struct sockaddr*)&addr,sizeof(addr))<0)
		{
			perror("sedto");
		}
	}

	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值