Linux系统编程-网络通信

目录

一、相关概念

TCP与UDP的区别:

端口号作用

字节序

二、Socket服务器与客户端的开发步骤

三、API 

四、实现双方聊天 

五、实现多方聊天 


一、相关概念

地址:IP地址和端口号

数据(数据格式):协议(HTTP/TCP/UDP)

socket(套接字):TCP/UDP

TCP与UDP的区别:

  • TCP:面向连接,A与B打电话,可靠,适用精度高。
  • UDP:面向报文,A与B发短信,不可靠,适用数据大

①TCP面向连接(如打电话之前先拨号建立连接);UDP是无连接的,即发送数据之前,不需要建立连接。

②TCP提供可靠服务。通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,不保证可靠交付。

③ TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议)。

④每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一,多对多的交互通信。

⑤TCP首部开销20字节;UDP首部开销小,只有8字节。

⑥TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。

端口号作用

一台拥有IP地址的主机可以提供许多服务,比如Web服务,FTP服务,SMTP服务等。主机是通过“IP地址+端口号”来区分不同的服务的

字节序

字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。

常见序:

Little endian:小端字节序,将低序字节存储在起始地址

Big endian:大端字节序,将高序字节存储在起始地址

网络字节序=大端字节序

例:0x01020304放入4000-4003与0x1234abcd放入0x0000-0x0003的存储

地址Big endianLittle endian          地址Big endianLittle endian
40000x010x040x00000x120xcd
40010x020x030x00010x340xab
40020x030x020x00020xab0x34
40030x040x010x00030xcd

0x12

二、Socket服务器与客户端的开发步骤

  1. 创建套接字
  2. 为套接字添加信息(IP地 址和端口号)
  3. 监听网络连接
  4. 监听有客户端接入,接受一个连接
  5. 数据交互
  6. 关闭套接字,断开连接

三、API 

1、连接协议(TCP/UDP)

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

返回值:socket描述符  sockfd

参数说明:

①domain:指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)

  • AF_INET IPv4因特网域
  • AF_INET6 IPv6因特网域
  • AF_UNIX Unix域
  • AF_ROUTE 路由套接字
  • AF_KEY 密钥套接字
  • AF_UNSPEC 未指定

②type:指定socket的类型

  • SOCK_STREAM:流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
  • SOCK_DGRAM:数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。
  • SOCK_RAW:允许程序使用低层协议如IP或ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议的开发。

③protocol:通常赋值‘0’

  • 0 选择type类型对应的默认协议
  • IPPROTO_TCP TCP传输协议
  • IPPROTO_TCP UDP传输协议 
  • IPPROTO_SCTP SCTP传输协议 
  • IPPROTO_TIPC TIPC传输协议

2、为套接字添加信息

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

用于绑定IP地址和端口号到sockfd

参数说明:

①sockfd:socket描述符

②addr:一个指向包含有本机IP地址和端口号等信息的sockaddr类型的指针,指向要绑定给sockfd的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同

例IPv4:

struct sockaddr{
    unsigned short sa_family;   //协议族
    char sa_data[14];           //IP+端口号
};

同等替换(此种常用):
struct sockaddr_in{
    sa_family_t      sin_family;  //协议族
    in_port_t        sin_port;    //端口号
    struct in_addr   sin_addr;    //IP地址结构体
    unsigned char    sin_zero[8]; //填充,无实际意义,为和sockaddr结构在内存中对齐,这样才能转换
}

3、地址转化API

把字符串形式的“192.168.1.1”转换为网络能识别的格式: 

int inet_aton(const char* straddr,struct in_addr* addrp);

把网络格式的ip地址转换为字符串格式:

char* inet_ntoa(struct in_addr inaddr);

4、监听

int listen(int sockfd, int backlog);

参数说明:

①sockfd:socket描述符

②backlog:指定在请求队列中允许的最大请求数

5、连接

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

参数说明:

①sockfd:socket描述符

②addr:客户端的协议地址

③addrled:客户端地址长度

返回值:

返回已连接的套接字描述符

6、数据收发

size_t send(int s, const void *msg.size_t len, int flags);

参数说明:

①s:套接字

②msg:待发数据

③len:数据长度

④flags:控制选项,一般设置为0

size_t recv(int s, void *buf.size_t len, int flags);

参数说明:

①s:套接字

②buf:接收缓冲区

③len:数据长度

④flags:控制选项,一般设置为0

7、客户端连接函数

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

参数说明:

①sockfd:目的服务器的socket描述符

②addr:服务器端的IP地址和端口号的地址结构指针

③addrlen:地址长度,常被设置为sizeof(struct sockaddr)

返回值:

成功返回0,遇到错误时返回-1,并且error中包含相应的错误码

8、字节序转换API

 #include <netinet/in.h>

uint16_t htons(uint16_t host16bitvalue);  //返回网络字节序的值

代码实现:

服务端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int s_fd;//socket标识符
	int n_read;//接收的内容
	char readbuf[128];//数据存放缓冲区
	char *msg = "I get your connect\n";//回复内容
	struct sockaddr_in s_addr;//发送结构体
	struct sockaddr_in c_addr;//接收结构体
	
	memset(&s_addr,0,sizeof(struct sockaddr_in));//开辟空间
	memset(&c_addr,0,sizeof(struct sockaddr_in));	

	//1.socket
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		perror("socket");
		exit(-1);
	}	
	//2.bind
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(8888);
	inet_aton("192.168.2.3",&s_addr.sin_addr);//转换为网络格式

	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
	//3.listen
	listen(s_fd,10);
	//4.accep
	int c_len = sizeof(struct sockaddr_in);
	int c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
	if(c_fd == -1){
		perror("accept");
	}
	printf("connect\n");
	printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
	//5.read
	n_read = read(c_fd,readbuf,128);
	if(n_read == -1){
		perror("read\n");
	}else{
		printf("get message:%d,%s\n",n_read,readbuf);
	}
	//6.recv
	write(c_fd,msg,strlen(msg));

	return 0;
}

 ①先用另一个终端中用telnet连接:

