热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【网络】TCP服务器的实现

socket编程基本概念在TCPIP协议中,IP地址+端口号标识个唯一的一个进程,“IP地址+端口号”就是socket在TCP协议中,建立连接需要两个进程各自有一个socket标识符,
socket编程基本概念

在TCP/IP协议中,IP地址+端口号标识个唯一的一个进程,“IP地址+端口号”就是socket

在TCP协议中,建立连接需要两个进程各自有一个socket标识符,这两个socket组成的socket pair就标识着唯一的连接

相关概念介绍

网络字节序

首先呢,我们都知道内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件的偏移也有大端和小端之分。

同理,网络数据流其实也有大端和小端之分。


网络数据流的地址这样规定:先发出的数据是低地址,后发出的数据是高地址。

TCP/IP协议规定:网络数据流应采用大端字节序,低地址高字节

socket地址数据类型


用到的相关函数

socket

作用

创建socket套接字

头文件

#include

#include

函数原型

int socket(int domain,int type, int protocol);

参数

domain我们一般选择AF_INET,表示IPv4

type表示传输数据的类型,有字节流类型和SOCK_STREAM数据报类型SOCK_DGRAM

protocol表示创建的类型方式,我们此时设置为0

返回值

成功返回对应的文件描述符

错误返回-1

bind

作用

进行服务器的IP地址和端口号的绑定

头文件

#include

#include

#include

函数原型

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

参数

sockfd是服务器的套接字,就是socket函数的返回值

addr是socket服务器的地址内容


addrlen是传入的协议地址的大小

返回值

成功返回0

错误返回-1

listen

作用

设置套接字为监听的状态

头文件

#include

#include

函数原型

int listen(int sockfd,int backlog);

参数

sockfd是要设置的套接字

backlog是服务器的最大等待队列的个数

返回值

成功返回0

错误返回-1

connect

作用

使客户端连接到服务器

头文件

#include

#include

函数原型

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

参数

sockfd表示需要连接到服务器的客户端套接字

addr表示服务器的地址和端口号

addrlen表示的是addr的大小

返回值

成功返回0

错误返回-1

accpet

作用

服务器用来接受连接

头文件

#include

#include

函数原型

int accept(int sockfd, struct sockaddr * addr,  socklen_t *addrlen);

参数

sockfd表示需要连接的客户端的套接字

addr是传出参数,用来传出客户端的IP地址和端口号

addrlen标示传出参数的长度

返回值

成功返回0

错误返回-1

TCP服务器的代码实现

普通版本

server.c

#include
#include
#include
#include
#include
#include
#include
#include

#define SERV_PORT 9999
#define _BACKLOG_ 10

void Usage()
{
printf("Usage: [ipaddr]\n");
}

int main(int argc,char* argv[])
{
if(argc != 2)
{
Usage();
return -1;
}

//1、调用socket来打开一个网络通信端口
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock <0)
{
perror("socket");
exit(1);
}

//2、服务器来绑定一个固定的IP地址和端口号
struct sockaddr_in serverSocket;//定义服务端的套接字
struct sockaddr_in clientSocket;//定义客户端的套接字

bzero(&serverSocket,sizeof(serverSocket));//归零初始化

serverSocket.sin_family = AF_INET;//设置地址的类型
serverSocket.sin_addr.s_addr = inet_addr(argv[1]);

serverSocket.sin_port = htons(SERV_PORT);//设置端口号,SERV_PORT为自定义的宏,值设置为9999

if(bind(sock,(struct sockaddr*)&serverSocket,sizeof(struct sockaddr_in))<0)//进行绑定,并进行判断
{
perror("bind");
close(sock);
exit(2);
}

//3、进行监听
if(listen(sock,_BACKLOG_)<0)
{
perror("listen");
close(sock);
exit(3);
}

printf("绑定和监听成功...请等待连接...\n");

