linux中学习网络编程,最先开始的莫过于写一个client/server程序作为练习。我最近也在写这些东西,还得在这里感谢公司老大, 给了我这个实习生学习的机会,自己很菜啦
首先:我们了解到在server端最原始也最简单的方式莫过于第一步建立一个socket,第二步将socket与IP地址和端口进行绑定bind,第三步监听端口,等待client端和server的connect,第四步accept 接受连接,和client建立了网络连接,第五步如果需要接受client端数据,并发送数据,则使用send,recv函数对或者使用read/write函数对。
这种最简单的服务器只能处理client的一次请求,如果多个client需要与serve连接,则必须等待。这个example仅适用于练习,对于实际的应用来讲没有什么用处。
如果想要server处理多个请求,最先想到的改进方法莫过于使用多进程了,下面给出个例子,本人是使用c++技术写的,所以封装了一个类:
server.h文件:
#ifndef TEST_PROJECT_SERVER_H_
#define TEST_PROJECT_SERVER_H_
#define MY_PORT 7800
#define LISTEN_NUM 10
class Server{
public:
Server(){}
void ForkServer(const int &send_message);
};
#endif // end TEST_PROJECT_SERVER_H_
server.cc文件:
#include "server.h"
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void Server::ForkServer(const int &send_message){
int m_listen_sock_fd, m_data_sock_fd;
struct sockaddr_in server_addr; // server address
struct sockaddr_in client_addr; // client address
unsigned int sin_size;
if ((m_listen_sock_fd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket error\n");
exit(1);
}
server_addr.sin_family=PF_INET;
server_addr.sin_port=htons(MY_PORT);
server_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(server_addr.sin_zero), 0);
if (bind(m_listen_sock_fd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
if (listen(m_listen_sock_fd, LISTEN_NUM) == -1) {
perror("listen error");
exit(1);
}
while (1) {
sin_size = sizeof(struct sockaddr_in);
if ((m_data_sock_fd = accept(m_listen_sock_fd, (struct sockaddr *)&client_addr, &sin_size)) == -1) {
perror("accept");
continue;
}
printf("server: got connection from %s\n", inet_ntoa(client_addr.sin_addr));
if (!fork()) { // childprocess code
if (send(m_data_sock_fd, &send_message, sizeof(int), 0) == -1) {
perror("send");
close(m_data_sock_fd);
exit(0);
}
}
close(m_data_sock_fd); // parent process don't need child process
waitpid(-1,NULL,WNOHANG);// wait for childprocess end, delete all resources
}
}
int main (int argc, char *argv[]) {
Server server;
server.ForkServer(3);
}
另外为了方便测试这里也把client端的代码贴出来,无论server如何变化,client都可以用:
#ifndef TEST_PROJECT_CLIENT_H_
#define TEST_PROJECT_CLIENT_H_
#define MY_PORT 7800
class Client{
public:
Client(){}
void ForkClient(const char *host_name);
};
#endif // end TEST_PROJECT_CLIENT_H_
#include "client.h"
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
void Client::ForkClient(const char *host_name){
int sockfd, numbytes;
int result = 0;
struct hostent *he;
struct sockaddr_in their_addr;
if((he=gethostbyname(host_name))==NULL) {
}
if ((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
their_addr.sin_family=PF_INET;
their_addr.sin_port=htons(MY_PORT);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero),0);
if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof(struct sockaddr)) == -1) {
perror("connect");
exit(1);
}
char send_message[] = "hello server";
if (send(sockfd, send_message, sizeof(send_message), 0) == -1) {
perror("send error");
close(sockfd);
exit(0);
}
if ((numbytes=recv(sockfd, &result, sizeof(int), 0)) == -1) {
perror("recv");
exit(1);
}
printf("Received: %d\n",result);
close(sockfd);
}
int main(int argc, char *argv[])
{
if (argc != 2) {
fprintf(stderr,"usage: %s hostname\n", argv[0]);
exit(1);
}
if(argv[1] == NULL) {
herror("gethostbyname");
exit(1);
}
Client client;
client.ForkClient(argv[1]);
return 0;
}
从上述例子中可以看到虽然使用了多进程方式,但是改server还是block类型的,改例子并没有解决阻塞问题。
reference:
Linux下各类TCP网络服务器的实现源代码