ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

网络编程:linux下的socket套接字编程之TCP服务器

2020-12-11 10:58:56  阅读:182  来源: 互联网

标签:socket int 编程 seraddr TCP sockfd 接字 addr


文章目录

前言

socket编程是一门网络编程技术,在主要的网络通信中都会使用到它,可以使用socket编程来实现计算机之间的通信。

一、Internet历史

1968:ARPAnet(阿帕网) 采用的协议 NCP(网络控制协议). internet的雏形

1.不能跨越系统,与平台.
2.不能对数据就行纠错.

1974: 第一份TCP(传输控制协议)协议详细说明.

1.协议在有数据包丢失时不能有效的纠正

1983:TCP/ip协议成为Internet的"世界语"

把之前的tcp协议分成两部分:
IP:  用于在互联网环境中找寻目标主机.
TCP: 对数据进行纠错.

二、网络体系结构

1.OSI模型

7 层协议模型 理想化模型.

	  1.应用层
	  2.表示层
	  3.会话层
	  4.传输层
	  5.网路层
	  6.数据链路层
	  7.物理层

2.TCP/IP模型

4 层协议模型 标准模型
 {
	  1.应用层: 
			作用:决定上层用户处理数据的方式
			http(超文本传输协议),FTP(文件传输协议),SMTP(邮件传输协议)
	  
	  2.传输层: 
			作用:提供端到端的连接(端口作用:标识进程)
			TCP(传输控制协议)
			UDP(用户数据报协议)  
			单位:数据包.
			
	  3.网络层:
			作用:提供数据包的路由. (标识主机)
			IP(网间协议): 网络环境中起到路由的作用(找目标主机) 
			ICMP(互联网控制信息协议):  
			IGMP:
			单位:分组形式.
				
	  4.网络接口层(数据链路与物理层): 
			作用:物理层面的通信(介质之间),
				  传输有地址的帧以及错误检测功能 
			以太网协议.
			物理层单位: 二进制流方式  位bit
			数据链路:数据帧.

}

三、网络设计框架

{
	c/s: 客户端 与 服务器
		 缺点:1.开发工作量大,调试麻烦.
			  2.用户安全隐患大.
		 优点:1.本地可以缓存数据.(技能特效)
			  2.协议比较灵活(可以自己制定)
		 
	b/s: 浏览器 与 服务器
		 优点:1.开发工作量小,调试较简单.
			  2.用户安全隐患小.
		 缺点:1.本地不可以缓存数据.
			  2.协议固定是http(要包含所有http协议内容)
}

四、TCP服务器编写流程

{
基础:

套接字:1.是一个网络通信的接口.
	   2.是一种特殊文件描述符(非负整数).
套接字类型:SOCK_STREAM: 流式套接字. 以二进制流方式依次发送.
								保证数据可靠
								默认采用tcp协议
		 SOCK_DGRAM:  数据报套接字.
								不保证数据可靠
								默认采用udp协议.
		 SOCK_RAW:    原始套接字.

流程:

1.创建套接字–socket() ----> 买手机

int socket(int domain, int type, int protocol);
{
		作用:创建一个用于网络通信的套接字.
		domain:
				AF_UNIX: 本地套接字协议(创建出的套接字只能本地通信)
				AF_INET: 选择ipv4协议族.
				AF_INET6: 选择ipv6协议族.
				IPv4:表示ip地址由32位二进制数据构成
				IPv6:表示ip地址由128位二进制数据构成
		type:
				SOCK_STREAM: 流式套接字
				SOCK_DGRAM:  数据报套接字
				
		protocol: 通常该参数都是以缺省方式 0 传参.
				例如: 第二个参数选择SOCK_STREAM,改参数传0
					   代表采用SOCK_STREAM的默认协议(TCP)
					   
		返回值:真确返回文件描述符(套接字)
				错误  -1
}

2.绑定套接字 bind() ----> 买卡以及与手机进行绑定

	  1.设置IP地址(标识主机)  2.设置端口号(标识进程)
	    struct sockaddr_in: 结构体作用 用于设置ipv4协议族 的ip地址与端口.
  struct sockaddr_in{
               sa_family_t    sin_family; // 选择协议族 AF_INET 
               in_port_t      sin_port;   // unsigned short  设置端口号.
               struct in_addr sin_addr;   // 结构体ip地址  要访问s_addr来进行设置
            };
		    struct in_addr {
               uint32_t       s_addr;     //s_addr 是ip地址的二进制形式.
            };
ip地址形式:点分十进制"192.168.2.2".这种形式是给用户看的计算机不认识
二进制形式 11000000 10101000 00000010 00000010 
 int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
{
		sockfd: socket函数正确返回的套接字
		addr:    标准地址信息结构体.(用户使用的结构体都要强转为该结构体)
		addrlen: 地址信息结构体长度. sizeof(seraddr).
		
		返回值: 成功返回 0
				 错误返回 -1.
	}
	端口:标识进程 系统占用:0 ~ 1023 
				   用户一般可使用 1024 ~ 65535
	ifconfig:查看网络信息.

3.监听listen() -----> 等待电话

int listen(int sockfd, int backlog);
{
		作用:监听客户端.
		sockfd: socket正确返回的套接字.
		backlog: 同一时间能够监听的客户端个数.(不是代表同时能连多少个客户端)
		返回值:成功返回 0
				错误返回 -1. 
}

4.接受客户端请求 accept(). ----> 接听电话

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
			
{
作用:接受客户端请求
	sockfd: socket函数正确返回的套接字
	addr:    获取客户端地址信息. 如果不需要则传 NULL.
	addrlen: 客户端地址信息长度. 如果不需要则传 NULL.
	
返回值: 正确返回一个通信套接字(连接套接字). 重点!
			 也是一个文件描述符.
		     与客户端的通信用通信套接字来实现.socket返回的值可以称为监听套接字
			 错误 -1
}

5.数据的收发. 因为是文件描述符所以 —> 通话过程

read()/write().可以表示收发
send()/recv() 也表示收发.  一定要配套使用.

6.关闭套接字. close(). ----> 挂电话
}