while(1)
{
//4、接受连接
socklen_t len = 0;//定义长度len
int clientSock = accept(sock, (struct sockaddr*)&clientSocket,&len);//接受连接
if(clientSock <0)
{
perror("accpet");
exit(4);
}

char buf[INET_ADDRSTRLEN];//定义缓冲区,用来存放IP字符串
memset(buf,0,sizeof(buf));//初始化为0

while(1)
{
char tmpBuf[1024];//定义空间来存储收到与发送的消息
ssize_t ret = read(clientSock,tmpBuf,sizeof(tmpBuf));//从客户端套接字中读入数据
if(ret <0)
{
perror("read");
exit(5);
}

tmpBuf[ret] = '\0';

printf("#client :# %s\n",tmpBuf);//打印从客户端读入的数据
printf("#server :# ");
memset(tmpBuf,'\0',sizeof(tmpBuf));//将buf空间置零
fgets(tmpBuf,sizeof(tmpBuf),stdin);//读入需要发送的数据
write(clientSock,tmpBuf,strlen(tmpBuf));
printf("请等待...\n");
}
}

close(sock);
return 0;
}

client.c

#include
#include
#include
#include
#include
#include

#define SERVER_PORT 9999

void Usage()
{
printf("Usage: [client_ip] [client_port]\n");
}

int main(int argc, char* argv[])
{
if(argc != 3)
{
Usage();
return 1;
}

//调用socket来打开一个网络端口
char buf[1024];
char* client_ip = argv[1];
memset(buf,'\0',sizeof(buf));
int sock = socket(AF_INET,SOCK_STREAM,0);

//定义sockaddr_in结构体,并进行初始化
struct sockaddr_in serverSocket;
serverSocket.sin_family = AF_INET;
serverSocket.sin_addr.s_addr = inet_addr(argv[1]);
serverSocket.sin_port = htons(SERVER_PORT);

//进行服务器的链接
int ret = connect(sock,(struct sockaddr*)&serverSocket,sizeof(serverSocket));
if(ret <0)
{
printf("连接失败...\n");
return 2;
}

printf("连接成功...\n");

while(1)
{
printf("#client :# ");
fflush(stdin);
fgets(buf,sizeof(buf),stdin);
buf[strlen(buf)-1] = '\0';

if(strcmp(buf,"quit")==0)
break;

write(sock,buf,sizeof(buf));

printf("请等待服务器响应...\n");
read(sock,buf,sizeof(buf));
printf("#server :# %s",buf);
}

close(sock);
return 0;
}

多进程版本

server.c

#include
#include
#include
#include
#include
#include

void Usage()
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}

int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}

int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd <0)
{
perror("sock");
exit(2);
}

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);


if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr)) <0)
{
perror("bind");
exit(3);
}

if(listen(sockfd,10) <0)
{
perror("listen");
exit(4);
}

printf("wait connect...\n");

while(1)
{
struct sockaddr_in saddr;
socklen_t straddr = sizeof(saddr);
int fd = accept(sockfd,(struct sockaddr *)&saddr,&straddr);

printf("accept connect...\n");

if(fd <0)
{
continue;
}

pid_t pid = fork();

if(pid <0)
{
perror("fork");
exit(6);
}
else if(pid > 0)
{
printf("find a new client... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
char buf[1024];
while(1)
{
int s = read(fd, buf,sizeof(buf)-1);
if(s <0)
{
perror("read");
exit(7);
}
else if(s == 0)
{
printf("connect quit... ip : %s ... port : %d\n",inet_ntoa(saddr.sin_addr),ntohs(saddr.sin_port));
close(fd);
break;
}
else
{
buf[s] = '\0';
printf("#client: %s\n",buf);
write(fd,buf,strlen(buf));
}
}
close(fd);
wait(pid,NULL,0);
}
else
{
continue;
}
}
close(sockfd);
return 0;
}

client.c

#include
#include
#include
#include
#include
#include
#include

void Usage()
{
printf("Usage: [ipaddr] [port]\n");
exit(1);
}

int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}

//打开一个网络端口
int sockfd = socket(AF_INET,SOCK_STREAM,0);

if(sockfd <0)
{
perror("socket");
exit(2);
}

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);

if(connect(sockfd,(struct sockaddr*)&addr, sizeof(addr))<0)
{
perror("connect");
exit(3);
}

printf("连接成功...\n");
char buf[1024];

