linux环境下c语言书写socket服务器程序的步骤和注意事项

合集下载

linux socket编程案例

linux socket编程案例

linux socket编程案例如何使用Linux Socket编程案例实现网络通信?一、介绍Socket编程是Linux操作系统中用于实现网络通信的一种编程接口。

通过Socket编程,我们可以在不同的主机之间建立网络连接,并进行数据传输。

本文将通过一个案例,一步一步介绍如何使用Linux Socket编程实现一个简单的网络通信应用。

二、准备工作在开始编写Socket程序之前,我们需要确保已经安装了Linux操作系统,并且具备一定的编程基础。

以下是本文案例所使用的环境和工具:- 操作系统:Ubuntu 20.04 LTS- 编程语言:C++- 编译器:g++三、案例背景我们打算实现一个简单的客户端-服务器模型。

客户端将向服务器发送一段文本,并在服务器端进行反转后返回给客户端。

四、服务器端代码实现1. 首先,我们创建一个服务器端的Socket,用于接收客户端连接:cpp#include <iostream>#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>int main() {创建Socketint serverSocket = socket(AF_INET, SOCK_STREAM, 0);if (serverSocket == -1) {std::cerr << "Failed to create socket." << std::endl;return -1;}设置监听地址struct sockaddr_in serverAddress;serverAddress.sin_family = AF_INET;serverAddress.sin_port = htons(8888);serverAddress.sin_addr.s_addr = INADDR_ANY;绑定Socket和地址if (bind(serverSocket, (struct sockaddr*)&serverAddress,sizeof(serverAddress)) == -1) {std::cerr << "Failed to bind socket." << std::endl;return -1;}开始监听if (listen(serverSocket, 5) == -1) {std::cerr << "Failed to listen on socket." << std::endl;return -1;}std::cout << "Server started, listening on port 8888." << std::endl;接受客户端连接struct sockaddr_in clientAddress;socklen_t clientAddressLength = sizeof(clientAddress);int clientSocket = accept(serverSocket, (structsockaddr*)&clientAddress, &clientAddressLength);处理客户端请求...关闭Socketclose(serverSocket);return 0;}以上代码中,我们先创建一个Socket对象,然后设置服务器的监听地址,接着绑定Socket和地址,最后开始监听客户端连接。

Linux下C语言的socket网络编程

Linux下C语言的socket网络编程

Linux下C语⾔的socket⽹络编程关于详细的服务器建⽴的步骤以及相关的socket套接字的知识我已经在python socket编程的⽂章中提到过了,⼤家可以参看那⼀篇博客来历接socket套接字编程的内容,由于要是⽤C相关的API所以这⾥采⽤了基于C语⾔的socket API编写相关的⽹络编程内容,具体的实现如下所⽰,调试通过。

⽂章链接:服务端Server.c程序内容:1 #include <sys/types.h>2 #include <sys/socket.h>3 #include <netinet/in.h>4 #include <arpa/inet.h>5 #include <netdb.h>6 #include <stdio.h>7 #include <errno.h>8 #include <stdlib.h>9 #include <string.h>10 #include <unistd.h>11/************************************************************************************************************************121、int socket(int family,int type,int protocol)13family:14指定使⽤的协议簇:AF_INET(IPv4) AF_INET6(IPv6) AF_LOCAL(UNIX协议) AF_ROUTE(路由套接字) AF_KEY(秘钥套接字)15type:16指定使⽤的套接字的类型:SOCK_STREAM(字节流套接字) SOCK_DGRAM17protocol:18如果套接字类型不是原始套接字,那么这个参数就为0192、int bind(int sockfd, struct sockaddr *myaddr, int addrlen)20sockfd:21 socket函数返回的套接字描述符22myaddr:23是指向本地IP地址的结构体指针24myaddrlen:25结构长度26struct sockaddr{27 unsigned short sa_family; //通信协议类型族AF_xx28 char sa_data[14]; //14字节协议地址,包含该socket的IP地址和端⼝号29};30struct sockaddr_in{31 short int sin_family; //通信协议类型族32 unsigned short int sin_port; //端⼝号33 struct in_addr sin_addr; //IP地址34 unsigned char si_zero[8]; //填充0以保持与sockaddr结构的长度相同35};363、int connect(int sockfd,const struct sockaddr *serv_addr,socklen_t addrlen)37sockfd:38 socket函数返回套接字描述符39serv_addr:40服务器IP地址结构指针41addrlen:42结构体指针的长度434、int listen(int sockfd, int backlog)44sockfd:45 socket函数绑定bind后套接字描述符46backlog:47设置可连接客户端的最⼤连接个数,当有多个客户端向服务器请求时,收到此值的影响。