创建TCP服务器示例代码:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>

#define err_log(log) do{perror(log);exit(-1);}while(0)

int main(int argc, char *argv[])
{
	/*1.创建套接字*/
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		err_log("socket");
	}

	printf("socket-----success\n");

	/*2.绑定套接字*/
	struct sockaddr_in seraddr;
	memset(&seraddr, 0, sizeof(seraddr));
	seraddr.sin_family = AF_INET;   //ipv4
	seraddr.sin_port = htons(8888); //设置端口
	seraddr.sin_addr.s_addr = inet_addr("123.123.12.123"); // inet_addr把ip点分十进制转为二进制形式.   ip地址要是本机能存在的.

	if(bind(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr)) == -1)
	{
		err_log("bind");
	}

	printf("bind----\n");

	/*监听*/

	if(listen(sockfd, 5) == -1)
	{
		err_log("listen");
	}

	printf("listen----\n");

	/*接收请求*/

	int connfd = accept(sockfd, NULL, NULL);
	if(connfd == -1)
	{
		err_log("accept");
	}

	printf("accept-----\n");

	/*数据收发*/
	char buf[32] = {0};
	recv(connfd, buf, sizeof(buf),0); //read(connfd, buf, sizeof(buf));

	printf("recv=%s\n", buf);
	
	send(connfd, "ok", 3, 0);  //write(connfd, "sb", 3);

	/*关闭套接字*/

	close(connfd);
	close(sockfd);

	return 0;
}

PS:

端口:系统占用 0~1023   用户一般使用 1024 ~ 65535
字节序转换:
	htons(): 主机字节序转网络字节序(以short类型转换)
	ntohs(): 网络字节序转主机字节序(以short类型转换)
	htonl(): 主机字节序转网络字节序(以long类型转换)
	ntohl(): 网络字节序转主机字节序(以long类型转换)
ip地址转换函数:
	in_addr_t inet_addr(const char *cp); 点分十进制转为地址二进制形式
	char *inet_ntoa(struct in_addr in);  地址二进制形式转为点分十进制.

五、TCP客户端编写流程

{
	1.创建套接字--socket()
	2.设置服务器地址信息 struct sockaddr_in.
	3.主动请求连接服务器  connect().
	4.数据的收发. recv()/send()
	5.关闭套接字 close().
}
ping:        测试主机网络是否连通.   用法: ping + ip
ifconfig:    查看当前主机的网络信息.
netstat -na: 查看系统网络连接状态. 通常需要与 grep一起使用
			 netstat -na | grep "port或ip"

客户端示例代码:

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

#define PORT 8888
#define err_log(log) do{perror(log);exit(-1);}while(0)

int main(int argc, char *argv[])
{
	if(argc != 3)
	{
		fprintf(stderr, "Usage:%s <ip> <port>\n", argv[0]);
		return -1;
	}
	/*1.创建套接字*/
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		err_log("socket");
	}

	printf("socket-----success\n");

	/*2.设置服务器地址信息*/
	struct sockaddr_in seraddr;
	memset(&seraddr, 0, sizeof(seraddr));
	seraddr.sin_family = AF_INET;   //ipv4
	seraddr.sin_port = htons(atoi(argv[2])); //设置端口
	//seraddr.sin_addr.s_addr = inet_addr("0.0.0.0"); // inet_addr把ip点分十进制转为二进制形式.   ip地址要是本机能存在的.
	seraddr.sin_addr.s_addr = inet_addr(argv[1]);

	/*3.请求连接*/
	if(connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr)) == -1)
	{
		err_log("connect");
	}

	printf("connect success\n");

	char buf[32] = {0};
	int ret = 0;
	while(1)
	{
		/*4.数据收发*/
		memset(buf, 0, sizeof(buf));
		printf("send msg:");
		fgets(buf, sizeof(buf), stdin);
		buf[strlen(buf)-1] = '\0';

		if(strcmp(buf, "quit") == 0)
		{
			printf("退出连接--------\n");
			break;
		}

		ret = send(sockfd, buf, sizeof(buf),0);
		if(ret == 0)
		{
			printf("client close\n");
			break;
		}

	}
	/*5.关闭套接字*/
	close(sockfd);

	return 0;
}

总结

本文的系统环境为Ubuntu,以上编程传输仅限于局域网的不同机器或者同一机器。按以上的流程就能写简单的TCP服务器,也可以自己动手试试写简单的文件服务器。可以在这基础上加更多东西,linux下一切皆文件,只要你设置好带宽,处理好数据收发,就应该什么都能传。
暂时先写简单的TCP客户端和服务器,后续文章有UDP协议的服务器和客户端,以及并发服务器;

标签:socket,int,编程,seraddr,TCP,sockfd,接字,addr
来源: https://blog.csdn.net/qq_42585866/article/details/111028252

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有