实现一个网络编程:
使用传输协议:TCP
主要是写一些关于socket函数的使用。
大致实现这么一个功能:
客户端
发送1 服务器返回 Hello
发送2 服务器返回 GOOD
发送3 服务器返回 Bye 并且关闭连接。
先看客户端的程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <errno.h> #define LOG_MSG(format, ...) do{fprintf(stderr, "[MessageInfo]: "format" \n", ##__VA_ARGS__);}while(0) #define PORT_NUM (2227) //定义端口号。 #define BUFFSIZE (1024) #define CMD_ONE ("1") #define CMD_TWO ("2") #define CMD_THREE ("3") /* ************************************** @brief 连接到指定的Server @param(in) host: 服务器名 @param(in) port: 服务器端口 @return Success: socket描述符应该(大于0) Failure: -1 ************************************** */ int32_t jet_connect(const int8_t *host, uint16_t port) { int32_t sd = -1; int8_t serv[NI_MAXSERV]; struct addrinfo *ai, *res = NULL; struct addrinfo hints; //异常处理。 if(host == NULL){ goto func_end; } //snprintf会在最后加上一个\0,所以这里就不memset serv了。 if(snprintf(serv, NI_MAXSERV, "%u", port) < 0){ goto func_end; } (void)memset(&hints, 0x00, sizeof hints); //Ipv4 , Ipv6地址都可以 如果只要Ipv4那么这里填写:AF_INET, IPv6:AF_INET6 hints.ai_family = AF_UNSPEC; //TCP 为SOCK_STREAM, UDP为:SOCK_DGRAM hints.ai_socktype = SOCK_STREAM; //这里写不写都无所谓。 socket函数会根据family和sockettype自动匹配。 hints.ai_protocol = IPPROTO_TCP; //取得服务器的addrinfo if(getaddrinfo(host, serv, &hints, &res) != 0){ LOG_MSG("%s", gai_strerror(errno)); goto func_end; } for(ai = res; ai != NULL; ai = ai->ai_next){ sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(sd < 0){ continue; } if(connect(sd, ai->ai_addr, ai->ai_addrlen) < 0){ //Connect失败了, 所以socket描述符关闭,因为后面不用了。 close(sd); continue; } //成功连接上了。 不用在去循环了. break; } //上述循环是否成功 可以通过ai是否为NULL来判断。 if(ai == NULL){ sd = -1; goto func_end; } func_end: if(res != NULL){ freeaddrinfo(res); } return sd; } /* ************************************** 从stdin这里获取输入,然后发送给Server。 ************************************** */ void do_event(int32_t sd) { int8_t *buf = NULL; int8_t *sendbuf; int8_t sendlen; if((buf = (int8_t *)malloc(BUFFSIZE)) == NULL){ goto func_end; } (void)memset(buf, 0x00, BUFFSIZE); while(fgets(buf, BUFFSIZE, stdin) != NULL){ //Check Command... if(strncmp(buf, CMD_ONE, strlen(CMD_ONE)) == 0){ sendbuf = CMD_ONE; }else if(strncmp(buf, CMD_TWO, strlen(CMD_TWO)) == 0){ sendbuf = CMD_TWO; }else if(strncmp(buf, CMD_THREE, strlen(CMD_THREE)) == 0){ sendbuf = CMD_THREE; }else{ //客户端如果可以的话,还是尽量不要将明显的错误信息送出去。 LOG_MSG("The Command Err. Please Input 1~3"); (void)memset(buf, 0x00, BUFFSIZE); continue; } sendlen = strlen(sendbuf); if(write(sd, sendbuf, sendlen) < sendlen){ LOG_MSG("message send to server Error"); } (void)memset(buf, 0x00, BUFFSIZE); if(read(sd, buf, BUFFSIZE) < 0){ LOG_MSG("message receive from server Error"); } LOG_MSG("%s", buf); if(sendbuf == (int8_t*)CMD_THREE){ break; } (void)memset(buf, 0x00, BUFFSIZE); } func_end: if(buf != NULL){ free(buf); } return ; } int main(int ac, char **av) { int32_t sd; //需要输入Server的地址或名字。 if(ac != 2){ LOG_MSG("you should indicate a server"); goto func_end; } if((sd = jet_connect(av[1], PORT_NUM)) < 0){ LOG_MSG("Connect to Server Failed"); goto func_end; } do_event(sd); close(sd); func_end: return 0; }
同时给出服务器端 程序:(注释 基本没写..)
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/types.h> #include <netdb.h> #define PORT_NUM (2227) //定义端口号。 #define BUFFSIZE (1024) #define CMD_ONE ("1") #define CMD_TWO ("2") #define CMD_THREE ("3") #define CMD_CODE_ERR (0) #define CMD_CODE_ONE (1) #define CMD_CODE_TWO (2) #define CMD_CODE_THREE (3) #define LISTEN_QUE (10) #define LOG_MSG(format, ...) do{fprintf(stderr, "[MessageInfo]: "format" \n", ##__VA_ARGS__);}while(0) typedef struct socklnk_st{ int32_t sd; struct socklnk_st *next; }socklnk_t; socklnk_t* create_soclnk(int32_t data) { socklnk_t *soclnk = NULL; soclnk = (socklnk_t *)malloc(sizeof(socklnk_t)); if(soclnk != NULL){ soclnk->next = NULL; soclnk->sd = data; } return soclnk; } void free_soclnk(socklnk_t *dst) { socklnk_t *tmp = dst; socklnk_t *next; while(tmp != NULL){ next = tmp->next; close(tmp->sd); free(tmp); tmp = next; } } //定义每个Code返回值。 //这个是C99下,数组初始化才可以这么使用 static int8_t *server_msg[] = { [CMD_CODE_ONE] = "Hello", [CMD_CODE_TWO] = "Good", [CMD_CODE_THREE] = "Bye", [CMD_CODE_ERR] = "Error Command", }; socklnk_t * start_serice(uint16_t port) { int8_t serv[NI_MAXSERV]; int32_t sd; int32_t opt = 1; struct addrinfo *ai, *res = NULL; struct addrinfo hints; socklnk_t *head = NULL; socklnk_t *cur = NULL; if(snprintf(serv, NI_MAXSERV, "%u", port) < 0){ goto func_end; } (void)memset(&hints, 0x00, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_PASSIVE; if(getaddrinfo(NULL, serv, &hints, &res) != 0){ goto func_end; } for(ai = res; ai != NULL; ai = ai->ai_next){ sd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); if(sd < 0){ continue; } if(setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) < 0){ close(sd); free_soclnk(head); goto func_end; } if(bind(sd, ai->ai_addr, ai->ai_addrlen) < 0){ close(sd); continue; } if(listen(sd, LISTEN_QUE) < 0){ close(sd); continue; } if(head == NULL){ head = create_soclnk(sd); if(head == NULL){ close(sd); free_soclnk(head); goto func_end; } cur = head; }else{ cur->next = create_soclnk(sd); if(cur->next == NULL){ close(sd); free_soclnk(head); goto func_end; } cur = cur->next; } } func_end: if(res != NULL){ freeaddrinfo(res); } return head; } int main(int ac, char **av) { int8_t *buf; int32_t connfd; int32_t maxfd = -1; int32_t cmd; int32_t sendlen; fd_set fds; socklnk_t *sdlnk = NULL; socklnk_t *this; buf = (int8_t*)malloc(BUFFSIZE); if(buf == NULL){ LOG_MSG("Service Start Failed"); goto func_end; } sdlnk = start_serice(PORT_NUM); if(sdlnk == NULL){ LOG_MSG("Service Start Failed"); goto func_end; } //main Loop for(;;){ FD_ZERO(&fds); for(this = sdlnk; this != NULL; this = this->next){ FD_SET(this->sd, &fds); if(this->sd > maxfd){ maxfd = this->sd; } } if(select(maxfd + 1, &fds, NULL, NULL, NULL) > 0){ for(this = sdlnk; this != NULL; this = this->next){ if(FD_ISSET(this->sd, &fds) != 0){ if((connfd = accept(this->sd, NULL, NULL)) < 0){ continue; } break; } } }else{ goto func_end; } if(this == NULL){ goto func_end; } for(;;){ (void)memset(buf, 0x00, BUFFSIZE); if(read(connfd, buf, BUFFSIZE) > 0){ if(strncmp(buf, CMD_ONE, strlen(CMD_ONE)) == 0){ cmd = CMD_CODE_ONE; }else if(strncmp(buf, CMD_TWO, strlen(CMD_TWO)) == 0){ cmd = CMD_CODE_TWO; }else if(strncmp(buf, CMD_THREE, strlen(CMD_THREE)) == 0){ cmd = CMD_CODE_THREE; }else{ cmd = CMD_CODE_ERR; } sendlen = strlen(server_msg[cmd]); if(write(connfd, server_msg[cmd], sendlen) != sendlen){ close(connfd); goto func_end; } if(cmd == CMD_CODE_THREE){ break; } }else{ break; } } close(connfd); } func_end: if(sdlnk != NULL){ free_soclnk(sdlnk); } if(buf != NULL){ free(buf); } return 0; }
现在用客户端输入1,2,3 都可以从服务器哪里接受到信息了。。
但是好像有点什么不足的样子。。
下一篇 对 服务器 端 做一个改进/。