(完整word版)linux环境下c语言书写socket服务器程序的步骤和注意事项

(完整word版)linux环境下c语言书写socket服务器程序的步骤和注意事项
{
printf("bind error\n");
return 0;
}
printf("bind-ret=[%d]\n",ret);
ret=-9;
if((ret=listen(sock,100)<0))////****listen()
{
printf("listen error\n");
return 0;
{
printf("accept error\n");
continue;
}
else
{
printf("socket accept from sock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);
printf("SOCKET BEGIN===================\n");
int n=0;
memset(data_buf,0,MAXLINE);
if(0>=(n=recv(sockettemp,data_buf,MAXLINE,0)))
{
continue;ຫໍສະໝຸດ }else{
send(sockettemp,ch_send_back,strlen(ch_send_back),0);
bzero(&(server.sin_zero),8);
setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int));
//bind
if((ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)))<0)////****bind()

LinuxC编程之十六网络编程基础-socket

LinuxC编程之十六网络编程基础-socket

LinuxC编程之⼗六⽹络编程基础-socket⼀、协议的概念1. 什么是协议从应⽤的⾓度出发,协议可理解为“规则”,是数据传输和数据的解释的规则。

假设,A、B双⽅欲传输⽂件。

规定:第⼀次,传输⽂件名,接收⽅接收到⽂件名,应答OK给传输⽅;第⼆次,发送⽂件的尺⼨,接收⽅接收到该数据再次应答⼀个OK;第三次,传输⽂件内容。

同样,接收⽅接收数据完成后应答OK表⽰⽂件内容接收成功。

由此,⽆论A、B之间传递何种⽂件,都是通过三次数据传输来完成。

A、B之间形成了⼀个最简单的数据传输规则。

双⽅都按此规则发送、接收数据。

A、B之间达成的这个相互遵守的规则即为协议。

这种仅在A、B之间被遵守的协议称之为原始协议。

当此协议被更多的⼈采⽤,不断的增加、改进、维护、完善。

最终形成⼀个稳定的、完整的⽂件传输协议,被⼴泛应⽤于各种⽂件传输过程中。

该协议就成为⼀个标准协议。

最早的ftp协议就是由此衍⽣⽽来。

TCP协议注重数据的传输。

http协议着重于数据的解释。

2. 典型协议传输层:常见协议有TCP/UDP协议。

应⽤层:常见的协议有HTTP协议,FTP协议。

⽹络层:常见协议有IP协议、ICMP协议、IGMP协议。

⽹络接⼝层:常见协议有ARP协议、RARP协议。

TCP传输控制协议(Transmission Control Protocol)是⼀种⾯向连接的、可靠的、基于字节流的传输层通信协议。

UDP⽤户数据报协议(User Datagram Protocol)是OSI参考模型中⼀种⽆连接的传输层协议,提供⾯向事务的简单不可靠信息传送服务。

HTTP超⽂本传输协议(Hyper Text Transfer Protocol)是互联⽹上应⽤最为⼴泛的⼀种⽹络协议。

FTP⽂件传输协议(File Transfer Protocol)IP协议是因特⽹互联协议(Internet Protocol)ICMP协议是Internet控制报⽂协议(Internet Control Message Protocol)它是TCP/IP协议族的⼀个⼦协议,⽤于在IP主机、路由器之间传递控制消息。

Linux下的CSocket编程--server端的简单示例

Linux下的CSocket编程--server端的简单示例

Linux下的CSocket编程--server端的简单⽰例Linux下的C Socket编程(三)server端的简单⽰例经过前⾯的client端的学习,我们已经知道了如何创建socket,所以接下来就是去绑定他到具体的⼀个端⼝上⾯去。

绑定socket到⼀个端⼝上bind()函数可以将socket绑定到⼀个端⼝上,client可以通过向这个端⼝发起请求,端⼝对应的socket便会与client端的socket连接。

