IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> C/Linux网络编程IV -> 正文阅读

[系统运维]C/Linux网络编程IV

目录

一.基于TCP协议的网络点对点通信的基本过程

二.介绍上述过程中涉及的函数

三.服务器端的实现代码


一.基于TCP协议的网络点对点通信的基本过程

二.介绍上述过程中涉及的函数

函数原型解释作用
<sys/socket.h>int socket(int domain, int type, int protocol);

domain:该套接字所遵循的IP协议,AF_INET或是AF_INET6

type:套接字的种类,分为流套接字SOCK_STREAM和报套接字SOCK_DGRAM

protocol:对应套接字种类的代表协议(流套接字的代表协议为TCP协议,报套接字的代表协议为UDP协议),填0即可。(或TCP协议填)

return:返回一个文件描述符

socket() creates an endpoint for communication and returns a descriptor.
~int bind(int sockfd, const struct sockaddr *addr,? socklen_t addrlen);

sockfd: socket函数返回的文件描述符

sockaddr: 套接字地址

socklen_t: 套接字地址长度

return: 0成功-1失败

socket()只是创建了一个文件标识符fd指定的套接字,但是其并没有地址。bind()函数为其绑定了一个套接字地址。
~

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

sockfd:客户端用于通信的套接字的文件描述符。

addr:传入参数。表示服务器端的套接字地址。(这结合功能很好理解)

addrlen:服务器套接字地址的长度(单位:字节)

return:0成功-1失败

对于TCP而言,connect函数进行一次连接尝试(三次握手),成功或者失败后返回。注意,不是到成功为止。

由客户端调用。

注意,connect函数只是发起三次握手请求,其需要和accept配合,实现三次握手。

~int listen(int sockfd, int backlog);

sockfd:主动状态下套接字的文件描述符。

backlog:服务器的监听上限。服务器有一个队列存放待处理的连接(三次握手)请求,backlog就是这个队列的长度(以一个请求为单位)。

return:0成功-1失败

将sockfd标识的套接字的状态由主动套接字转变为被动套接字。并且为服务器设置最大的监听上限。
~int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:服务器端的被动套接字

addr:传出参数。客户端的套接字地址。

addrlen:传入传输参数。我们需要一个buffer存放客户端套接字地址。我们把sizeof(buffer)传入,accept会传出精确的客户端套接字的长度。

return:返回一个连接套接字的文件描述符。

若存放连接的队列为空,accept则进行阻塞监听;不为空,accept则取出队首,将客户端套接字的地址存入addr指定的内存单元中,并且释放掉监听套接字,返回一个连接套接字,其和监听套接字的原地址绑定。
#include <unistd.h>?int close(int fd);

fd:指定的文件描述符

return:0成功,-1失败

?close() ?closes ?a ?file ?descriptor, ?so that it no longer refers to any file and may be reused.

1.我们发现在基于TCP协议的通信过程中,服务器端为socket函数产生的套接字(实质上是一个文件描述符)绑定了一个套接字地址,但是客户端并没有这么做。这是因为在客户端调用connect函数时,系统为其套接字自动分配了一个套接字地址。

2.套接字的三种状态(very important):文件描述符状态。就是刚刚被socket创建,还未与套接字地址绑定时的状态。? ? ? ? 关闭状态或称被动状态。文件描述符状态的套接字,在经过bind函数绑定一个套接字地址之后,就进入关闭状态。关闭状态的套接字为主动套接字,即客户端的套接字,将调用connect函数主动寻求和服务器连接。? ? ? ? ?监听状态或称被动状态。主动状态的套接字调用listen函数后变为服务器端的套接字,将调用accept函数进行监听阻塞。

3.更详细的信息需要在Linux的手册中看。命令诸如man listen等等。

4.区分服务器端的监听套接字和连接套接字。监听套接字是由socket函数创建的,作为bind函数,listen函数和accept参数的那个套接字,其作用仅仅是用于引导客户端的套接字和服务器端的连接套接字组成socket pair,建立TCP连接。? ? ? ? 连接套接字是由accept函数返回的那个文件描述指定的,是真正与客户端建立连接的套接字。? ? ? ? 服务器端往往只有一个监听套接字,其被反复使用,用于引导建立连接。

三.服务器端的实现代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <pthread.h>
#define SERV_PORT 9527
#include <ctype.h>
#include <sys/socket.h>
#include <arpa/inet.h>
//用于抛出错误
void sys_err(const char *str){
	perror(str);
	exit(1);

}

int main(int argc, char *argv[]){
	int sfd = socket(AF_INET,SOCK_STREAM,0);//套接字文件描述符--监听套接字
    int cfd = -1;//通信套接字
	struct sockaddr_in serv_addr;		//服务器端套接字地址
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(SERV_PORT) ;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	struct sockaddr_in clnt_addr;
	socklen_t lgth_clnt_addr = sizeof(clnt_addr);
	int rec = 0;//read到的字节数
	char buf[BUFSIZ];	//BUFSIZ是系统预定义的,为4096
	int i = 0;

	if(sfd == -1){
		sys_err("fail to create a socket!");	
	}
	if((bind(sfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))) == -1){
		sys_err("fail to bind a socket!");
	}	
	if(listen(sfd,128) == -1){
		sys_err("fail to listen!");
	}
	cfd = accept(sfd,(struct sockaddr*)&clnt_addr,&lgth_clnt_addr);
	if(cfd == -1){
		sys_err("fail to accept!");
	}
 //既然成功连接了客户端,咱打印一下
        printf("The IP address of the client is %s.\n",inet_ntop(AF_INET,&clnt_addr.sin_addr.s_addr,clnt_ip_addr,sizeof(clnt_ip_addr)));
        printf("The port of the client is %d.\n",ntohs(clnt_addr.sin_port));

	while(1){
		rec = read(cfd,buf,sizeof(buf));//这里涉及到数组的大小是多少,为整个数组的内存大小
		write(STDOUT_FILENO,buf,rec);//这步是向屏幕输出接收到的字母序列,STDOUT_FILENO是屏幕的文件标识符
		for(i = 0;i < rec;++i){
			buf[i] = toupper(buf[i]);
		}
		write(cfd,buf,rec);
	}
	close(sfd);
	close(cfd);
	return 0;
}

下面是几个注意点:

1.sockaddr和sockaddr_in的库,socket相关函数的库,字节序转换库<arpa/inet.h>不要忘记include。

2.sfd和cfd最好分别定义一个。尽管可以通过sfd = accept(sfd...),但是这样的话在最后close时,只能关闭通信套接字,释放不了监听套接字。而且当需要建立两个连接时,监听套接字不能被复用。

3.Linux的gcc编译器可能比较老,不支持C99语法,结构体前面必需加struct,否则会报错。

4.sockaddr到sockaddrd_in的强制类型转换不要忘记了。

5.每一步的容错不要懒得写,等到连接失败的时候很容易不知道哪步出错。

下面是不写客户端就可以测试的方法。先用XShell打开另外一个Linux命令行,执行nc 127.0.0.1 9527命令,9527是我们赋给SERV_PORT常量的值,也可以是其他的,不过代码就要相应地做出更改。这样就可以直接在命令行中输入小写字母序列,命令行会打印对应的大写字母。

?

?上面边是服务器端,下面是客户端。

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-01 00:32:30  更:2022-04-01 00:36:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 23:53:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码