socket编程实现tcp服务器_C/C++
1. 需求分析
实现一个回声服务器的C/S(客户端client/服务器server)程序,功能为客户端连接到服务器后,发送一串字符串,服务器接受信息后,返回对应字符串的大写形式给客户端显示。
例如:
客户端发送“this is a webserver example!
",
服务器返回"THIS IS A WEBSERVER EXAMPLE!
"
2. 项目实现
2.1 服务器端程序echo_server.c
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <unistd.h> //read, write, close
#include <sys/types.h> //socket, bind, listen, accept
#include <sys/socket.h> //socket, bind, listen, accept
#include <string.h> //strerror
#include <ctype.h> //inet_ntop
#include <arpa/inet.h> //inet_ntop
#include <errno.h> //strerror
#define SERVER_PORT 666
//出错处理
void perror_exit(const char* des) {
fprintf(stderr, "%s error, reason: %s\n", des, strerror(errno));
exit(1);
}
int main(void){
int sock;//代表信箱
int ret;//作为bind和listen的返回值,用于处理出错信息
struct sockaddr_in server_addr;
//1.创建套嵌字(信箱)。成功:返回socket的文件描述符,失败:返回-1,设置errno
sock = socket(AF_INET, SOCK_STREAM, 0);
if(sock == -1) {
perror_exit("create socket");
}
//2.清空服务器地址空间(标签),写上地址和端口号
bzero(&server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;//选择协议族IPV4
//inet_pton(AF_INET, "1.1.1.1", &server_addr.sin_addr.s_addr);//测试出错处理函数perror_exit
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本地所有IP地址
server_addr.sin_port = htons(SERVER_PORT);//绑定端口号
//3. 实现标签贴到收信得信箱上
ret = bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(ret == -1) {
perror_exit("bind");
}
//4. 把信箱挂置到传达室,这样,就可以接收信件了(监听客户端)
ret = listen(sock, 128);
if(ret == -1) {
perror_exit("listen");
}
//万事俱备,只等来信
printf("等待客户端的连接\n");
//5. 处理客户端请求
int done =1;
while(done){
struct sockaddr_in client;
int client_sock, len, i;
char client_ip[64];
char buf[256];
socklen_t client_addr_len;
client_addr_len = sizeof(client);
client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
//打印客服端IP地址和端口号
printf("client ip: %s\t port : %d\n",
inet_ntop(AF_INET, &client.sin_addr.s_addr,client_ip,sizeof(client_ip)),
ntohs(client.sin_port));
/*读取客户端发送的数据*/
len = read(client_sock, buf, sizeof(buf)-1);
buf[len] = '\0';
printf("receive[%d]: %s\n", len, buf);
//转换成大写
for(i=0; i<len; i++){
buf[i] = toupper(buf[i]);
}
len = write(client_sock, buf, len);
printf("finished. len: %d\n", len);
close(client_sock);
}
//6. 关闭连接
close(sock);
return 0;
}
2.2 客户端程序echo_client.c
#include <stdio.h> //printf
#include <stdlib.h> //exit
#include <string.h> //memset, strlen
#include <unistd.h> //read, write, close
#include <sys/socket.h> //socket, connect
#include <arpa/inet.h> //inet_pton
#define SERVER_PORT 666
#define SERVER_IP "127.0.0.1"
int main(int argc, char *argv[]){//argc表示传入命令的个数,argv表示传入的具体信息
int sockfd;
char *message;
struct sockaddr_in servaddr;
int n;
char buf[64];
//异常处理
if(argc != 2){
fputs("Usage: ./echo_client message \n", stderr);
exit(1);
}
message = argv[1];//传入的信息
printf("message: %s\n", message);
//1. 创建套嵌字
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&servaddr, '\0', sizeof(struct sockaddr_in));//分配空间
//定义地址IP和端口
servaddr.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);
servaddr.sin_port = htons(SERVER_PORT);
//2. 连接服务器
connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
//3. 读写和服务器的交互信息
write(sockfd, message, strlen(message));
n = read(sockfd, buf, sizeof(buf)-1);
if(n>0){
buf[n]='\0';
printf("receive: %s\n", buf);
}else {
perror("error!!!");
}
printf("finished.\n");
//4. 关闭连接
close(sockfd);
return 0;
}
3. 程序运行方式
我的echo_server.c程序在/share/echo_server文件夹下,echo_client.c程序在/share/echo_client文件夹下。
必须先运行服务器程序,再运行客户端程序。顺序不能反!!
3.1. 运行服务器程序
- 首先,进入echo_server.c所在文件夹
root@lxb-virtual-machine:/# cd /share/echo_server
- 之后,编译程序
root@lxb-virtual-machine:/share/echo_server# gcc echo_server.c -o echo_server
- 最后运行程序
root@lxb-virtual-machine:/share/echo_server# ./echo_server
全过程截图:
3.2. 运行客户端程序
- 首先,进入echo_client.c所在文件夹
root@lxb-virtual-machine:/# cd /share/echo_client
- 之后,编译程序
root@lxb-virtual-machine:/share/echo_client# gcc echo_client.c -o echo_client
- 最后运行程序
root@lxb-virtual-machine:/share/echo_client# ./echo_client "this is a webserver example!"
全过程截图:
4. 分析
首先我们进行感性的分析,用来理解各个步骤的用意。之后我们需要对里面涉及到的函数进行具体的分析。
4.1 李华写信模型
我们在高中英语经常遇到的一道作文题就是“你是李华,请给国外的笔友Andy写信”,而我们网络通信也可以类比于“李华与国外笔友通信”的模型。这里我们将“李华”比作客户端,“国外笔友Andy”作为服务器端。
4.1.1 Andy应该怎么做呢?
为了使“李华同学”与“国外笔友”能够交流,首先需要统一语言,同时约定好寄信方式,邮局寄信还是电子邮件之类的,(这就是“socket套嵌字”)。之后Andy准备好一个信箱,之后找一张标签纸(server_addr),整理干净这张标签纸(bzero函数),往上面写上自己的地址和门牌号,一切准备好后,将贴好标签纸的信箱挂到外面(listen函数),这样大家都能给Andy寄信。最后Andy只需要时不时去看看信箱有没有信,有的话把信的内容读出来(read函数),之后再写封回信寄回去(write函数)。
4.1.2 李华应该怎么做?
李信作为写信人就比较简单了,首先还是使用统一的寄信方式,往信封上写上自己要寄的地址和门牌号,也就是Andy家的地址和门牌号,之后与Andy联系上(connect函数)。接下来就可以给Andy写信(write函数),读Andy的回信(read函数)。收到回信,不想再和Andy通信了,这时就把两个人的联系断开(close函数)。
4.2 程序流程图
4.3 具体函数解析
4.3.1 socket函数
所属头文件
#include <sys/socket.h>
函数定义
int socket(int domain, int type, int protocol);
参数含义
- domain:
AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
AF_INET6 与上面类似,不过是来用IPv6的地址
AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用 - type:
SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类型,这个socket是使用TCP来进行传输。
SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
SOCK_SEQPACKET该协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。
SOCK_RAW socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使用该协议)
SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数据包的顺序 - protocol:
传0 表示使用默认协议。 - 返回值:
成功:返回指向新创建的socket的文件描述符,失败:返回-1,设置errno
- domain:
4.3.2 bind函数
所属头文件
#include <sys/socket.h>
函数定义
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数含义
- sockfd:
socket文件描述符 - addr:
IP地址加端口号 - addrlen:
addr的长度 - 返回值:
成功:返回0,失败:返回-1,设置errno
- sockfd:
4.3.3 listen函数
所属头文件
#include <sys/socket.h>
函数定义
int listen(int sockfd, int backlog);
参数含义
- sockfd:
socket文件描述符 - backlog:
在Linux 系统中,它是指排队等待建立3次握手队列长度 - 返回值:
成功:返回0,失败:返回-1,设置errno
- sockfd:
4.3.4 accept函数
所属头文件
#include <sys/socket.h>
函数定义
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数含义
- sockfd:
socket文件描述符 - addr:
IP地址加端口号 - addrlen:
addr的长度 - 返回值:
成功:返回一个新的socket文件描述符,失败:返回-1,设置errno
- sockfd:
4.3.5 connect函数
所属头文件
#include <sys/socket.h>
函数定义
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数含义
- sockfd:
socket文件描述符 - addr:
IP地址加端口号 - addrlen:
addr的长度 - 返回值:
成功:返回一个新的socket文件描述符,失败:返回-1,设置errno
- sockfd:
4.3.6 出错处理函数
所属头文件
#include <errno.h>
#include <string.h>
函数定义
char *strerror(int errnum);
参数含义
- errnum:
错误编号的值,一般取 errno 的值 - 返回值:
错误原因
- errnum:
5.感谢
感谢bilibili的Martin老师的视频: C语言/C++服务器开发】小白实现第一个服务器入门项目 网络通信与Socket 编程详解&源码分享,本篇博客也是基于Martin老师这个视频所做的。
socket编程实现tcp服务器_C/C++的更多相关文章
- socket编程 ------ 建立 TCP 服务器和客户端流程(阻塞方式)
服务器端: 服务器端先创建一个socket,然后把这个socket绑定到端口上,接着让它向tcp/ip协议栈请求一个监听服务并创建一个accept队列来接受客户端请求. void creat_tcpS ...
- 计算机网络(十三),Socket编程实现TCP和UDP
十三.Socket编程实现TCP和UDP 1.TCP (1)TCPServer.java类 package com.interview.javabasic.socket; import com.int ...
- python网络编程socket编程(TCP、UDP客户端服务器)
摘录 python核心编程 使用socket()模块函数创建套接字——通信端点 >>> from socket import * >>> tcpSock = soc ...
- day8---多线程socket 编程,tcp粘包处理
复习下socket 编程的步骤: 服务端: 1 声明socket 实例 server = socket.socket() #括号里不写 默认地址簇使用AF_INET 即 IPv4 ...
- Windows Socket 编程_单个服务器对多个客户端简单通讯
单个服务器对多个客户端程序: 一.简要说明 二.查看效果 三.编写思路 四.程序源代码 五.存在问题 一.简要说明: 程序名为:TcpSocketOneServerToMulClient 程序功能:实 ...
- python socket 编程(TCP与UDP)
实验环境:python2 一.TCP编程 1.建立TCP服务器 ①创建TCPServer.py文件 ②编写服务器代码 1)创建socket对象,调用socket构造函数 2)绑定ip端口(IP号和端口 ...
- day08 多线程socket 编程,tcp粘包处理
复习下socket 编程的步骤: 服务端: 1 声明socket 实例 server = socket.socket() #括号里不写 默认地址簇使用AF_INET 即 IPv4 ...
- socket编程和并发服务器
socket这个词可以表示很多概念: 在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程,“IP地址+端口号”就称为socket. 在TCP协议中,建立连接的两个进程 ...
- 基于C#的socket编程的TCP同步实现
该博客源著地址https://www.cnblogs.com/sunev/archive/2012/08/05/2604189.html 一.摘要 总结一下基于C#的TCP传输协议的涉及到的常用方法及 ...
随机推荐
- springboot user guide hand book
手册: https://docs.spring.io/spring-boot/docs/1.5.9.RELEASE/reference/html/getting-started-first-appli ...
- phpstorm+xdebug调试详细教程
对于PHP开发,初来咋到,开发环境的搭建和理解感觉是最烦人的一件事了.不像JAVA,打开一个Eclipse就可以开搞,Php的Debug都要几个插件来配合工作.这些都是死的,好说.但是对于Xdebug ...
- 为什么要使用 rabbitmq?
(1)在分布式系统下具备异步,削峰,负载均衡等一系列高级功能; (2)拥有持久化的机制,进程消息,队列中的信息也可以保存下来. (3)实现消费者和生产者之间的解耦. (4)对于高并发场景下,利用消息队 ...
- 我们能自己写一个容器类,然后使用 for-each 循环码?
可以,你可以写一个自己的容器类.如果你想使用 Java 中增强的循环来遍历, 你只需要实现 Iterable 接口.如果你实现 Collection 接口,默认就具有该属性.
- 什么是HTML 5?
HTML 5是HTML的新标准,其主要目标是无需任何额外的插件如Flash.Silverlight等,就可以传输所有内容.它囊括了动画.视频.丰富的图形用户界面等. HTML5是由万维网联盟(W3C) ...
- mapper.xml文件中标签没有提示的解决
1.首先我们来看看mapper.xml的头文件 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTY ...
- 使用Ansible部署openstack平台
使用Ansible部署openstack平台 本周没啥博客水了,就放个云计算的作业上来吧(偷个懒) 案例描述 1.了解高可用OpenStack平台架构 2.了解Ansible部署工具的使用 3.使用A ...
- L298N双H桥集成电路板的双H桥是什么意思?为什么要叫双H桥?L298N工作原理
H桥是一个典型的直流电机控制电路,因为它的电路形状酷似字母H,故得名与"H桥".4个三极管组成H的4条垂直腿,而电机就是H中的横杠. 控制两个三极管的导通来控制电流方向,从而实现电 ...
- java中抽象类和抽象方法到底有什么用呢?
抽象类和抽象方法有什么用呢?马克-to-win:当初sun公司为什么要设计抽象类和抽象方法呢?当你在做车的系统设计时,当你设计车这个通用类时,假如你确认别人实例化车这个通用类没有意义时(不知道是bik ...
- Python疫情爬取输出到txt文件
在网上搬了一个代码,现在不适用了,改了改 import requestsimport jsondef Down_data(): url = 'https://view.inews.qq.com/g2/ ...