【linux网络编程socket套接字】

linux网络编程socket套接字


前言

本章分享linux下的网络编程(socket)套接字,如何编写一个简易的服务端和客户端,并实现双方通信。


提示:以下是本篇文章正文内容,下面案例可供参考
3

一、网络编程概述

1)TCP/UDP对比
1.TCP面向连接(如打电话要先拨号建立连接),UDP是无连接的,即发送数据之前不需要建立连接。
2.TCP提供可靠的服务也就是说通过TCP连接传送的数据,无差错,不丢失,不重复,并且按序到达,UDP尽最大努力交付,即不保证可靠交付。
3.TCP面向字节流,实际上是TCP把数据看成一串无结构的字节流;UDP是面向报文的UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速度降低(对实时应用很有用,如IP电话,实时视频会议等)。
4.每一条TCP连接只能是点到点的;UDP支持一对多,一对一,和多对一,多对多的交互通信。
5.TCP首部开销20字节;UDP首部开销小只有8个字节。
6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。
(二)端口号的作用
一台用于IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。
这些服务完全可以提供1个IP地址来实现。那么主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因此IP地址与网络服务的关系是一对多的关系。
实际上是通过IP地址+端口号来区分不同的额服务的。端口提供了一种访问通道。
服务器一般都是通过知名端口号来识别的,比如:对于每一个TCP/IP实现来说FTP端口号都是21,每一个TeLnet服务器的TCP端口号都是23号,每一个TFTP(简单文件传送协议)服务器的UDP端口号都是69。
二、字节序
Little endian:将低序字节存储在起始地址 (小端模式)
Big endian:将高序字节存储在起始地址 (大端模式)
网络字节序=大端字节序

二、socket编程步骤

基于socket的网络编程对于服务端和客户端的实现有以下简单步骤:
2.1 服务端的编程
1、创建套接字
2、绑定
3、监听
4、接收
5、读写操作
6、关闭

2.2 客户端的编程
1、创建套接字
2、连接
3、读写操作
4、关闭
各函数的使用同样我们还是通过man手册来查看,每个函数的具体参数也不介绍了,使用的时候看看man 手册中的介绍就知道了。

各操作函数原型如下:
//创建套接字
 int socket(int domain, int type, int protocol);
 //绑定
 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
 //监听
 int listen(int sockfd, int backlog);
 //接收
 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 //读数据
 ssize_t read(int fd, void *buf, size_t count);

 //写数据
 ssize_t write(int fd, const void *buf, size_t count);
 //关闭
 int close(int fd);
 //连接
 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);


note:以上就是在编程中需要用到的一些函数。

三、socket编程服务端、客户端的实现

在进行编程时我们有以下需要注意的:
1、在进行socket结构体成员配置时 我们是以struct sockaddr_in这个优化结构体进行配置,而这个原本使用的是struct sockaddr 所以在函数中进行传参是需要我们进行强制类型转换成(struct sockaddr *)类型
2、进行查找小技巧
进入 /usr/include 下进行相关查找
譬如:我们想要查看struct sockaddr_in 中的结构体成员
grep “struct sockaddr_in {” * -nir 通过grep管道进行字符串的查看
3、网络字节格式相关转换

主机转网络
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地址之间的转换
int inet_aton(const char *cp, struct in_addr *inp);
char *inet_ntoa(struct in_addr in);

在这里插入图片描述

在这里插入图片描述

下面进入编程正题吧

3.1 服务端实现

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


//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换  地址转网络 htons inet_aton


/*-------------------------------------服务端-------------------------------------*/
int main(void)
{
    
    
	char readbuf[128] = {
    
    0}; //接收数据
	char writebuf[128] = "message from server"; //发送数据
	int s_fd = 0,c_fd = 0;
	int n_read = 0,size = 0;
	struct sockaddr_in server,client;
	//创建套接字
	//  int socket(int domain, int type, int protocol);
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1)
	{
    
    
		printf("socket failed\n");
		exit(-1);
	}

	size = sizeof(struct sockaddr);
	//清空
	memset(&server,0,sizeof(struct sockaddr_in));
	memset(&client,0,sizeof(struct sockaddr_in));
	//结构体初始化
	server.sin_family = AF_INET;
	server.sin_port = htons(8080);
	// int inet_aton(const char *cp, struct in_addr *inp);
	inet_aton("127.0.0.1",&server.sin_addr);

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

	if(bind(s_fd,(struct sockaddr *)&server,sizeof(struct sockaddr_in)) == -1)
	{
    
    
		perror("bind:");
		exit(-1);
	}

	//  int listen(int sockfd, int backlog);
	//监听
	iif(listen(s_fd,10) == -1)
	{
    
    
		perror("listen:");
		exit(-1);
	}

	// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	//接收
	c_fd = accept(s_fd,(struct sockaddr *)&client,&size);
	if(c_fd == -1)
	{
    
    
		printf("accept failed\n");
		exit(-1);
	}
	printf("connect scuuess client ip:%s\n",inet_ntoa(server.sin_addr));


	//读写操作
	//       ssize_t read(int fd, void *buf, size_t count);
	n_read = read(c_fd,readbuf,sizeof(readbuf));
	if(n_read == -1)
	{
    
    
		printf("read failed\n");
		exit(-1);
	}
	else
	{
    
    
		printf("data:%s\n",readbuf);
	}
	write(c_fd,writebuf,sizeof(writebuf));
	//关闭
	close(s_fd);
	close(c_fd);

	return 0;
}

