最近刚刚开始学习网络编程,今天看了童永清的《Linux C编程实战》一书,在看到这本书网络编程后面的部分,有一个实现面向连接的Client/Server实例,在此将它整理了一下,并加上一些自己的备注,希望对大家有所帮助。
先简要介绍一下这个实例:客户端通过IP和端口号向服务器发起连接,服务器收到连接后,打印客户端的IP,并请求客户端用户名和密码,客户端输入用户名和密码,服务器检查用户名和密码是否匹配,若匹配成功,则服务器在终端打印该用户已登录,客户端退成程序。
一、服务器端源代码
//my_server.c
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "my_recv.h"
#define SERV_PORT 6666 //port
#define LISTENQ 12 //the max request deque
#define VALID_USERINFO "y" //the flag of valid user
#define INVALID_USERINFO "n" //the flag of invalid user
#define USERNAME 0 //user name
#define PASSWORD 1 //password
//userinfo
typedef struct userinfo {
char username[32];
char password[32];
}userInfo;
//users
userInfo users[] = {
{"zhangsan", "123456"},
{"linux", "unix"}
};
/**
* Function:find_name
* Description:find the num of the name in the list of users
* Input: user name
* Output:the num of the user name
*/
int find_name(const char *name) {
int i = 0;
if (NULL == name) {
printf("name is NULL");
return -2;
}
while (users[i].username[0] != ' ') {
if (strcmp(users[i].username, name) == 0) {
return i;
}
++i;
}
return -1;
}
//send data to client
void send_data(int conn_fd, const char *string) {
if (send(conn_fd, string, strlen(string), 0) < 0) {
my_err("send", __LINE__);
}
}
int main(int args, char *argv[]) {
int sock_fd, conn_fd; //the descriptor of socket
int optval; //option value
int flag_recv = USERNAME; //the flag of name or password
int ret; //
int name_num; //the num of user
pid_t pid; //the id of the process
socklen_t cli_len;
struct sockaddr_in cli_addr, serv_addr;
char recv_buf[128]; //the buffer of the receive data
/**
* int socket(int domain, int type, int protocol);
* Input:
* domain: this selects the protocol family
* type: the type of socket
* 0:the protocol is decided by the first and the second argument
*Output::On success, a file descriptor for the new socket,
On error, -1 is returned, and errno is set appropriately.
*/
sock_fd = socket(AF_INET, SOCK_STREAM, 0); //create a new socket
if (sock_fd < 0) {
my_err("socket", __LINE__);
}
optval = 1;
//set the socket so that we can use it again
if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&optval, sizeof(int) ) < 0) {
my_err("setsockopt", __LINE__);
}
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//bind the socket to the local port
if (bind(sock_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr_in)) < 0) {
my_err("bind", __LINE__);
}
//set the socket to listen status
if (listen(sock_fd, LISTENQ) < 0) {
my_err("listen", __LINE__);
}
cli_len = sizeof(struct sockaddr_in);
while (1) {
//wait a request
conn_fd = accept(sock_fd, (struct sockaddr *)&cli_addr, &cli_len);
if (conn_fd < 0) {
my_err("accept", __LINE__);
}
printf("accept a new client, ip is %s\n",inet_ntoa(cli_addr.sin_addr));
if ((pid =fork()) == 0) {
while (1) {
if ((ret = recv(conn_fd, recv_buf, sizeof(recv_buf), 0)) < 0) {
perror("recv");
}
//the flag of end
recv_buf[ret - 1] = '\0';
if (flag_recv == USERNAME) {
name_num = find_name(recv_buf);
switch(name_num) {
case -1:
send_data(conn_fd, "n\n");
break;
case -2:
exit(1);
break;
default:
send_data(conn_fd, "y\n");
flag_recv = PASSWORD;
}
}
else if (flag_recv == PASSWORD) {
if (strcmp(users[name_num].password, recv_buf) == 0) {
//send data to client
send_data(conn_fd, "y\n");
send_data(conn_fd, "Welcome to my server\n");
printf("%s login\n", users[name_num].username);
}
else {
send_data(conn_fd, "n\n");
}
}
}
close(sock_fd);
close(conn_fd);
exit(0);
}
else {
close(conn_fd);
}
}
return 0;
}
二、客户端源代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "my_recv.h"
#define INVALID_USERINFO 'n'
#define VALID_USERINFO 'y'
//get user's information from user's input
int get_userinfo(char *buf, int len) {
int i;
int c;
if (buf == NULL) {
return -1;
}
i = 0;
while (((c = getchar()) != '\n') && (c != EOF) && (i < len-2)) {
buf[i++] = c;
}
buf[i++] = '\n';
buf[i++] = '\0';
return 0;
}
//get information of user from server
void input_userinfo(int conn_fd, const char *string) {
char input_buf[32];
char recv_buf[BUFSIZE];
int flag_userinfo;
do {
printf("%s:", string);
if (get_userinfo(input_buf, 32) < 0) {
printf("error return from get_userinfo\n");
exit(1);
}
//send user's information to server
if (send(conn_fd, input_buf, strlen(input_buf), 0) < 0) {
my_err("send", __LINE__);
}
//receive information from server
if (my_recv(conn_fd, recv_buf, sizeof(recv_buf)) < 0) {
printf("data is to long\n");
exit(1);
}
if (recv_buf[0] == VALID_USERINFO) {
flag_userinfo = VALID_USERINFO;
}
else {
printf("%s error, input again\n", string);
flag_userinfo = INVALID_USERINFO;
}
} while (flag_userinfo == INVALID_USERINFO);
}
int main(int argc, char *argv[]) {
int i;
int ret;
int conn_fd;
int serv_port;
struct sockaddr_in serv_addr;
char recv_buf[BUFSIZE];
if (argc != 5) {
printf("Usage: [-p] [serv_port] [-a] [serv_address]\n");
exit(1);
}
memset(&serv_addr, 0, sizeof(struct sockaddr_in));
serv_addr.sin_family = AF_INET;
for (i = 1; i < argc; ++i) {
//is the argument a flag of port
if (strcmp("-p", argv[i]) == 0) {
//atoi():convert a string into an integer
serv_port = atoi(argv[i+1]);
if (serv_port < 0 || serv_port > 65535) {
printf("invalid ser_addr.sinport\n");
exit(1);
}
else {
//htons(): host to network short
serv_addr.sin_port = htons(serv_port);
}
continue;
}
//is the argument a flag of ip
if (strcmp("-a", argv[i]) == 0) {
if (inet_aton(argv[i+1], &serv_addr.sin_addr) == 0) {
printf("invalid server ip address\n");
exit(1);
}
continue;
}
}
if (serv_addr.sin_port == 0 || serv_addr.sin_addr.s_addr == 0) {
printf("Usage: [-p] [serv_addr.sin_port] [-a] [ser_address]\n");
exit(1);
}
//create a socket
conn_fd = socket(AF_INET, SOCK_STREAM, 0);
if (conn_fd < 0) {
my_err("connect", __LINE__);
}
//connect server
if (connect(conn_fd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) < 0) {
my_err("connect", __LINE__);
}
input_userinfo(conn_fd, "username");
input_userinfo(conn_fd, "password");
//receive data from server
if ((ret = my_recv(conn_fd, recv_buf, sizeof(recv_buf))) < 0) {
printf("data is too long\n");
exit(1);
}
for (i = 0; i < ret; ++i) {
printf("%c", recv_buf[i]);
}
printf("\n");
close(conn_fd);
return 0;
}
三、读取数据以及错误处理
头文件的定义:
//my_recv.h
#ifndef _MY_RECV_H
#define _MY_RECV_H
#define BUFSIZE 1024
void my_err(const char *err_string, int line);
int my_recv(int conn_fd, char *data_buf, int len);
#endif
源代码:
//my_recv.c
#include
#include
#include
#include
#include
#include
#include "my_recv.h"
void my_err(const char *err_string, int line) {
fprintf(stderr, "line:%d ", line);
perror(err_string);
exit(1);
}
int my_recv(int conn_fd, char *data_buf, int len) {
static char recv_buf[BUFSIZE];
static char *pread;
static int len_remain = 0;
int i;
if (len_remain <= 0) {
if ((len_remain = recv(conn_fd, recv_buf, sizeof(recv_buf), 0)) < 0) {
my_err("recv", __LINE__);
}
else if (len_remain == 0) {
return 0;
}
pread = recv_buf;
}
for (i = 0; *pread != '\n'; ++i) {
if (i > len) {
return -1;
}
data_buf[i] = *pread++;
len_remain--;
}
len_remain--;
pread++;
return i;
}
五、运行并测试程序
首先运行服务端程序:
./my_server
./my_client -a 127.0.0.1 -p 6666