#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>int main() {int socket_desc;struct sockaddr_in server;socket_desc = socket(AF_INET, SOCK_STREAM, 0);if (-1 == socket_desc) {perror("cannot create socket");exit(1);}// 监听服务器⾃⾝server.sin_addr.s_addr = INADDR_ANY;server.sin_family = AF_INET;server.sin_port = htons(8888);// 绑定到端⼝if (bind(socket_desc, (struct sockaddr *)&server, sizeof(server)) < 0) {perror("cannot bind error");exit(1);}printf("bind success");close(socket_desc);return 0;}对于server.sin_addr.s_addr的更多信息可以参考通过将socket绑定到⼀个确定的端⼝上,我们接下来要做的便是接收这个端⼝下的所有数据。

linux下C语言socket网络编程

linux下C语言socket网络编程
#include <string.h>
int main()
{
int cfd; /* 文件描述符 */
int recbytes;
int sin_size;
char buffer[1024]={0}; /* 接受缓冲区 */
struct sockaddr_in s_add,c_add; /* 存储服务端和本端的ip、端口等信息结构体 */
if(-1 == cfd)
{
printf("socket fail ! \r\n");
return -1;
}
printf("socket ok !\r\n");
/* 构造服务器端的ip和端口信息,具体结构体可以查资料 */
bzero(&s_add,sizeof(struct sockaddr_in));
}
printf("socket ok !\r\n");
/* 填充服务器端口地址信息,以便下面使用此地址和端口监听 */
bzero(&s_add,sizeof(struct sockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY); /* 这里地址使用全0,即所有 */
}
/*连接成功,从服务端接收字符*/
if(-1 == (recbytes = read(cfd,buffer,1024)))
{
printf("read data fail !\r\n");
return -1;

Linux socket 编程

Linux socket 编程在开始之前关于这本教程IP socket 是在其上建立高级 Internet 协议的最低级的层:从HTTP 到 SSL 到 POP3 到 Kerberos 再到 UDP-Time,每种 Internet 协议都建立在它的基础上。

为了实现自定义的协议,或定制众所周知的协议的实现,程序员需要掌握基本的 socket 基础结构的工作知识。

虽然本教程主要集中在 C 编程上,而且还使用 Python 作为例子中的代表性高级语言,不过类似的 API 在许多语言中都可用。

本教程将向您介绍使用跨平台的 Berkeley Sockets Interface 编写自定义的网络工具程序的基本知识。

几乎所有 Linux 和其他基于UNIX 的操作系统上的网络工具都依赖这个接口。

学习本教程需要具备最低限度的 C 语言知识,同时熟悉 Python 则更理想(主要是为了下面的第二部分)。

然而,如果您对任何一种编程语言都不熟悉,那么您就应该付出一点额外的努力来学习一点编程语言;一种编程语言的大多数基本概念都可以同样应用到其它语言上,而且在诸如 Ruby、Perl、TCL 之类的大多数高级脚本语言中的称呼都相当类似。

正如上一小节所指出的,当您编写 socket 应用程序的时候,您可以在使用 TCP 还是使用 UDP 之间做出选择。

它们都有各自的优点和缺点。

TCP 是流协议,而UDP是数据报协议。

换句话说,TCP 在客户机和服务器之间建立持续的开放连接,在该连接的生命期内,字节可以通过该连接写出(并且保证顺序正确)。

然而,通过 TCP 写出的字节没有内置的结构,所以需要高层协议在被传输的字节流内部分隔数据记录和字段。

另一方面,UDP 不需要在客户机和服务器之间建立连接,它只是在地址之间传输报文。

UDP 的一个很好特性在于它的包是自分隔的(self-delimiting),也就是一个数据报都准确地指出它的开始和结束位置。

c socket 编程

c socket 编程C语言中使用Socket编程可以实现网络通信,主要针对TCP和UDP两种协议。

下面是C Socket编程的相关参考内容。

1. 应用层通信模型:- 客户端/服务器模型:客户端向服务器发送请求,服务器接收请求并发送回复。

- 对等通信模型:两个或多个进程之间直接通信,不需要中间服务器。

2. Socket编程流程:- 创建Socket:使用`socket()`函数创建一个Socket。

- 绑定Socket:使用`bind()`函数将Socket绑定到一个特定的地址和端口号。

- 监听连接请求:对于服务器端,使用`listen()`函数监听连接请求。

- 接收连接请求:对于服务器端,使用`accept()`函数接收连接请求。

- 建立连接:对于客户端,使用`connect()`函数连接到服务器。

- 发送和接收数据:使用`send()`和`recv()`函数发送和接收数据。

- 关闭连接:使用`close()`函数关闭Socket连接。

3. TCP Socket编程:- 创建Socket:使用`socket(AF_INET, SOCK_STREAM, 0)`函数创建TCP Socket。

- 绑定Socket:使用`bind()`函数将Socket绑定到服务器的地址和端口号。

- 监听连接请求:使用`listen()`函数开始监听连接请求。

- 接收连接请求:使用`accept()`函数接收来自客户端的连接请求,并创建一个新的Socket用于通信。

- 建立连接:使用`connect()`函数连接到服务器的地址和端口号。

- 发送和接收数据:使用`send()`和`recv()`函数发送和接收数据。

- 关闭连接:使用`close()`函数关闭Socket连接。

4. UDP Socket编程:- 创建Socket:使用`socket(AF_INET, SOCK_DGRAM, 0)`函数创建UDP Socket。

linux socket编程 c语言

linux socket编程 c语言Linux Socket编程是一种用于在不同进程之间,或在不同计算机之间进行通信的技术。

在C语言中,我们可以使用Socket API来创建和使用Sockets。

Socket编程主要涉及到以下几个步骤:创建Socket:首先,我们需要使用socket()函数来创建一个新的Socket。

这个函数需要三个参数:协议族(例如,AF_INET表示IPv4,AF_INET6表示IPv6),Socket类型(例如,SOCK_STREAM表示TCP,SOCK_DGRAM表示UDP),以及协议(通常为0,表示使用默认的协议)。

绑定Socket:然后,我们需要使用bind()函数将Socket绑定到一个特定的地址和端口。

这个函数需要三个参数:Socket的文件描述符,一个包含地址和端口信息的sockaddr结构体,以及这个结构体的大小。

监听和接受连接:对于服务器端的Socket,我们还需要使用listen()函数来监听来自客户端的连接请求。

当有客户端连接时,我们可以使用accept()函数来接受这个连接,并返回一个新的Socket文件描述符用于与这个客户端进行通信。

发送和接收数据:一旦Socket建立好连接,我们就可以使用send()或write()函数来发送数据,使用recv()或read()函数来接收数据。

关闭Socket:最后,当我们完成通信后,我们需要使用close()函数来关闭Socket,释放相关资源。

下面是一个简单的TCP服务器和客户端的示例代码:c// TCP服务器#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#define PORT 8080int main() {int server_fd, new_socket;struct sockaddr_in address;int opt = 1;int addrlen = sizeof(address);char buffer[1024] = {0};// 创建socket文件描述符if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0 {perror("socket failed");exit(EXIT_FAILURE);}// 绑定socket到端口address.sin_family = AF_INET;address.sin_addr.s_addr = INADDR_ANY;address.sin_port = htons(PORT);if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed");exit(EXIT_FAILURE);}// 开始监听if (listen(server_fd, 3) < 0) {perror("listen");exit(EXIT_FAILURE);}// 接受连接if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {perror("accept");exit(EXIT_FAILURE);}// 接收并打印数据read(new_socket, buffer, 1024);printf("%s\n", buffer);// 关闭socketclose(new_socket);close(server_fd);return 0;}// TCP客户端#include <stdio.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <netdb.h>#define PORT 8080int main() {struct sockaddr_in serv_addr;int sock = 0, valread;struct sockaddr_in serv_addr_in;char *hello = "Hello from client";char buffer[1024] = {0};// 创建socket文件描述符if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { printf("\n Socket creation error \n");return -1;}// 设置服务器地址serv_addr_in.sin_family = AF_INET;serv_addr_in.sin_port = htons(PORT);// 转换服务器地址if (inet_pton(AF_INET。

linux创建socket收发链路层报文的c语言代码

linux创建socket收发链路层报文的c语言代码引言概述:在Linux操作系统中,使用C语言编写代码可以创建socket并进行收发链路层报文的操作。

本文将详细介绍如何使用C语言编写代码来实现这一功能。

正文内容:1. socket的创建1.1. 引入必要的头文件:在C语言代码中,需要引入一些必要的头文件,如<sys/types.h>、<sys/socket.h>和<netinet/in.h>等,以便使用相关的函数和数据结构。

1.2. 创建socket:使用socket()函数可以创建一个socket,该函数需要指定协议族、套接字类型和协议类型等参数。

常用的协议族有AF_PACKET(链路层协议族)、AF_INET(IPv4协议族)和AF_INET6(IPv6协议族)等。

1.3. 设置socket选项:可以使用setsockopt()函数来设置socket的选项,如设置接收和发送缓冲区的大小等。

2. 绑定socket2.1. 创建一个用于绑定的结构体:使用struct sockaddr_ll结构体来保存链路层地址信息,包括接口索引、协议类型和目标MAC地址等。

2.2. 绑定socket:使用bind()函数将socket与特定的链路层地址绑定,以便接收和发送链路层报文。

3. 发送链路层报文3.1. 构建报文:使用C语言的数据结构和函数来构建链路层报文,包括设置目标MAC地址、源MAC地址、协议类型和数据等。

3.2. 发送报文:使用sendto()函数发送链路层报文,该函数需要指定socket、报文数据和报文长度等参数。

4. 接收链路层报文4.1. 创建一个接收缓冲区:使用malloc()函数动态分配一个足够大的缓冲区来接收链路层报文。

4.2. 接收报文:使用recvfrom()函数接收链路层报文,该函数需要指定socket、接收缓冲区和缓冲区大小等参数。

5. 关闭socket5.1. 关闭socket:使用close()函数关闭已创建的socket,释放相关资源。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

在linux下用c语言编写socket服务器。

首先分析需要解决的问题:1.Socket的处理机制。

一般的处理方法是在服务端socket初始化和建立监听之后,开启一个while死循环。

不断接受传递过来的socket链接请求,如果链接请求成功,开启一个子进程fork,在子进程中对socket进行处理。

处理完毕之后,子进程结束退出。

经本人测试研究发现了1中框架的几个问题,比较的突出。

具体如下。

2.开启子进程容易产生僵尸进程。

一开始的代码不完善,在子进程中exit后,父进程没有对其进行操作,导致了子进程成为僵尸进程,处理此问题有3种方法,将在后面进行详细说明。

3.Socket的处理容易不严谨,导致程序奔溃。

Socket的处理要严谨,否则在程序的运行过程中会出现socket资源不能释放的问题。

4.日志文件的处理。

在多进程(线程)处理中会面临一个日志文件不可共享写的问题。

本文中的代码均为作者测试使用过,在本文的最后会附上所有代码,如有疑问或者需要可以联系作者,qq:398810897。

具体问题分析:1.socket的基本框架(代码文件为server01.c)。

在windows和linux下的socket编程基本上是一样的,没有多大的差别,都是遵循着创建socket、定义socket(socket赋值)、绑定socket、开启监听、socket收发、关闭socket的流程来的,在windows下一开始还需要初始化socket版本,在这里主要针对的linux环境下的socket,所以windows下的东西就不细说了。

创建可以收发数据的socket的必须的步骤如下://创建socketsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){printf("socket error\n");return 0;}printf("sock-ret=[%d]\n",sock);//网络字节序server.sin_family=AF_INET;server.sin_addr.s_addr=INADDR_ANY;server.sin_port=htons(6000);//.........MYPORTbzero(&(server.sin_zero),8);setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int));//bindif((ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)))<0)////****bind(){printf("bind error\n");return 0;}printf("bind-ret=[%d]\n",ret);ret=-9;if((ret=listen(sock,100)<0))////****listen(){printf("listen error\n");return 0;}printf("listen-ret=[%d]\n",ret);在socket的创建以及初始化之后,做一个while(1)的死循环进行和客户端的链接。

while(1){namelen=sizeof(struct sockaddr_in);//sockaddr_in:地址信息if((msgsock=accept(sock,(struct sockaddr *)&client,&namelen))<0)////****accept(){printf("accept error\n");continue;}else{。

}。

}如果有客户端链接成功,即开启一个子进程来处理相关的流程。

