一个基本的C/S服务器模型很简单: 客户端 <------------------------> 服务器
简而言之就是客户端跟服务器之间的通话,通话方式一般采用TCP和UDP这两种。
TCP和UDP区别
1、Tcp提供客户与服务器之间的连接。TCP客户端先与某个给定服务器建立一个连接,再跨该连接于那个服务器交换数据,然后终止这个连接。
(连接其实就是一种协商机制,预先定义好了双方的一些状态变量,告诉对方诸如序列号和通告窗口大小等状态信息)
2、Tcp提供了可靠的传输机制,不需要像UDP那样需要通过应用层来实现可靠性,直接通过协议来实现。发送数据后等待对方确认,没有收到确认就继续重传与等待,数次重传失败后才会放弃。
3、Tcp提供了流量控制,tcp总是告诉对端在任何时刻它一次能够从对端接收多少字节的数据。
采用套接字的TCP连接的基本程序模型
就是通过socket的这个函数封装数据,实现server额client的通信
客户端 服务端
sokcket socket() //创建套接字描述符
bind() //将服务器地址和相应套接字描述符绑定
listen() //将主动套接字转换为监听套接字,该套接字可以接受来自客户端的请求
connect() <-------------> accept() //connect()客户端连接请求 , accpet()函数解析监听来的客户端信息,
write() ------------> read() //网络读写io
read() <-------------- write()
close() ------------> read()
close() //close关闭连接
函数接口
1、socket
#include<sys/types.h> #include<sys/socket.h> int socket(int domain, int type, int protocol); 若成功返回非负描述符,若出错返回-1
2、connect
#include<sys/socket.h> int connect(int sockfd, struct sockaddr *serv_addr, int addrlen); 若成功返回0,若出错返回-1
3、bind
#include<sys/socket.h> int bind(int sockfd, struct sockaddr *my_addr, int addrlen) 若成功返回0,若出错返回-1将服务器地址和套接字联系起来
4、listen
#include<sys/socket.h> int listen(int sockfd, int backlog); 若成功返回0,若出错返回-1告诉内核,这个是服务器创建的套接字,不是客户端的,将主动状态转换为被动状态。
5、accept
#include<sys/socket.h>int accept(int listenfd, struct sockaddr *addr, int *addrlen);
若成功返回0,若出错返回-1等待来自于客户端的连接请求到达监听描述符listenfd,然后在addr中填写客户端的套接字地址,并返回一个已连接描述符,这个描述符可以用来利用Unix I/O函数与客户端通信。
套接字地址结构体
<netinet/in.h> struct in_addr{ in_addr_t s_addr; //32bit ipv4 address } struct sockaddr_in{ uint8_t; sa_family_t sin_family; // AF_INET in_port_t sin_port; //端口 struct in_addr sin_addr; char sin_zero[8]; //未使用 };
通过调用gethostbyname和gethostbyaddr函数,从DNS数据库中检索任意的主机条目。
<pre name="code" class="cpp">#include <netdb.h>struct hostent{ char *h_name; // 主机域名 char **h_aliases; int h_addrtype; //主机地址 int h_length; char **h_addr_list;
具体代码实现:
实现一个简单的客户端服务器模型,并不涉及多线程、I/O复用此类
server.c 服务端
#include<stdio.h> #include<sys/socket.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<netinet/in.h> #include<netdb.h> #include<arpa/inet.h> #define MAX_LISTEN 1024 #define MAX_LINE 1024 int Socket(int domain, int type, int protocol){ int sockfd = socket(domain, type, protocol); if ( sockfd < 0 ){ perror("init socket: "); exit(0); } return sockfd; } void Bind(int sockfd, struct sockaddr *myaddr, int addrlen){ if ( bind(sockfd, myaddr, addrlen) < 0 ){ perror("bind: "); exit(0); } } void Listen(int sockfd, int backlog){ if ( listen(sockfd, backlog) < 0){ perror("listen:"); exit(0); } } int Accept(int listenfd, struct sockaddr *addr, int *addrlen){ int clientfd = accept(listenfd, addr, addrlen); if ( clientfd < 0){ perror("accept: "); exit(0); } return clientfd; } void Close(int clientfd){ if ( close(clientfd) < 0){ perror("close: "); exit(0); } } struct hostent* Gethostbyaddr(const char *addr, int len, int domain){ struct hostent* host = gethostbyaddr(addr, len, domain); if ( NULL == host ){ perror("host_by_addr: "); exit(0); } return host; } ssize_t Read(int fd, void* buf, size_t n){ ssize_t num= read(fd, buf, n); if ( n < 0){ perror("read: "); exit(0); } return num; } ssize_t Write(int fd, const void* buf, size_t n){ ssize_t num = read(fd, buf, n); if ( n < 0){ perror("write: "); exit(0); } return num; } void echo(listenfd){ ssize_t n; char write_buff[MAX_LINE]; char read_buff[MAX_LINE]; memset(write_buff, 0, MAX_LINE); memset(read_buff, 0, MAX_LINE); n = read(listenfd, read_buff, MAX_LINE); read_buff[n] = '\0'; strcpy(write_buff, "from server echo: "); strcpy(write_buff+strlen("from server echo: "), read_buff); n = write(listenfd, write_buff, MAX_LINE); } int main(int argc, char **argv){ int servfd, clientfd, port, clientlen; struct sockaddr_in servaddr; struct sockaddr_in cliaddr; struct hostent *host; char* hostaddr; if ( argc != 2){ fprintf(stderr,"usage:%s<port>\n", argv[0]); exit(0); } port = atoi(argv[1]); // get port servfd = Socket(AF_INET, SOCK_STREAM, 0); // init servaddr memset(&servaddr, 0, sizeof(servaddr)); memset(&cliaddr, 0, sizeof(cliaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons((unsigned short)port); clientlen = sizeof(cliaddr); Bind(servfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); Listen(servfd, MAX_LISTEN); while(1){ // init server memset(&cliaddr, 0, sizeof(cliaddr)); clientfd = Accept(servfd, (struct sockaddr*)&cliaddr, &clientlen); host = Gethostbyaddr((const char*)&cliaddr.sin_addr.s_addr, sizeof(cliaddr.sin_addr.s_addr), AF_INET); printf("server connect to host: %s %s\n",host->h_name, inet_ntoa(cliaddr.sin_addr)); echo(clientfd); Close(clientfd); } }
client.c 客户端
#include<stdio.h> #include<sys/socket.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> #include<netinet/in.h> #include<netdb.h> #define MAX_LINE 1024 int Socket(int domain, int type, int protocol){ int sockfd = socket(domain, type, protocol); if ( sockfd < 0 ){ perror("init socket"); exit(0); } return sockfd; } void Close(int clientfd){ if ( close(clientfd) < 0){ perror("close"); exit(0); } } struct hostent* Gethostbyaddr(const char *addr, int len, int domain){ struct hostent* host = gethostbyaddr(addr, len, domain); if ( NULL == host ){ perror("host_by_addr"); exit(0); } return host; } ssize_t Read(int fd, void* buf, size_t n){ if ( read(fd, buf, n) < 0){ perror("read"); exit(0); } } ssize_t Write(int fd, const void* buf, size_t n){ if ( write(fd, buf, n) < 0){ perror("write"); exit(0); } } void Connect(int sockfd, struct sockaddr* serv_addr, int addrlen){ if ( connect(sockfd, serv_addr, addrlen) < 0){ perror("connect"); exit(0); } } void message_handle(int clientfd){ size_t n; char send_buff[MAX_LINE]; char recv_buff[MAX_LINE]; memset(send_buff, 0, MAX_LINE); memset(recv_buff, 0, MAX_LINE); fgets(send_buff, MAX_LINE, stdin); send_buff[strlen(send_buff)-1] = '\0'; n = Write(clientfd, send_buff, strlen(send_buff)+1); n = Read(clientfd, recv_buff, MAX_LINE); printf("%s \n", recv_buff); } int main(int argc, char **argv){ int clientfd, port; struct sockaddr_in servaddr; if ( argc != 3){ fprintf(stderr,"usage:%s<addr> <port>\n", argv[0]); exit(0); } port = atoi(argv[2]); printf("port: %d\n", port); printf("addr: %s\n", argv[1]); clientfd = Socket(AF_INET, SOCK_STREAM, 0); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(port); inet_pton(AF_INET, argv[1], &servaddr.sin_addr); while(1){ Connect(clientfd, (struct sockaddr*)&servaddr, sizeof(servaddr)); message_handle(clientfd); Close(clientfd); } }
服务端按上图TCP模型建立服务器处理客户端连接,每当有客户端连接时即打印客户信息,然后接受到客户发来的消息后,将客户消息反射回客户端。
运行结果:
客户端:
客户端输入wwww,返回wwww,然后关闭连接,再connect的perror报错,exit(0)退出
服务端:
客户端中断后使用netstat -a 命令查看tcp连接状态,9000端口处于listen状态,该连接处于TIME_WAIT状态
版权声明:本文为博主原创文章,未经博主允许不得转载。
(C/C++)网络编程(2)——采用TCP的基本server的实现
原文地址:http://blog.csdn.net/sshhiixx/article/details/47377133