TCP_server
来源:互联网 发布:网络投资挣钱 编辑:程序博客网 时间:2024/06/14 13:44
编写代码前首先了解几个重要接口:
创建套接字:
创建套接字,其实是在创建一个文件描述符。
#include<sys/types.h>#include<sys/socket.h>int socket(int domain,int type,int protocol);
参数:
domain :AF_INET 使用IPV4协议;
type:SOCK_STREAM 使用TCP协议;
protocol:0 状态参数;
返回值: 成功返回一个文件描述符sockfd,失败返回-1;
绑定IP,port:
#include<sys/types.h>#include<sys/socket.h>int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
参数:
sockfd:sock 要被绑定的文件描述符;
addr:sockaddr数据结构的地址;
addrlen:sockadde数据结构的大小(字节);
返回值:成功返回0,失败返回-1;
参数中的‘struct sockaddr’ 结构体,是sock地址的泛型化接口,我们使用IPV4协议,使用的sockaddr接口应该是‘struct sockaddr_in’,因此在使用的时候需要进行地址的强制转化。
使用命令: grep -ER ‘struct sockaddr_in {’ /usr/include/
显示:/usr/include/linux/in.h:struct sockaddr_in {
键入命令: vim /usr/include/linux/in.h
可以查找结构体定义,如图:
因此在绑定IP,port之前首先要对’struct sockaddr_in’的对象进行赋值,也就是后面代码中的:
struct sockaddr_in local;local.sin_family = AF_INET;local.sin_addr.s_addr = inet_addr(ip);local.sin_port = htons(port);
监听函数:
#include<sys/types.h>#include<sys/socket.h>int listen(int sockfd,int backlog);
参数:
sockfd:sock 要监听的文件描述符;
backlog:10 sockfd等待连接队列可能增长的最大长度;
返回值:成功返回0,失败返回-1;
接受一个连接:
#include<sys/types.h>#include<sys/socket.h>int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
参数:
sockfd:listen_sock 监听到连接的文件描述符
addr:&client 记录客户端IP,port的一个sockaddr结构的指针
addrlen:&len 调用者对其进行初始化,即client方的sockaddr结构大小
返回值:成功返回一个服务型的文件描述符,失败返回-1
因为要获取client方的IP,port信息,因此在函数调用前要先构建一个结构体对象:
struct sockaddr_in client;socklen_t len = sizeof(client);
连接一台服务器:
#include<sys.types.h>#include<sys/socket.h>int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen);
参数:
sockfd:若该套接字是SOCK_STREAM,则尝试进行连接
addr:客户端sockaddr的地址
addrlen:客户端sockaddr的地址长度
返回值:成功返回0,失败返回-1
下面看看单进程版本服务器的代码,以及客户端代码:
//服务器server端:#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<netinet/in.h>#include<sys/types.h>#include<string.h> #include<arpa/inet.h>void usage(char* str){ printf("usage:%s [local_addr] [local_port]\n",str);}int startup(char* ip,int port){ int sock = socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port =htons( port); local.sin_addr.s_addr = inet_addr(ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(3); } if(listen(sock,10)<0) { perror("listen"); exit(4); } return sock;}int main(int argc,char* argv[]){ if(argc<3) { usage(argv[0]); exit(1); } int listen_sock = startup(argv[1],atoi(argv[2])); while(1) { struct sockaddr_in client; socklen_t len = sizeof(client); int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len); if(new_sock<0) { perror("accept"); continue; } printf("get a client: [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); char buf[1024]; while(1) { ssize_t s = read(new_sock,buf,sizeof(buf)-1); if(s>0) { buf[s] = 0; if(buf[0] != 0) { printf("client:# %s\n",buf); printf("Please Enter:"); fgets(buf,sizeof(buf),stdin); write(new_sock,buf,strlen(buf)+1); printf("Please Wait...\n"); } } else if(s == 0) { printf("client send over\n"); close(new_sock); break; } else { break; } } } close(listen_sock); return 0;}
代码的大致情况是,首先创建一个套接字listen_sock,用来监听链接的,而这个套接字创建出来后先要绑定IP地址还有端口号,因为是一个服务器,所以要有固定的IP和port,然后监听链接,当有链接请求时,listen_sock被accept函数调用获得,client方的IP和port,从而成功建立链接,返回一个服务型的新的套接字new_sock,通过new_sock进行服务器与客户端的通信。
//客户client端:#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<netinet/in.h>#include<sys/types.h>#include<string.h>#include<arpa/inet.h>void usage(char* str){ printf("usage:%s [local_addr] [local_port]\n",str);}int startup(char* ip,int port){ int sock = socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); return 2; } struct sockaddr_in server; server.sin_family = AF_INET; server.sin_port = htons(port); server.sin_addr.s_addr = inet_addr(ip); if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0) { perror("connect"); return 3; } return sock;}int main(int argc,char* argv[]){ if(argc<3) { usage(argv[0]); exit(1); } int sock = startup(argv[1],atoi(argv[2])); printf("Connect to a server\n"); while(1) { printf("Plesae Enter:"); fflush(stdout); char buf[1024]; fgets(buf,sizeof(buf),stdin); buf[sizeof(buf)-1] = 0; write(sock,buf,sizeof(buf)); printf("Please wait ...\n"); read(sock,buf,sizeof(buf)); printf("server send:$ %s\n",buf); } close(sock); return 0;}
与服务器端大致相同,因为client是发起连接请求的一端,所以client不需要绑定IP和port,也不需要进行监听,而获得一个链接,也就变成发起连接请求,建立连接成功后,就可以进行通讯了。
makefile:
.PHONY:allall:server clientserver:server.c gcc -o $@ $^client:client.c gcc -o $@ $^.PHONY:cleanclean: rm -f server client
多进程版本的服务器代码:
#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<netinet/in.h>#include<sys/types.h>#include<string.h> #include<arpa/inet.h>void usage(char* str){ printf("usage:%s [local_addr] [local_port]\n",str);}int startup(char* ip,int port){ int sock = socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port =htons( port); local.sin_addr.s_addr = inet_addr(ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(3); } if(listen(sock,10)<0) { perror("listen"); exit(4); } return sock;}int main(int argc,char* argv[]){ if(argc<3) { usage(argv[0]); exit(1); } int listen_sock = startup(argv[1],atoi(argv[2])); while(1) { struct sockaddr_in client; socklen_t len = sizeof(client); int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len); if(new_sock<0) { perror("accept"); continue; } printf("get a client: [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); pid_t id=fork(); if(id<0) { perror("fork"); close(new_sock); continue; } else if(id == 0) { close(listen_sock); if(fork()>0) { exit(0); } else { char buf[1024]; while(1) { ssize_t s = read(new_sock,buf,sizeof(buf)-1); if(s>0) { buf[s] = 0; if(buf[0] != 0) { printf("client[%s,%d]:# %s\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port),buf); write(new_sock,buf,strlen(buf)+1); } } else if(s == 0) { printf("client[%s:%d]send over\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port)); close(new_sock); break; } else { close(new_sock); break; } } exit(5); } } else { close(new_sock); waitpid(id,NULL,0); } } close(listen_sock); return 0;}
在单进程版本的基础上,在成功获取到一个连接后,使用fork函数创建子进程,让子进程去进行与客户端的通讯,因为子进程不需要监听,所以关闭listen_sock,父进程重新去获得一个新的连接,不需要进行与客户端的通信,因此关闭new_sock,但是由于子进程尚未退出,父进程需要进行等待,这样的话,事实上还是一个单进程的服务器,因此在子进程内部在fork一个子进程,让子进程的子进程去与客户端进行通讯,自己直接退出,这样父进程收到了子进程退出的信号,父进程就回去等待新的连接建立,而子进程的子进程,由于他的父进程直接退出,导致他变成孤儿进程,当他与客户端完成通信。他就会被操作系统回收。
多线程版本server服务器代码:
#include<stdio.h>#include<stdlib.h>#include<sys/socket.h>#include<netinet/in.h>#include<sys/types.h>#include<string.h> #include<arpa/inet.h>#include<pthread.h>void usage(char* str){ printf("usage:%s [local_addr] [local_port]\n",str);}int startup(char* ip,int port){ int sock = socket(AF_INET,SOCK_STREAM,0); if(sock<0) { perror("socket"); exit(2); } struct sockaddr_in local; local.sin_family = AF_INET; local.sin_port =htons( port); local.sin_addr.s_addr = inet_addr(ip); if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0) { perror("bind"); exit(3); } if(listen(sock,10)<0) { perror("listen"); exit(4); } return sock;}void* handler_request(void* arg){ int new_sock = *(int*)arg; char buf[1024]; while(1) { ssize_t s = read(new_sock,buf,sizeof(buf)-1); if(s>0) { buf[s] = 0; if(buf[0] != 0) { printf("client:# %s\n",buf); write(new_sock,buf,strlen(buf)+1); } } else if(s == 0) { printf("client send over\n"); close(new_sock); break; } else { close(new_sock); break; } } close(new_sock); return (void*)0;}int main(int argc,char* argv[]){ if(argc<3) { usage(argv[0]); exit(1); } int listen_sock = startup(argv[1],atoi(argv[2])); while(1) { struct sockaddr_in client; socklen_t len = sizeof(client); int new_sock = accept(listen_sock,(struct sockaddr*)&client,&len); if(new_sock<0) { perror("accept"); continue; } printf("get a client: [%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));// 多线程版本 pthread_t fid; pthread_create(&fid,NULL,(void*)handler_request,&new_sock); pthread_detach(fid); } close(listen_sock); return 0;}
多线程版本服务器,是利用现成的可分离性来实现,多人连接服务器的;
//makefileserver:server.c gcc -o $@ $^ -lptnread.PHONY:cleanclean: rm -f server
因为server.c加入了线程的库,使用了线程接口,因此在编译时要链接线程静态的库;
多线程版本中出现了两个新的函数接口:
//创建新线程:#include<pthread.h>int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine) (void *),void *arg);
参数:
thread:新创建启动的线程
start_routine:新建线程的启动例程;
arg:给启动历程传参;
返回值:成功返回0;失败返回错误码
//线程分离:#include<pthread.h>int pthread_detach(pthread_t thread);
参数:
thread:要分离的线程
返回值:成功返回0,失败返回错误码
- TCP_server
- TCP_server
- TCP_server
- TCP_Server
- tcp_server
- tcp_server
- TCP_server
- tcp_server
- 【网络】tcp_server
- 编写tcp_server
- tcp_server.c服务模型
- tcp_server的实现
- tcp_server和tcp_client
- tcp_server的实现
- socket网络编程之TCP_Server
- Linux套接字编程tcp_server
- 【Linux】中TCP_server的实现
- LINUX--TCP_server端的编写
- 服务器端应用安全
- linux命令——svn分支创建、合并
- ELk日志系统搭建
- c++设计模式之简单工厂模式、工厂方法模式、抽象工厂模式
- 信号量与互斥锁之间的区别
- TCP_server
- Android使用map存类名然后直接使用,很方便
- HDU 2795 Billboard 线段树
- SDUT 3661 山峰
- CSS top、margin-top和padding-top的区别
- Linux 升级 MySQL 5.7
- QThread类参考
- 在eclipse中启动tomcat,无法访问localhost:8080
- <转载>如何用git将项目代码上传到github