在else中处理这个问题,在这里把else里面的代码贴出来。

else{printf("socket accept from sock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);printf("SOCKET BEGIN===================\n");if(!fork())//child{int sockettemp=msgsock;while(1){char data_buf[MAXLINE];//socket recvchar ch_send_back[300];//socket backint n=0;memset(data_buf,0,MAXLINE);if(0>=(n=recv(sockettemp,data_buf,MAXLINE,0))){continue;}else{send(sockettemp,ch_send_back,strlen(ch_send_back),0);close(sockettemp);break;}}close(sockettemp);printf("SOCKET END ===================\n");exit(0);}以上就是一个简单的socket服务器程序的框架了。

2.僵尸进程的处理。

在程序运行之后,会出现如下的问题,就是fork了子进程之后,父进程便不再处理子进程,导致了僵尸进程的出现。

如下图所示,会出现一个[server01] <defunct>的僵尸进程:为了避免这个问题的出现,现有四种方式可以处理。

(1) 父进程通过wait和waitpid等函数等待子进程结束,这会导致父进程挂起。

执行wait()或waitpid()系统调用,则子进程在终止后会立即把它在进程表中的数据返回给父进程,此时系统会立即删除该进入点。

在这种情形下就不会产生defunct进程。

(2) 如果父进程很忙,那么可以用signal函数为SIGCHLD安装handler。

在子进程结束后,父进程会收到该信号,可以在handler中调用wait回收。

(3) 如果父进程不关心子进程什么时候结束,那么可以用signal(SIGCLD, SIG_IGN)或signal(SIGCHLD, SIG_IGN)通知内核,自己对子进程的结束不感兴趣,那么子进程结束后,内核会回收,并不再给父进程发送信号(4)fork两次,父进程fork一个子进程,然后继续工作,子进程fork一个孙进程后退出,那么孙进程被init接管,孙进程结束后,init会回收。

不过子进程的回收还要自己做。

具体的测试结果:(1)方法是可以避免僵尸进程的产生,但是效率很低,每一次fork了子进程,父进程都要等到子进程exit了才能继续运行,不适合在并发量很高的服务器程序上使用。

(2)在程序中定义一个signal的函数void proc_child(int SIGNO);然后在server监听了之后,和客户端链接之前,调用此函数signal(SIGCHLD, proc_child);函数的具体实现如下:void proc_child(int SIGNO){int pid = -1;int stat;pid = waitpid(-1, &stat, WNOHANG);}具体代码参考server02.c。

试验结果还是比较满意,在单客户端访问时,效率几乎没什么影响,也不会产生僵尸进程,但是多个客户端,或者长期大量的链接的时候还是会产生僵尸进程。

测试环境比较难以搭建。

(3)方法可以,而且简单容易实现,具体代码参考server03.c。

(4)没有测试,所以这里就只好不说了。

3.在测试的过程中发现了由于作者一开始对socket没有释放导致程序不能收发数据的现象。

主要是在accept之后,跳出了fork之后没有close造成的。

4.日志文件是一个比较麻烦的问题。

一般情况下,日志要写到一起,只能一个一个的进程对文件进行操作,必然影响程序的并发性。

这种情况下就需要建立一个缓冲区,将日志的数据保存到缓冲区中,然后在定时刷新,将缓冲区中的数据写到日志里面去。

在windows下,作者比较喜欢使用一个动态链接库建立缓冲区的方法来实现,而在linux 下,作者使用了一个比较简单的pipe(管道)的方法来实现这个。

、具体的代码见server04.附录-代码Server01:/**/#include <stdio.h>#include <stdlib.h>#include <memory.h>#include <errno.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <signal.h>#define MAXLINE 4096int main(){int sock;//server socketstruct sockaddr_in server;//server socketaddr_instruct sockaddr_in client;char msgsock;//client socketint namelen;//socket addr lengthint bReuseaddr=1;int ret=-9;int client_id=0;//创建socketsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){printf("socket error\n");return 0;}printf("sock-ret=[%d]\n",sock);//网络字节序server.sin_family=AF_INET;server.sin_addr.s_addr=INADDR_ANY;server.sin_port=htons(6000);//.........MYPORTbzero(&(server.sin_zero),8);setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int));//bindif((ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)))<0)////****bind() {printf("bind error\n");return 0;}printf("bind-ret=[%d]\n",ret);ret=-9;if((ret=listen(sock,100)<0))////****listen(){printf("listen error\n");return 0;}printf("listen-ret=[%d]\n",ret);while(1){namelen=sizeof(struct sockaddr_in);//sockaddr_in:地址信息if((msgsock=accept(sock,(struct sockaddr*)&client,&namelen))<0)////****accept(){printf("accept error\n");continue;}else{printf("socket accept fromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);printf("SOCKET BEGIN===================\n");if(!fork())//child{int sockettemp=msgsock;while(1){char data_buf[MAXLINE];//socket recvchar ch_send_back[300];//socket backint n=0;memset(data_buf,0,MAXLINE);if(0>=(n=recv(sockettemp,data_buf,MAXLINE,0))){continue;}else{send(sockettemp,ch_send_back,strlen(ch_send_back),0);close(sockettemp);break;}}close(sockettemp);printf("SOCKET END ===================\n");exit(0);}else//father{close(msgsock);}}usleep(10);}return 0;}Server02:/**/#include <stdio.h>#include <stdlib.h>#include <memory.h>#include <errno.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <signal.h>#define MAXLINE 4096void proc_child(int SIGNO){int pid = -1;int stat;pid = waitpid(-1, &stat, WNOHANG);}int main(){int sock;//server socketstruct sockaddr_in server;//server socketaddr_instruct sockaddr_in client;char msgsock;//client socketint namelen;//socket addr lengthint bReuseaddr=1;int ret=-9;int client_id=0;//创建socketsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){printf("socket error\n");return 0;}printf("sock-ret=[%d]\n",sock);//网络字节序server.sin_family=AF_INET;server.sin_addr.s_addr=INADDR_ANY;server.sin_port=htons(6000);//.........MYPORTbzero(&(server.sin_zero),8);setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int)); //bindif((ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)))<0)////****bind() {printf("bind error\n");return 0;}printf("bind-ret=[%d]\n",ret);ret=-9;if((ret=listen(sock,100)<0))////****listen(){printf("listen error\n");return 0;}printf("listen-ret=[%d]\n",ret);signal(SIGCHLD, proc_child);//signal(SIGCHLD, SIG_IGN);while(1){namelen=sizeof(struct sockaddr_in);//sockaddr_in:地址信息if((msgsock=accept(sock,(struct sockaddr*)&client,&namelen))<0)////****accept(){printf("accept error\n");continue;}else{printf("socket accept fromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);printf("SOCKET BEGIN===================\n");if(!fork())//child{int sockettemp=msgsock;while(1){char data_buf[MAXLINE];//socket recvchar ch_send_back[300];//socket backint n=0;memset(data_buf,0,MAXLINE);if(0>=(n=recv(sockettemp,data_buf,MAXLINE,0))){continue;}else{send(sockettemp,ch_send_back,strlen(ch_send_back),0);close(sockettemp);break;}}close(sockettemp);printf("SOCKET END ===================\n");exit(0);}else//father{close(msgsock);}}usleep(10);}return 0;}void proc_child(int SIGNO){int pid = -1;int stat;pid = waitpid(-1, &stat, WNOHANG);}Server03:/**/#include <stdio.h>#include <stdlib.h>#include <memory.h>#include <errno.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <signal.h>#define MAXLINE 4096void proc_child(int SIGNO){int pid = -1;int stat;pid = waitpid(-1, &stat, WNOHANG);}int main(){int sock;//server socketstruct sockaddr_in server;//server socketaddr_instruct sockaddr_in client;char msgsock;//client socketint namelen;//socket addr lengthint bReuseaddr=1;int ret=-9;int client_id=0;//创建socketsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){printf("socket error\n");return 0;}printf("sock-ret=[%d]\n",sock);//网络字节序server.sin_family=AF_INET;server.sin_addr.s_addr=INADDR_ANY;server.sin_port=htons(6000);//.........MYPORTbzero(&(server.sin_zero),8);setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int)); //bindif((ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)))<0)////****bind() {printf("bind error\n");return 0;}printf("bind-ret=[%d]\n",ret);ret=-9;if((ret=listen(sock,100)<0))////****listen(){printf("listen error\n");return 0;}printf("listen-ret=[%d]\n",ret);// signal(SIGCHLD, proc_child);signal(SIGCHLD, SIG_IGN);while(1){namelen=sizeof(struct sockaddr_in);//sockaddr_in:地址信息if((msgsock=accept(sock,(struct sockaddr*)&client,&namelen))<0)////****accept(){printf("accept error\n");continue;}else{printf("socket accept fromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);printf("SOCKET BEGIN===================\n");if(!fork())//child{int sockettemp=msgsock;while(1){char data_buf[MAXLINE];//socket recvchar ch_send_back[300];//socket backint n=0;memset(data_buf,0,MAXLINE);if(0>=(n=recv(sockettemp,data_buf,MAXLINE,0))){continue;}else{send(sockettemp,ch_send_back,strlen(ch_send_back),0);close(sockettemp);break;}}close(sockettemp);printf("SOCKET END ===================\n");exit(0);}else//father{close(msgsock);}}usleep(10);}return 0;}void proc_child(int SIGNO){int pid = -1;int stat;pid = waitpid(-1, &stat, WNOHANG);}Server04/**/#include <stdio.h>#include <stdlib.h>#include <memory.h>#include <errno.h>#include <fcntl.h>#include <sys/socket.h>#include <sys/wait.h>#include <netinet/in.h>#include <arpa/inet.h>#include <string.h>#include <signal.h>#define MAXLINE 4096#include "logfile.h"void proc_child(int SIGNO);int main(){int sock;//server socketstruct sockaddr_in server;//server socketaddr_instruct sockaddr_in client;char msgsock;//client socketint namelen;//socket addr lengthint bReuseaddr=1;int ret=-9;int client_id=0;FILE * pFile;//日志句柄int pipe_fd[2];//管道char ch_log[1000];//开启日志进程,打开日志文件,打开管道pFile=SetFileHandle();if(pipe(pipe_fd)<0){printf("pipe create error\n");return -1;}if(!fork())//开启日志进程{while(1){WriteLog(pipe_fd[0],NULL,pFile);usleep(100);//100ms刷新一次日志}}else{//WritePipe(pipe_fd[1],"[LOG-LOG]log process begin.");}//打开设备//创建socketsock=socket(AF_INET,SOCK_STREAM,0);if(sock<0){printf("socket error\n");sprintf(ch_log,"[SOCK] socket create Error!");//WritePipe(pipe_fd[1],ch_log);return 0;}printf("sock-ret=[%d]\n",sock);sprintf(ch_log,"[SOCK] socket create Ok,socket=%d!",sock);//WritePipe(pipe_fd[1],ch_log);//网络字节序server.sin_family=AF_INET;server.sin_addr.s_addr=INADDR_ANY;server.sin_port=htons(6000);//.........MYPORTbzero(&(server.sin_zero),8);setsockopt(sock,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(int)); //bindif((ret=bind(sock,(struct sockaddr *)&server,sizeof(struct sockaddr)))<0)////****bind() {printf("bind error\n");sprintf(ch_log,"[SOCK] socket bind Error!");//WritePipe(pipe_fd[1],ch_log);return 0;}sprintf(ch_log,"[SOCK] socket bind Ok!");//WritePipe(pipe_fd[1],ch_log);printf("bind-ret=[%d]\n",ret);ret=-9;if((ret=listen(sock,100)<0))////****listen(){printf("listen error\n");sprintf(ch_log,"[SOCK] socket listen Error!");//WritePipe(pipe_fd[1],ch_log);return 0;}printf("listen-ret=[%d]\n",ret);sprintf(ch_log,"[SOCK] socket listen Ok!");////WritePipe(pipe_fd[1],ch_log);//signal(SIGCHLD, proc_child);signal(SIGCHLD, SIG_IGN);while(1){printf("accept error\n");namelen=sizeof(struct sockaddr_in);//sockaddr_in:地址信息if((msgsock=accept(sock,(struct sockaddr*)&client,&namelen))<0)////****accept(){printf("accept error\n");continue;}else{sprintf(ch_log,"[SOCK] get connect fromsock=%d,ip=%s,port=%d",msgsock,inet_ntoa(client.sin_addr),client.sin_port);//WritePipe(pipe_fd[1],ch_log);printf("socket accept fromsock=%d,ip=%s,port=%d\n",msgsock,inet_ntoa(client.sin_addr),client.sin_port);printf("SOCKET BEGIN===================\n");if(!fork())//child{int sockettemp=msgsock;while(1){char data_buf[MAXLINE];//socket recvchar ch_send_back[300];//socket backint n=0;memset(data_buf,0,MAXLINE);if(0>=(n=recv(sockettemp,data_buf,MAXLINE,0))){continue;}else{sprintf(ch_log,"[SOCK] recv data fromsock=%d,data=%s",sockettemp,data_buf);//WritePipe(pipe_fd[1],ch_log);//WritePipe(pipe_fd[1],ch_log);send(sockettemp,ch_send_back,strlen(ch_send_back),0);sprintf(ch_log,"[SOCK] socket sendback data=%s",ch_send_back);//WritePipe(pipe_fd[1],ch_log);close(sockettemp);break;}}close(sockettemp);printf("SOCKET END ===================\n");exit(0);}else//father{close(msgsock);}}usleep(10);}return 0;}void proc_child(int SIGNO){int pid = -1;int stat;pid = waitpid(-1, &stat, WNOHANG);}//---------------------------------------------------//#endif附录-管道资料、管道概述及相关API应用1.1 管道相关的关键概念管道是Linux 支持的最初Unix IPC 形式之一,具有以下特点:•管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;•只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);•单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

相关文档
最新文档