3.2 客户端实现

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


//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换  地址转网络 htons inet_aton


/*----------------------------------------客户端-------------------------------------*/
int main(void)
{
    
    
	char readbuf[128] = {
    
    0}; //接收数据
	char writebuf[128] = "message from client"; //发送数据
	int c_fd = 0,ret = 0;
	struct sockaddr_in client;
	//创建套接字
	//  int socket(int domain, int type, int protocol);
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1)
	{
    
    
		printf("socket failed\n");
		exit(-1);
	}

	//清空
	memset(&client,0,sizeof(struct sockaddr_in));
	//结构体初始化
	client.sin_family = AF_INET;
	client.sin_port = htons(8080);
	// int inet_aton(const char *cp, struct in_addr *inp);
	inet_aton("127.0.0.1",&client.sin_addr);

	//连接 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
	if(connect(c_fd,(struct sockaddr *)&client,sizeof(struct sockaddr)) == -1) 
	{
    
    
		perror("connect:");
		exit(-1);
	}
	printf("connect success current ip:%s\n",inet_ntoa(client.sin_addr));

	//读写操作
	//       ssize_t read(int fd, void *buf, size_t count);
	
	write(c_fd,writebuf,sizeof(writebuf));
	read(c_fd,readbuf,sizeof(readbuf));
	printf("data:%s\n",readbuf);
	//关闭
	close(c_fd);

	return 0;
}

3.3 实验结果

在这里插入图片描述
在这里插入图片描述

四、socket编程实现多方消息收发

上面实验中实现了简单的收发操作但时不灵活,ip和端口写的太固定并且还没有实现连续收发的效果,下面实验进行优化实现通过用户自己输入ip和端口 支持多发消息的收发。

4.1 服务端编写

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


//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换  地址转网络 htons inet_aton


