C语言简单网络聊天程序及系统调用跟踪
一、socket介绍
socket起源于linux,在Linux中,一个非常重要的思想就是“一切皆文件”,一切行为皆可描述为“打开文件---->读写文件----->关闭文件”,socket可以理解成一种特殊的文件,把对底层tcp/ip网络的调用封装起来,提供给用户一些调用的接口来是实现网络编程。
引用一张图清晰的解释,此图来自CMU ICS
我们都知道网络通信需要知道一个三元组——ip、protocol、port,来唯一的标识网络中的某个主机上的某个进程,从而实现不同主机间进程的通信。
具体实现:服务器端:创建socket,返回一个socket描述符,和服务器地址和端口bind,listen函数开启监听想要连接的客户端,accept接受客户端的连接请求,为客户端分配一个专属的socket连接,在传输完成后close关闭连接
相比之下,客户端就比较简单了,socket创建,connect,read and write,然后close,下面实现了一个简单的多线程网络聊天。
二、简单聊天程序
serve.c
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> #define PORT 6666 //端口号 #define SIZE 1024 //定义的数组大小 int create_socket() //创建套接字和初始化以及监听函数 { int listen_socket = socket(AF_INET, SOCK_STREAM, 0); //创建一个负责监听的套接字 协议族,协议类型,具体协议,返回大于0表示创>建成功 if(listen_socket< 0) { perror("socket"); return -1; } struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); //服务器端地址结构 addr.sin_family = AF_INET; /* Internet地址族 */ addr.sin_port = htons(PORT); /* 端口号 */ addr.sin_addr.s_addr = htonl(INADDR_ANY); /* 服务器IP地址 */ if(bind(listen_socket, (struct sockaddr *)&addr, sizeof(addr))==-1){ perror("bind"); return -1; } if(listen(listen_socket, 4) == -1) //监听 { perror("listen"); return -1; } return listen_socket; } //信息处理函数,功能是将客户端传过来的小写字母转化为大写字母 void *handle_client(void *ptr){ int fd=*(int *)ptr; printf("the new connect_socket is %d\n",fd); char buf[SIZE]; while(1) { int ret = read(fd, buf, SIZE-1); if(ret == -1) { perror("read"); break; } if(ret == 0) break; buf[ret] = '\0'; printf("%s\n", buf); write(fd, buf, ret); if(strncmp(buf,"bye",3)==0) break; } close(fd); printf("连接已断开\n"); } int main() { pthread_t thread; int listen_socket = create_socket(); struct sockaddr_in client_addr; socklen_t addrlen=sizeof(client_addr); printf("等待与客户端连接......\n"); while(1){ int client_socket=accept(listen_socket,(struct sockaddr *)&client_addr,&addrlen); if(client_socket==-1){ perror("accept error\n"); return -1; } printf("connect success %s\n",inet_ntoa(client_addr.sin_addr)); pthread_create(&thread,NULL,handle_client,(void *)&client_socket); } printf("close socket"); close(listen_socket); return 0; }
client.c
#include<stdio.h> #include<sys/types.h> #include<sys/socket.h> #include<unistd.h> #include<stdlib.h> #include<netinet/in.h> #include<arpa/inet.h> #define s 512 int main(int argc,const char* argv[]) { if(argc != 3) { printf("Usage:%s [ip] [port]\n",argv[0]); return 0; } //创建一个用来通讯的socket int sock = socket(AF_INET,SOCK_STREAM, 0); if(sock < 0) { perror("socket"); return 1; } //服务器端的地址结构体 struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(atoi(argv[2])); //inet_addr 将字符串格式的ip地址转化为网络字节顺序 server.sin_addr.s_addr = inet_addr(argv[1]); socklen_t len = sizeof(struct sockaddr_in); if(connect(sock, (struct sockaddr*)&server, len) < 0 ) { perror("connect"); return 2; } //连接成功进行发送和接受数据 char buf[1024]; char buf_rec[1024]; while(1) { printf("send:"); fflush(stdout); ssize_t _s = read(0, buf, sizeof(buf)-1); buf[_s] = 0; write(sock, buf, _s); if(strncmp(buf,"bye",3)==0) break; recv(sock,buf_rec,s,0); printf("%s\n",buf_rec); } close(sock); printf("client close socket"); return 0; }
三、strace跟踪系统调用
strace是一个可用于诊断、调试和教学的Linux用户空间跟踪器。我们用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等。有关的命令不再介绍,自行百度。
执行命令
sudo strace -t -T -o ./info.txt ./server
这里-t 是显示时间,-T 显示执行某个系统调用耗费的时间 ,-o将输出信息写到指定到文件,./server执行server
查看info.txt文件可以看到
可以看到程序启动涉及到一堆系统调用,有execve创建一个一个进程,brk分配内存等等,向下查看,可以看到socket,bind listen,accept等函数
在程序运行期间,用-e trace=network 来过滤,只查看有关网络的系统调用,可以看到,服务器端启动之后完成一系列初始化,即socket的创建,绑定ip地址,端口号,协议,监听端口,最后在accpet函数阻塞,等待客户端的连接。
当客户端请求连接时(这里设置了两个客户端同时请求连接),服务器端fork一个子进程,为每个客户端创建专属的socket描述符,在整个通信期间都使用这个socket来通信,直到连接释放。
原文地址:https://www.cnblogs.com/andyflyto/p/12024576.html
- Dora.Interception, 为.NET Core度身打造的AOP框架[3]:Interceptor的注册
- Dora.Interception, 为.NET Core度身打造的AOP框架:不一样的Interceptor定义方式
- Dora.Interception,为.NET Core度身打造的AOP框架:全新的版本
- ASP.NET Core的路由[4]:来认识一下实现路由的RouterMiddleware中间件
- 浅谈 Java 并发编程中的若干核心技术
- ASP.NET Core的路由[3]:Router的创建者——RouteBuilder
- ASP.NET Core的路由[2]:路由系统的核心对象——Router
- ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系
- 学习ASP.NET Core, 怎能不了解请求处理管道[6]: 管道是如何随着WebHost的开启被构建出来的?
- 学习ASP.NET Core, 怎能不了解请求处理管道[5]: 中间件注册可以除了可以使用Startup之外,还可以选择StartupFilter
- 学习ASP.NET Core, 怎能不了解请求处理管道[4]: 应用的入口——Startup
- 学习ASP.NET Core, 怎能不了解请求处理管道[3]: 自定义一个服务器感受一下管道是如何监听、接收和响应请求的
- .NET Core多平台开发体验[4]: Docker
- .NET Core多平台开发体验[3]: Linux (Windows Linux子系统)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Qt5.5.1版本中QString().arg()和qss在处理路径及文件名需要注意的地方
- Typecho将Gravatar头像改为QQ头像
- python socket 简单示例
- 深入了解C++虚函数
- Mathematica 在高考数学与高等数学等学习中的简单应用与思考
- 数据结构之【实现数组】
- 数据结构【单链表基本操作】
- Django Models 随机获取指定数量数据方法
- 数据结构【静态栈】代码实现
- Qt官方示例解析-Address Book-基于单个数据模型在不同视图呈现不同数据
- React 中请求远程数据的四种方法
- 数据结构【动态队列】代码实现
- C++抽象工厂
- 数据结构【动态栈】代码实现
- UOS安装chrome谷歌浏览器依赖Fonts-Liberation的问题