本文介绍Tcp的简单应用:简单的 回传(即客户端发送什么,服务器就转发给客户端什么)。
主要包含以下几个函数原型:
服务器端:
//服务器端主要函数原型:int socket(int domain, int type, int protocol); int bind( int sockfd , const struct sockaddr * my_addr, socklen_t addrlen); int listen( int fd, int backlog); SOCKET PASCAL accept( SOCKET s, struct sockaddr * addr,int * addrlen);
客户端:
int socket(int domain, int type, int protocol); int PASCAL FAR connect( SOCKET s, const struct sockaddr FAR* name, int namelen);
服务器端和客户端:以上函数错误的情况下都是 -1;
服务器端:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> //错误处理,推荐做法 #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) void do_service(int sockfd); int main(int argc, const char *argv[]) { //socket 返回一个监听的文件描述符 int listenfd = socket(PF_INET, SOCK_STREAM, 0); if(listenfd == -1) ERR_EXIT("socket"); //地址复用--->记住即可 int on = 1; if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) ERR_EXIT("setsockopt"); //bind 实现将服务器的地址IP,端口号PORT绑定 struct sockaddr_in addr; memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); addr.sin_port = htons(8976); if(bind(listenfd, (struct sockaddr*)&addr, sizeof addr) == -1) ERR_EXIT("bind"); //listen 监听集合(监听客户端是否发送给服务器消息) if(listen(listenfd, SOMAXCONN) == -1) ERR_EXIT("listen"); //accept 接受客户端请求,并返回另外一个文件描述符单独处理客户的请求 int peerfd = accept(listenfd, NULL, NULL); //read&write do_service(peerfd); //close close(peerfd); close(listenfd); return 0; } //read&write void do_service(int sockfd) { char recvbuf[1024] = {0}; while(1) { //read 读取客户端发送来的数据 int nread = read(sockfd, recvbuf, sizeof recvbuf); if(nread == -1)//出错情况以及处理 { if(errno == EINTR) continue; ERR_EXIT("read"); } else if(nread == 0)//客户端已发送完毕,即客户端的写端关闭 { printf("close ...\n"); exit(EXIT_SUCCESS); } //回传给客户端 write(sockfd, recvbuf, strlen(recvbuf)); //清空缓冲区,以免发送覆盖不完全情况 memset(recvbuf, 0, sizeof recvbuf); } }
客户端:
//client.c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #define ERR_EXIT(m) do { perror(m); exit(EXIT_FAILURE); }while(0) void do_service(int sockfd); int main(int argc, const char *argv[]) { //socket int peerfd = socket(PF_INET, SOCK_STREAM, 0); if(peerfd == -1) ERR_EXIT("socket"); //connect请求与服务器连接 struct sockaddr_in addr; memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //localhost addr.sin_port = htons(8976); socklen_t len = sizeof addr; if(connect(peerfd, (struct sockaddr*)&addr, len) == -1) ERR_EXIT("Connect"); //send&recv do_service(peerfd); return 0; } //send&recv void do_service(int sockfd) { char recvbuf[1024] = {0}; //接收缓冲区 char sendbuf[1024] = {0};//发送缓冲区 while(1) { fgets(sendbuf, sizeof sendbuf, stdin);//从键盘中输入进发送缓冲区数据 write(sockfd, sendbuf, strlen(sendbuf)); //向服务器发送数据 //read int nread = read(sockfd, recvbuf, sizeof recvbuf); //读取服务器发来的数据 if(nread == -1)//err { if(errno == EINTR) continue; ERR_EXIT("read"); } else if(nread == 0)//EOF { printf("server close!\n"); close(sockfd); exit(EXIT_SUCCESS); } printf("receive msg : %s", recvbuf); //打印所接收的数据 memset(recvbuf, 0, sizeof recvbuf); memset(sendbuf, 0, sizeof sendbuf); } }
这样我们就简单实现了tcp通信。
注意:本程序不能解决 字节流的 粘包问题。
如下程序:服务器与客户端发生改变的代码仅仅是 do_serve 程序;
服务器端多改动的部分如下:
void do_service(int sockfd) { int cnt = 0; char recvbuf[1024000] = {0}; //从缓冲区一次读取的数据远远大于客户端一次发送的数据(1024) while(1) { int nread = read(sockfd, recvbuf, sizeof recvbuf); //从缓冲区读数据 if(nread == -1) { if(errno == EINTR) continue; ERR_EXIT("read"); } else if(nread == 0) { printf("close ...\n"); exit(EXIT_SUCCESS); } printf("count = %d, receive size = %d\n", ++cnt, nread); memset(recvbuf, 0, sizeof recvbuf); } }
客户端所改动的数据如下:
void do_service(int sockfd) { #define SIZE 1024 char sendbuf[SIZE + 1] = {0}; int i; for(i = 0; i < SIZE; ++i) //缓冲区中的每个字符的值 sendbuf[i] = ‘a‘; int cnt = 0; //次数 while(1) { int i; for(i = 0; i < 10; ++i) //每次发送SIZE个字符,总共发送十次 { write(sockfd, sendbuf, SIZE); printf("count = %d, write %d bytes\n", ++cnt, SIZE); } nano_sleep(4); //暂停客户端4s钟 memset(sendbuf, 0, sizeof sendbuf); } } void nano_sleep(double val) { struct timespec tv; tv.tv_sec = val; //取整 tv.tv_nsec = (val - tv.tv_sec) * 1000 * 1000 * 1000; int ret; do { ret = nanosleep(&tv, &tv); }while(ret == -1 && errno == EINTR); }
时间: 2024-10-20 01:51:57