在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标示网络通讯中的一个进程,“IP地址+端口号”为socket(套接字)
常用API:
#include<sys/types.h>
#include<sys/socket.h>
int sochet(int domain,int type,int protocol);
socket()打开一个网络通信端口,如果成功,就返回新套接字的文件描述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket调用出错则返回-1。对于IPv4,domain参数指明所使用的协议族,指定为AF_INET,对于TCP协议,type参数指定为SOCK_STREAM,便是面向流的传输协议,protocol参数用来指明所要接受的协议包,指定为0即可。
#include<sys/types.h>
#include<sys/socket.h>
int bind(int sockfd,const,struct sockaddr* addr,socklen_t addrlen);
服务器调用bind绑定一个固定的网络地址和端口号,bind()成功返回0,失败返回-1。
bind()作用是将参数sockfd和myaddr绑定到一起,使socket这个用于网络通讯的文件描述符监听addr所描述的地址和端口号。struct sockaddr*是一个通用指针类型。
#include<sys/types.h>
#include<sys/socket.h>
int listen(int sockfd,int backlog);
listen()函数使用主动连接套接口变为被连接套接口,使得一个进程可以接受其他进程的请求,从而成为一个服务器进程。
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr* addr,socklen_t *addrlen);
accept()接受一个套接字中已建立的连接
提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回该套接字的文件文件描述符,失败返回-1
#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen);
connect用于客户端建立TCP连接,sockfd标示一个套接字,addr为套接字想要连接的主机地址和端口号,addrlen为缓冲区的长度,连接成功返回0,失败返回-1
代码实现
//tcp_server.c
include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
static void usage(const char *proc)
{
printf("Usage:%s [local_ip] [local_port]\n",proc);
}
int startup(const char* _ip,int _port)
{
int sock=(socket(AF_INET,SOCK_STREAM,0));
if(sock<0){
perror("socket");
exit(2);
}
printf("fd:%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);
}
if((listen(sock,10))<0){
perror("listen");
exit(4);
}
return sock;
}
void * request(void *arg)
{
int n_sock=(int)arg;
while(1){
char buf[1024];
ssize_t s=read(n_sock,buf,sizeof(buf)-1);
if(s>0){
buf[s]=0;
printf("client:%s\n",buf);
write(n_sock,buf,strlen(buf));
}else if(s==0){
close(n_sock);
printf("client quit..\n");
break;
}
else{
printf("read false\n");
break;
}
}
}
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("get a new client:[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
//多线程
// pthread_t id;
// pthread_create(&id,NULL,request,(void*)new_sock);
// pthread_detach(id);
//多进程
// pid_t id=fork();
// if(id<0){
// close(new_sock);
// }
// else if(id==0){
// // close(listen_sock);
// if(fork()<0){
// exit(0);
// }
// 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){
// close(new_sock);
// printf("client quit...\n");
// break;
// }
// else{
// perror("read");
// }
// }
// }
// else{
// close(new_sock);
// }
// 普通版
char buffer[1024];
while(1)
{
ssize_t s=read(new_sock,buffer,sizeof(buffer)-1);
if(s>0){
buffer[s]=0;
printf("client# %s\n",buffer);
write(new_sock,buffer,strlen(buffer));
}else if(s==0){
printf("client quit!\n");
break;
}
else{
perror("read");
}
}
close(new_sock);
}
close(listen_sock);
return 0;
}
//tcp_client.c
#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<stdlib.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
#include<pthread.h>
static void usage(const char *proc)
{
printf("Usage:%s[local_ip] [local_port]\n",proc);
}
int main(int argc,char*argv[])
{
if(argc!=3){
usage(argv[0]);
return 1;
}
char buf[1024];
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0){
perror("socket");
exit(2);
}
struct sockaddr_in server;
server.sin_family=AF_INET;
server.sin_port=htons(atoi(argv[2]));
server.sin_addr.s_addr=inet_addr(argv[1]);
if(connect(sock,(struct sockaddr*)&server,sizeof(server))<0){
perror("connect");
exit(3);
}
while(1){
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));
int _s=read(sock,buf,sizeof(buf)-1);
if(_s>0){
buf[_s]=0;
printf("server echo# %s\n",buf);
}
}
}
return 0;
}
普通版本地连接运行结果:
普通版本无法实现多个客户端同时连接服务器,向服务器发送消息,只有当一个客户端断开时其他客户端才能进行连接输入消息
多线程版本地连接运行结果:
多进程版本本地连接运行结果:
PS:为了说明现象,图截到一块了,可以缩放一下网页进行查看