/*-------------------------------------服务端-------------------------------------*/
int main(int argc,char *argv[])
{
    
    
	char readbuf[128] = {
    
    0}; //接收数据
	char writebuf[128] = {
    
    0}; //发送数据
	int s_fd = 0,c_fd = 0;
	int n_read = 0,size = 0;
	struct sockaddr_in server,client;
	if(argc != 3)
	{
    
    
		printf("param error\n");
		exit(-1);
	}
	//创建套接字
	//  int socket(int domain, int type, int protocol);
	s_fd = socket(AF_INET,SOCK_STREAM,0);
	if(s_fd == -1)
	{
    
    
		printf("socket failed\n");
		exit(-1);
	}

	size = sizeof(struct sockaddr);
	//清空
	memset(&server,0,sizeof(struct sockaddr_in));
	memset(&client,0,sizeof(struct sockaddr_in));
	//结构体初始化
	server.sin_family = AF_INET;
	server.sin_port = htons(atoi(argv[2]));
	// int inet_aton(const char *cp, struct in_addr *inp);
	inet_aton(argv[1],&server.sin_addr);

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

	if(bind(s_fd,(struct sockaddr *)&server,sizeof(struct sockaddr_in)) == -1)
	{
    
    
		perror("bind:");
		exit(-1);
	}

	//  int listen(int sockfd, int backlog);
	//监听
	if(listen(s_fd,10) == -1)
	{
    
    
		perror("listen:");
		exit(-1);
	}

	// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
	//接收
	while(1)
	{
    
    
		c_fd = accept(s_fd,(struct sockaddr *)&client,&size);
		if(c_fd == -1)
		{
    
    
			printf("accept failed\n");
			exit(-1);
		}
		printf("connect scuuess client ip:%s\n",inet_ntoa(server.sin_addr));
		if(fork() == 0)
		{
    
    
			if(fork() == 0)
			{
    
    
				while(1)
				{
    
    
					//输入数据
					memset(writebuf,0,sizeof(writebuf));
					printf("please input data:");
					gets(writebuf);
					write(c_fd,writebuf,sizeof(writebuf));
				}
			}

			while(1)
			{
    
    
				//读取数据
				memset(readbuf,0,sizeof(readbuf));
				read(c_fd,readbuf,sizeof(readbuf));
				printf("get data:%s\n",readbuf);
			}
		}
	}
	
	//关闭
	close(s_fd);
	close(c_fd);

	return 0;
}

4.2 客户端编写

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


//搜索技巧 grep “struct sockaddr_in" * -nir
//注意网络字节序转换  地址转网络 htons inet_aton


/*----------------------------------------客户端-------------------------------------*/
int main(int argc,char *argv[])
{
    
    
	char readbuf[128] = {
    
    0}; //接收数据
	char writebuf[128] = {
    
    0}; //发送数据
	int c_fd = 0,ret = 0;
	struct sockaddr_in client;
	if(argc != 3)
	{
    
    
		printf("param error\n");;
		exit(-1);
	}
	//创建套接字
	//  int socket(int domain, int type, int protocol);
	c_fd = socket(AF_INET,SOCK_STREAM,0);
	if(c_fd == -1)
	{
    
    
		printf("socket failed\n");
		exit(-1);
	}

	//清空
	memset(&client,0,sizeof(struct sockaddr_in));
	//结构体初始化
	client.sin_family = AF_INET;
	client.sin_port = htons(atoi(argv[2]));
	// int inet_aton(const char *cp, struct in_addr *inp);
	inet_aton(argv[1],&client.sin_addr);

	while(1)
	{
    
    
		//连接 int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
		if(connect(c_fd,(struct sockaddr *)&client,sizeof(struct sockaddr)) == -1) 
		{
    
    
			perror("connect:");
			exit(-1);
		}
		printf("connect success current ip:%s\n",inet_ntoa(client.sin_addr));
		if(fork() == 0)
		{
    
    
			//读写操作
			while(1)
			{
    
    
				//输入数据
				memset(writebuf,0,sizeof(writebuf));
				printf("please input data:");
				gets(writebuf);
				write(c_fd,writebuf,sizeof(writebuf));
			}
		    //  ssize_t read(int fd, void *buf, size_t count);
		}
		
		while(1)
		{
    
    
			//读取数据
			memset(readbuf,0,sizeof(readbuf));
			read(c_fd,readbuf,sizeof(readbuf));
			printf("get data:%s\n",readbuf);

		}		
	}
	
	//关闭
	close(c_fd);

	return 0;
}

4.3 实验结果

在这里插入图片描述

总结

上面对linux下网络编程进行了简单分享,对处理多方消息收发中并没有处理很好,如果多个客户接入时,这时候服务端发送时可能接收消息的不是指定的客户,因为每个客户端接入后他们之间的关系是竞争关系。后面可能要用到多并发处理,学了之后再进行优化改进吧。加油

猜你喜欢

转载自blog.csdn.net/boybs/article/details/123085911