c实现服务端和客户端交互:
学习查阅的博客:
https://blog.csdn.net/u011068702/article/details/54380259
https://blog.csdn.net/iamhycljc/article/details/6859013
在Terminator中的快捷键,alt+a锁定多个,alt+o取消锁定
c语言中,printf("...... "); 一定要加 !!!!!!
编译的时候,加上-Wall -g 前者可以打印错误或警告信息,后者可以用gdb调试
服务端 server.c:
调试方法,在命令行输入:nc 127.0.0.1 port
///c #include <stdio.h> #include <unistd.h> #include <sys/socket.h> #include <stdlib.h> #include <netinet/in.h> #include <arpa/inet.h> #include <ctype.h> #include <strings.h> #define SERV_IP "127.0.0.1"//点分十进制类型 #define SERV_PORT 6666//服务端port,不能大于65535(2的16次方),1000以下的端口一般给系统使用,一般3000以上 ///服务端代码:socket --> bind --> listen --> accept --> (read write) --> close两个socket int main(void) { int sfd;//用来建立连接的文件描述符 struct sockaddr_in serv_addr;//存储客户端的ip、port struct sockaddr_in client_addr; int cfd;//用来和客户端通信的文件描述符 记住,socket都是成对出现的 socklen_t client_addr_len;//listen的传入传出参数 char buf[BUFSIZ];//BUFSIZ 操作系统自带的宏,8K大小 char client_IP[BUFSIZ]; int n;//read 实际读到的字节 //查看方式:linux命令行, man socket //三个参数: int socket(int domain, int type, int protocol); //domain: 即地址类型,ipv4或ipv6,即AF_INET或AF_INET6 //type: 用来建立连接的方式,即tcp(默认)或udp //protocol: 0 sfd = socket(AF_INET, SOCK_STREAM, 0); //TODO 检测sfd返回值 bzero(&serv_addr, sizeof(serv_addr));//使用serv_addr之前,先初始化,即清空 和memset不同在于,memset可以初始化到任意数值 serv_addr.sin_family = AF_INET;//作用域(类型):ipv4、ipv6、本地套接字 等 serv_addr.sin_port = htons(SERV_PORT);//注意:默认是小端存储,要把本地字节序,转化成网络字节序 int--->二进制0101 //serv_addr.sin_addr.s_addr = inet_pton(SERV_IP);//注意:同理 #include <arpa/inet.h> 字符串--->二进制0101 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//区别,一个是int转,一个是字符串转 //serv_addr.sin_addr.s_addr = inet_addr(SERV_IP);//INADDR_ANY就是inet_addr("0.0.0.0") bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); //std::cout << sizeof(serv_addr) << std::endl; listen(sfd, 128);//参数2:允许最大上限的客户端同时请求连接数 client_addr_len = sizeof(client_addr); cfd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len);//等待客户端连接 阻塞等待 printf("client IP:%s, client port:%d ", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_IP, sizeof(client_IP)), ntohs(client_addr.sin_port)); while(1){ n = read(cfd, buf, sizeof(buf));//从cfd中读,读到的数据都在buf缓存中 n:实际读到的字节 for (int i = 0; i < n; ++i) { buf[i] = toupper(buf[i]);//小写转大写 } write(cfd, buf, n); } close(sfd); close(cfd); return 0; }
客户端 client.c:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #define SERV_IP "127.0.0.1" #define SERV_PORT 6666 int main(void) { int cfd; struct sockaddr_in serv_addr;//这里存的是服务端的ip和port,因为是connect的参数 socklen_t serv_addr_len; char buf[BUFSIZ]; int n;//读到的字节数 cfd = socket(AF_INET, SOCK_STREAM, 0);//ipv4,流式协议 //linux会随机分配ip给客户端,因此可以不bind memset(&serv_addr, 0, sizeof(serv_addr));//初始化,防止ip的初始值是乱七八糟的 memset:将指针*转换成int serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(SERV_PORT);//大端转小端 //ip:serv_addr.sin_addr.s_addr = inet_addr(SERV_IP); inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); connect(cfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); /*在C语言中有strlen和sizeof两个函数求字符数组的长度函数,他们俩的区别就是是否把最后的结束标志也加上去。 strlen是不加的,他表示字符串的长度。 而sizeof求的是字符串在内存中的长度,所以它是加上最后的' '的 所以一般而言后者的长度会比前者多1。*/ while (1){ fgets(buf, sizeof(buf), stdin);//类似c++ 中的cin << 将用户的输入写入到buf中 fgets:用户输入hello world --> hello world write(cfd, buf, strlen(buf)); n = read(cfd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, n);//写到屏幕上去 } close(cfd); return 0; }
boost::asio实现服务端和客户端交互:
为cmakelists.txt添加boost组件:https://www.cnblogs.com/magic-428/p/9144492.html
boost::asio的同步和异步方式:https://www.cnblogs.com/lidabo/p/8317196.html
写好的代码编译的时候,一定要加上 -lboost_system 比如:/usr/bin/g++ main.cpp -o test_main -Wall -g -lboost_system
cmakelists.txt(为了在clion中写代码有代码提示)文件写法例如:
cmake_minimum_required(VERSION 2.8) project( process ) SET(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-std=c++11") find_package(Boost REQUIRED COMPONENTS # regex filesystem # 我的工程中只使用了 boost 的 filesystem 功能,因此这里只有一个组件 system ) if(NOT Boost_FOUND) message("Not found Boost") endif() include_directories(${Boost_INCLUDE_DIRS}) message("${Boost_INCLUDE_DIRS}") message("${Boost_LIBRARIES}") add_executable( 01_test main.cpp) target_link_libraries(01_test ${Boost_LIBRARIES})
运行报错,正好学习分析一下,linux下的core dumped这种错误,主要学习,ulimit命令和 gdb <program> core 来调试:
https://www.cnblogs.com/s-lisheng/p/11278193.html
同步方式实现的服务端和客户端:
服务端:
#include <iostream> #include <boost/asio.hpp> using namespace boost::asio; int main(int argc, char* argv[]) { // 所有asio类都需要io_service对象 io_service iosev; ip::tcp::acceptor acceptor(iosev, ip::tcp::endpoint(ip::tcp::v4(), 6667));//todo 尽量使用3000以上的端口!!! for(;;) { // socket对象 ip::tcp::socket socket(iosev); // 等待直到客户端连接进来 acceptor.accept(socket); // 显示连接进来的客户端 std::cout << socket.remote_endpoint().address() << std::endl; //std::cout << socket.remote_endpoint().data() << std::endl; // 向客户端发送hello world! boost::system::error_code ec; socket.write_some(buffer("hello world!"), ec); // 如果出错,打印出错信息 if(ec) { std::cout << boost::system::system_error(ec).what() << std::endl; break; } // 与当前客户交互完成后循环继续等待下一客户连接 } return 0; }
客户端:
#include <iostream> #include <boost/asio.hpp> using namespace boost::asio; int main(int argc, char* argv[]) { // 所有asio类都需要io_service对象 io_service iosev; // socket对象 ip::tcp::socket socket(iosev); // 连接端点,这里使用了本机连接,可以修改IP地址测试远程连接 ip::tcp::endpoint ep(ip::address_v4::from_string("127.0.0.1"), 6667); // 连接服务器 boost::system::error_code ec; socket.connect(ep,ec); // 如果出错,打印出错信息 if(ec) { std::cout << boost::system::system_error(ec).what() << std::endl; return -1; } // 接收数据 char buf[100]; size_t len=socket.read_some(buffer(buf), ec); // std::cout.write(buf, len); std::cout.write(buf, len); std::cout << std::endl; return 0; }