1.预备知识介绍
以下函数发生错误均可查看errno错误原因
int socket(int domain, int type,int protocol)
举例:socket(AF_INET,SOCK_STREAM,0);
头文件:
#include<sys/systypes.h>
#include<sys/socket.h>
第一个参数是指协议族类型,第二个参数表示套接字通信的类型,第三个表示一个协议的不同种类选择,只有一种就是0.
返回值失败返回-1,成功返回这个socket对应的文件描述符。
int bind(int sockfd,const struct sockaddr *my_addr,socklen_t addrlen)
第一个参数是对应socket的文件描述符
第二个参数是指向sockaddr结构的指针,这个结构包含地址,端口和ip信息
第三个参数是长度,一般用sizeof(struct sockaddr)即可
int listen(int sockfd,int backlog)
第一个参数是socket对应文件描述符,第二个参数是accept函数处理之前等待队列中客户端的个数,超过这个个数客户端就会返回一个ECONNREFUSED错误
当listen成功运行时,返回值是0,运行失败返回-1;有些协议可能不支持listen,如SOCK_DGRAM
接受一个网络请求accept
int accept(int sockfd,struct sockaddr *addr, socklen_t *addrlen);
该函数返回值是客户端socket的文件描述符
通过该函数可以获得客户端的IP地址,端口和协议等等,存在addr指针中。
int connect(int sockfd,struct sockaddr* ,int addrlen)
该函数成功返回0,发生错误返回-1.
第一个参数为建立客户端套接字返回的文件描述符
第二个参数为指针,包括客户端需要连接服务器的目的端口和IP地址,以及协议类型。第三个参数设为sizeof(struct sockaddr)即可
2.测试程序
服务器端(tcpserver.c)
#include<stdio.h>
#include<stdlib.h>
#include<strings.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#define port 8888
#define queuelong 2 //监听队列长度
void process_conn_server(int s) //处理客户端传过来的消息,参数为客户端socket的描述符
{
ssize_t size=0;
char buffer[1024];
for(;;)
{
size=read(s,buffer,1024); //从客户端socket读数据
if(size==0)return ;
//构建响应字符,为接收到客户端字节数量
sprintf(buffer,"%d bytes altogether\n",size); //写在buffer里
write(s,buffer,strlen(buffer)+1); //发送给客户端
}
}
int main(int argc,int *argv[])
{
int ss,sc; //ss为服务器socket描述符,sc为客户端socket描述符
struct sockaddr_in server_addr; //服务器地址结构
struct sockaddr_in client_addr; //客户端地址结构
int err; //返回值
pid_t pid; //分支进程号
//建立套接字
ss=socket(AF_INET,SOCK_STREAM,0);
if(ss<0)
{
printf("socket error!!!");
return -1;
}
//设置服务器地址
bzero(&server_addr,sizeof(server_addr)); //清零
server_addr.sin_family=AF_INET; //协议类型
server_addr.sin_addr.s_addr=htonl(INADDR_ANY); //
server_addr.sin_port=htons(port); //端口
//将设置好的网络地址结构与套接字绑定
err=bind(ss,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(err<0)
{
printf("listen error!");
return -1;
}
err=listen(ss,queuelong); //监听
if(err<0)
{
printf("listen failed!");
return -1;
}
printf("bind is over!");
//主循环
for(;;)
{
socklen_t addrlen=sizeof(struct sockaddr);
sc=accept(ss,(struct sockaddr*)&client_addr,&addrlen); //接受客户端请求
if(sc<0)
continue;
pid=fork(); //为每一个客户请求复制一个子进程
printf("sub process is create!");
if(pid==0)
{
close(ss);
process_conn_server(sc); //子进程处理
}
else
close(sc);
}
客户端(tcpclient.c)
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#define PORT 8888
void process_conn_client(int s)
{
ssize_t size=0;
char buffer[1024];
for(;;)
{
size=read(0,buffer,1024); //从标准输入读入数据
if(size>0)
{
write(s,buffer,size); //给服务器写数据
size=read(s,buffer,1024); //读数据
write(1,buffer,size);//向屏幕写
}
}
}
int main(int argc,char *argv[])
{
int s; //socket描述符
struct sockaddr_in server_addr;
s=socket(AF_INET,SOCK_STREAM,0); //建立客户端socket
if(s<0)
{
printf("socket error!!");
return -1;
}
//设置连接的服务器地址
bzero(&server_addr,sizeof(server_addr)); //清零
server_addr.sin_family=AF_INET; //协议类型
server_addr.sin_addr.s_addr=htonl(INADDR_ANY); //ip--这里是任意地网卡地址
server_addr.sin_port=htons(PORT); //端口
//将用户输入的字符串类型的ip地址转换成整形
inet_pton(AF_INET,argv[1],&server_addr.sin_addr);
printf("begin to connect!");
//连接服务器
connect(s,(struct sockaddr*)&server_addr,sizeof(struct sockaddr));
process_conn_client(s);
printf("process_conn_client is over!");
close(s);
return 0;
}
3.测试及结果
分别编译两个.c文件,得到两个可执行文件
先开一个终端,执行tcpserver
再开一个执行:
以上就是效果!
最后总结一下linux下tcp的步骤