服务器:

connect
get connect:192.168.2.3
get message:5,www

另一个终端:

telnet 192.168.2.3 8888
Trying 192.168.2.3...
Connected to 192.168.2.3.
Escape character is '^]'.
www
I get your connect
Connection closed by foreign host.

 客户端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main()
{
	int c_fd;
	int n_read;
	char readbuf[128];
	char *msg = "msg from client\n";
	struct sockaddr_in c_addr;
	
	memset(&c_addr,0,sizeof(struct sockaddr_in));	

	//1.socket
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1){
		perror("socket");
		exit(-1);
	}
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(8888);
	inet_aton("192.168.2.3",&c_addr.sin_addr);
	//2.connect
	if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
		perror("connect");
		exit(-1);	
	}
	//3.send
	write(c_fd,msg,strlen(msg));
	//4.read
	n_read = read(c_fd,readbuf,128);
	if(n_read == -1){
		perror("read\n");
	}else{
		printf("get message:%d,%s\n",n_read,readbuf);
	}

	return 0;
}

②服务器和客户端连接

服务器:

connect
get connect:192.168.2.3
get message:16,msg from client

客户端:

get message:19,I get your connect 

四、实现双方聊天 

服务器端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
	int s_fd;
	int c_fd;
	int n_read;
	char readbuf[128];
	//char *msg = "I get your connect\n";
	char msg[128] ={0};
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;

	if(argc != 3){
		printf("param is not good\n");
		exit(-1);
	}	
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));	

	//1.socket
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		perror("socket");
		exit(-1);
	}	
	//2.bind
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&s_addr.sin_addr);

	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
	//3.listen
	listen(s_fd,10);
	//4.accep
	int c_len = sizeof(struct sockaddr_in);
	while(1){
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
		if(c_fd == -1){
			perror("accept");
		}
		printf("connect\n");
		printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
	
		if(fork() == 0){
			
			if(fork() == 0){			
				while(1){
					memset(msg,0,sizeof(msg));
					printf("input: ");
					gets(msg);	
					write(c_fd,msg,strlen(msg));
				}
			}
			//5.read
			while(1){
				memset(readbuf,0,sizeof(readbuf));
				n_read = read(c_fd,readbuf,128);
				if(n_read == -1){
					perror("read\n");
				}else{
					printf("get message:%d,%s\n",n_read,readbuf);
				}
			}
			//6.recv
			break;
		}
	}
	return 0;
}

客户端:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
	int c_fd;
	int n_read;
	char readbuf[128];
	//char *msg = "msg from client\n";
	char msg[128] = {0};
	struct sockaddr_in c_addr;

	if(argc != 3){
		printf("param is not good\n");
		exit(-1);
	}	
	memset(&c_addr,0,sizeof(struct sockaddr_in));	

	//1.socket
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1){
		perror("socket");
		exit(-1);
	}
	c_addr.sin_family = AF_INET;
	c_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&c_addr.sin_addr);
	//2.connect
	if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){
		perror("connect");
		exit(-1);	
	}
	
	while(1){
		if(fork() == 0){
			while(1){
				//3.send
				memset(msg,0,sizeof(msg));
				printf("input: ");
				gets(msg);
				write(c_fd,msg,strlen(msg));
			}
		}
		while(1){
			memset(readbuf,0,sizeof(readbuf));
			//4.read
			n_read = read(c_fd,readbuf,128);
			if(n_read == -1){
				perror("read\n");
			}else{
				printf("get message:%d,%s\n",n_read,readbuf);
			}
		}
	}
	return 0;
}

五、实现多方聊天 

在服务器端加入mark:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv)
{
	int s_fd;
	int c_fd;
	int n_read;
	int mark = 0;
	char readbuf[128];
	//char *msg = "I get your connect\n";
	char msg[128] ={0};
	struct sockaddr_in s_addr;
	struct sockaddr_in c_addr;

	if(argc != 3){
		printf("param is not good\n");
		exit(-1);
	}	
	memset(&s_addr,0,sizeof(struct sockaddr_in));
	memset(&c_addr,0,sizeof(struct sockaddr_in));	

	//1.socket
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1){
		perror("socket");
		exit(-1);
	}	
	//2.bind
	s_addr.sin_family = AF_INET;
	s_addr.sin_port = htons(atoi(argv[2]));
	inet_aton(argv[1],&s_addr.sin_addr);

	bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));
	//3.listen
	listen(s_fd,10);
	//4.accep
	int c_len = sizeof(struct sockaddr_in);
	while(1){
		c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&c_len);
		if(c_fd == -1){
			perror("accept");
		}
		mark++;
		printf("connect\n");
		printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));
	
		if(fork() == 0){
			
			if(fork() == 0){			
				while(1){
					sprintf(msg,"No.%d client",mark);
					write(c_fd,msg,strlen(msg));
					sleep(3);
				}
			}
			//5.read
			while(1){
				memset(readbuf,0,sizeof(readbuf));
				n_read = read(c_fd,readbuf,128);
				if(n_read == -1){
					perror("read\n");
				}else{
					printf("get message:%d,%s\n",n_read,readbuf);
				}
			}
			//6.recv
			break;
		}
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值