while(1)
{
printf("#client: ");
fflush(stdout);
int s = read(0,buf,sizeof(buf)-1);
if(s <= 0)
{
perror("read");
exit(4);
}
else
{
buf[s-1] = 0;
write(sockfd,buf,strlen(buf));
}

int ret = read(sockfd,buf,sizeof(buf)-1);
if(ret <0)
{
perror("read");
exit(5);
}
else if(ret == 0)
{
printf("连接已断开...\n");
break;
}
else
{
buf[s] = '\0';
printf("#server: %s\n",buf);
}
}
close(sockfd);

return 0;
}

多线程版本

server.c

#include
#include
#include
#include
#include
#include
#include
#include
#include

void Usage()
{
printf("Usage: [addrip] [port]\n");
exit(1);
}

int StartUp(char* argv[])
{
//创建接口
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd <0)
{
perror("socket");
exit(2);
}

//进行绑定

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);

if(bind(sockfd,(struct sockaddr *)&addr,sizeof(addr)) <0)
{
perror("bind");
exit(3);
}

//进行监听
if(listen(sockfd,10) <0)
{
perror("listen");
exit(4);
}

return sockfd;
}

void* Myhandler(void* arg)
{
int fd = (int)arg;
char buf[1024];

while(1)
{
ssize_t s = read(fd,buf,sizeof(buf));
if(s <0)//error
{
perror("read");
exit(6);
}
else if(s == 0)//quit
{
printf("client quit...\n");
close(fd);
break;
}
else//write
{
buf[s] = 0;
printf("#client : %s\n",buf);
write(fd,buf,strlen(buf));
}
}

}

int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}

int sockfd = StartUp(argv);

printf("初始化成功...等待连接...\n");

while(1)
{
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
int fd = accept(sockfd,(struct sockaddr*)&addr,&addrlen);
if(fd <0)
{
continue;
}
printf("连接成功... ip : %s , port: %d\n",inet_ntoa(addr.sin_addr),ntohs(addr.sin_port));

pthread_t td;
pthread_create(&td,NULL,Myhandler,(void*)fd);
pthread_detach(td);
}

close(sockfd);
return 0;
}

client.c

#include
#include
#include
#include
#include
#include
#include
#include
#include
static void Usage()
{
printf("Usage : [ipaddr] [port]\n");
exit(1);
}

int StartUp(char* argv[])
{
//打开接口
int sockfd = socket(AF_INET,SOCK_STREAM,0);

struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(atoi(argv[2]));
addr.sin_addr.s_addr = inet_addr(argv[1]);

if(connect(sockfd,(struct sockaddr*)&addr,sizeof(addr)) <0)
{
perror("connect");
exit(2);
}
return sockfd;
}

int main(int argc,char* argv[])
{
if(argc != 3)
{
Usage();
}

int sockfd = StartUp(argv);

printf("连接成功...\n");

while(1)
{
printf("#client : ");
fflush(stdout);
char buf[1024];
ssize_t s = read(0,buf,sizeof(buf)-1);
if(s <= 0)
{
perror("read");
exit(2);
}
else
{
buf[s-1] = '\0';
write(sockfd,buf,strlen(buf));
}

s = read(sockfd,buf,sizeof(buf)-1);
if(s == 0)
{
printf("server quit...\n");
break;
}
else if(s <0)
{
perror("read");
exit(3);
}
else
{
buf[s] = '\0';
printf("#server : %s\n",buf);
}
}
close(sockfd);
return 0;
}



推荐阅读
  • 本文介绍了在mac环境下使用nginx配置nodejs代理服务器的步骤,包括安装nginx、创建目录和文件、配置代理的域名和日志记录等。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • Nginx使用(server参数配置)
    本文介绍了Nginx的使用,重点讲解了server参数配置,包括端口号、主机名、根目录等内容。同时,还介绍了Nginx的反向代理功能。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Hyperledger Fabric外部链码构建与运行的相关知识,包括在Hyperledger Fabric 2.0版本之前链码构建和运行的困难性,外部构建模式的实现原理以及外部构建和运行API的使用方法。通过本文的介绍,读者可以了解到如何利用外部构建和运行的方式来实现链码的构建和运行,并且不再受限于特定的语言和部署环境。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • 本文介绍了计算机网络的定义和通信流程,包括客户端编译文件、二进制转换、三层路由设备等。同时,还介绍了计算机网络中常用的关键词,如MAC地址和IP地址。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
author-avatar
榴莲牛奶
这个家伙不是很懒,但是也没留下什么!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有