tcp_server(简单版本,多进程版本,多线程版本)

简单版本

(1) 创建套接字socket
int socket(int domain, int type, int protocol);
参数  :domain指定通信域,这里使用AF_INET,
            type指定传输数据的类型,这里使用流式数据类型SOCK_STREAM,
    protocol指定一个与套接字一起使用的协议,也可以给0。
返回值:该函数成功则返回文件描述符,失败返回-1。

(2)  通常,在SOCK_STREAM套接字可能接收连接之前通常需要使用bind()分配本地地址
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数  :sockfd,文件描述符sockfd引用的套接字socket,
            const struct sockaddr *addr,该结构体中保存着ip地址与端口号分配给sockfd
    socklen_t addrlen,结构体大小
返回值:该函数成功则返回0,失败返回-1。

(3) 监听
 int listen(int sockfd, int backlog);
参数  :sockfd,文件描述符sockfd引用的套接字socket,
             backlog参数定义sockfd的挂起连接队列可能增长的最大长度。这里给10。
返回值:该函数成功则返回0,失败返回-1

(4) 接收链接,当客户端向服务器发送请求时服务器用accept()返回并接收这个链接。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数  :sockfd,文件描述符sockfd引用的套接字socket,
            struct sockaddr *addr 该结构体中保存着client端的ip地址与端口号。
  socklen_t *addrlen,必须初始化,保存着结构体大小。
返回值:该函数成功则返回0,失败返回-1

这些全部搞定之后就可以开始进行数据传送了,因为是流式数据类型,所以可以用read和write进行数据读写。
下面是简单的tcp服务器代码以及客户端代码。
server.c
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
int startup(const char* ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock< 0){
perror("socket");
exit(2);
}
printf("socket succes! sock= %d;\n",sock);
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = inet_addr(ip);
if(bind(sock,(struct sockaddr*)&server,sizeof(server))< 0){
perror("bind");
exit(3);
}
printf("bind succes\n");
if(listen(sock,10)< 0)
{
perror("listen");
exit(4);
}
return sock;
}
void usage(char* s)
{
printf("usage: %s [loacl_ip] [loacl_port]\n",s);
}
int main(int argc,char* argv[])
{
if(argc!= 3){
usage(argv[0]);
return 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("accept succes!\n");
printf("get connect ip:%s,port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));

while(1)
{
char buf[1024];
ssize_t s = read(new_sock,buf,sizeof(buf)-1);
if(s > 0)
{
buf[s]= 0;
printf("client# %s\n",buf);
write(new_sock,buf,strlen(buf));
}else if(s==0){
printf("clinet is quit!\n");
break;
}else{
perror("read");
break;
}
}
close(new_sock);
}
return 0;
}


client.c
客户端需要使用connect()链接服务器。
 int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数  :sockfd,文件描述符sockfd引用的套接字socket,
            struct sockaddr *addr 该结构体中保存着需要连接的服务器端的ip地址与端口号。
  socklen_t addrlen,结构体大小。
返回值:该函数成功则返回0,失败返回-1
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main(int argc,char* argv[])
{
if(argc!= 3){
printf("usage:%s,ip port \n",argv[0]);
return 1;
}
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock< 0){
perror("sock");
exit(2);
}
struct sockaddr_in client;
client.sin_family = AF_INET;
client.sin_addr.s_addr = inet_addr(argv[1]);
client.sin_port = htons(atoi(argv[2]));
int ret = connect(sock,(struct sockaddr*)&client,sizeof(client));
if(ret< 0){
perror("connect");
exit(3);
}
printf("connect succes!\n");
while(1)
{
char buf[1024];
printf("PLease Enter# ");
fflush(stdout);
ssize_t s = read(0, buf ,sizeof(buf)-1);
if(s > 0)
{
buf[s-1]= 0;
write(sock,buf,strlen(buf));
ssize_t k=read(sock,buf,sizeof(buf)-1);
if(k > 0){
buf[k = 0];
printf("server echo# %s\n",buf);}
}
}
close(sock);
return 0;
}

多进程版本

多进程版本整体与简单版本一致,只不过将读写任务交给孙子进程去做,爷爷进程监听,
父进程创建子进程后自己退出,爷爷进程回收父进程然后监听。
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
int startup(const char* ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock< 0){
perror("socket");
exit(2);
}
printf("socket succes! sock= %d;\n",sock);
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);
}


printf("bind succes!\n");
if(listen(sock,10)< 0)
{
perror("listen");
exit(4);
}
return sock;
}
void usage(char* s)
{
printf("usage: %s [loacl_ip] [loacl_port]\n",s);
}

int main(int argc,char* argv[])
{
if(argc!= 3){
usage(argv[0]);
return 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("accept succes!\n");
printf("get connect ip:%s,port:%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){//child
close(listen_sock);
if(fork() > 0){
exit(0);
}
while(1){
char buf[1024];
ssize_t s = read(new_sock,buf,sizeof(buf)-1);//有bug
if(s > 0){
buf[s-1]= 0;
printf("client# %s\n",buf);
write(new_sock,buf,strlen(buf));
}else if(s==0){
printf("clinet is quit!\n");
break;
}else{
perror("read");
break;
}
}
close(new_sock);
exit(1);
}else{//father
close(new_sock);
waitpid(id,NULL,0);
}
}
return 0;
}

多线程版本

创建一个线程去读写,然后将线程设置成分离状态
server.c
#include<stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
int startup(const char* ip,int port)
{
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock< 0){
perror("socket");
exit(2);
}
printf("socket succes! sock= %d;\n",sock);
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);
}


printf("bind succes!\n");
if(listen(sock,10)< 0)
{
perror("listen");
exit(4);
}
return sock;
}
void usage(char* s)
{
printf("usage: %s [loacl_ip] [loacl_port]\n",s);
}
void *request(void* arg)
{
int new_sock = (int)arg;
while(1){
char buf[1024];
ssize_t s = read(new_sock,buf,sizeof(buf)-1);//有bug
if(s > 0)
{
buf[s]= 0;
printf("client# %s\n",buf);
write(new_sock,buf,strlen(buf));
}else if(s==0){
printf("clinet is quit!\n");
break;
}else{
perror("read");
break;
}
}
close(new_sock);
return (void*)0;
}
int main(int argc,char* argv[])
{
if(argc!= 3){
usage(argv[0]);
return 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("accept succes!\n");
printf("get connect ip:%s,port:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//version 3
pthread_t id;
pthread_create(&id, NULL, request, (void*)new_sock);
pthread_detach(id);

}
return 0;
}

多进程与多线程服务器,使用telnet进行测试。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值