socket linux c tcp

Socket编程

连接:

服务端调用socket(),bind(),listen()完成初始化以后,调用accept()阻塞等待。处于监听端口状态。客户端调用socket函数初始化后调用connect()发出SYN并阻塞等待服务器应答,服务器回答一个SYN-ACK段,客户端收到后从connect()返回。同时应答一个ACK。服务器收到后从accept()返回。


例子

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<sys/socket.h>

#include<netinet/in.h>


IPv4IPv6的地址格式定义在netinet/in.h,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32IP地址,IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128IP地址和一些控制字段。


structsocketaddr_in

{

unsignedshort int sin_family;

uint16_tsin_port;

structin_addr sin_addr;

unsignedchar sin_zero[8];

};

sin_family:指定通信的地址类型。如果是TCP/IP通信,则该值为AF_INET

sin_port:套接字使用的端口号。

sin_addr:需要访问的IP地址。in_addr也是一个结构体,定义方法如下所示。 作用是保存一个IP地址。

structin_addr

{

uint32_ts_addr;

};

sin_zero:未使用的字段,填充为0

例如下面程序中

servaddr.sin_family= AF_INET;

servaddr.sin_addr.s_addr= htonl(INADDR_ANY);

servaddr.sin_port=htons(SERV_PORT);



#defineMAXLINE 80

#defineSERV_PORT 8000


intmain()

{

structsockaddr_in servaddr,cliaddr;

socklen_tcliaddr_len;

intlistenfd,connfd;

charbuf[MAXLINE];

charstr[INET_ADDRSTRLEN];

inti,n;

listenfd= socket(AF_INET,SOCK_STREAM,0);


intsocket(int family, int type, int protocol);

socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描述符,应用程

序可以像读写文件一样用read/write在网络上收发数据,如果socket()调用出错则返回-1。对于IPv4,family参数指定为AF_INET。对于TCP协议,type参数指定为SOCK_STREAM,表示

面向流的传输协议。如果是UDP协议,type参数指定为SOCK_DGRAM,表示面向数据报的

传输协议。protocol参数指定为0即可。



bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family= AF_INET;

servaddr.sin_addr.s_addr= htonl(INADDR_ANY);

servaddr.sin_port=htons(SERV_PORT);


首先将整个结构体清零,然后设置地址类型为AF_INET,网络地址为INADDR_ANY,这个宏表

示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,这样设

置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪个IP

,端口号为SERV_PORT,我们定义为8000


htonl,htons, ntohl, ntohs - convert values between host and network byteorder



bind(listenfd,(structsockaddr *)&servaddr,sizeof(servaddr));


intbind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);

sockaddr_in的功能与 soockaddr相同,也是用来保存一个套接字的信息。不同的是将

IP地址与端口分开为不同的成员

bind()的作用是将参数sockfdmyaddr绑定在一起,使sockfd这个用于网络通讯的文件描述符监听myaddr所描述的地址和端口号。


listen(listenfd,20);

intlisten(int sockfd, int backlog);

listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略。listen()成功返回0,失败返回-1


printf("acceptingconnect.....\n");


while(1) {

cliaddr_len= sizeof(cliaddr);

connfd= accept (listenfd,(struct sockaddr*)&cliaddr,&cliaddr_len);


intaccept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);

三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有客户端的连

接请求,就阻塞等待直到有客户端连接上来。cliaddr是一个传出参数,accept()返回时传出客户

端的地址和端口号。addrlen参数是一个传入传出参数(value-resultargument),传入的是调

用者提供的缓冲区cliaddr的长度以避免缓冲区溢出问题,传出的是客户端地址结构体的实际长

(有可能没有占满调用者提供的缓冲区)。如果给cliaddr参数传NULL,表示不关心客户端的

地址。



n= read(connfd,buf,MAXLINE);

printf("receviedfrom %s at port\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));



inet_ntop- convert IPv4 and IPv6 addresses from binary to text form

constchar *inet_ntop(int af, const void *src, char *dst, socklen_t size);


for( i = 0; i< n ;i++) {

buf[i]= toupper(buf[i]);

}

write(connfd,buf, n);

close(connfd);

}


}





#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<sys/socket.h>

#include<netinet/in.h>

#defineMAXLINE 80

#defineSERV_PORT 8000



intmain(int argc ,char * argv[])

{


structsockaddr_in servaddr;

charbuf[MAXLINE];

intsockfd,n;

char*str;

if(argc != 2) {

fputs("usage:./client message\n", stderr);

exit(1);

}

str= argv[1];


sockfd= socket(AF_INET,SOCK_STREAM,0);


servaddr.sin_family= AF_INET;

inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);

servaddr.sin_port= htons(SERV_PORT);


connect(sockfd,(structsockaddr *)&servaddr,sizeof(servaddr));


intconnect(int sockfd, const struct sockaddr *servaddr, socklen_t

addrlen);



由于客户端不需要固定的端口号,因此不必调用bind(),客户端的端口号由内核自动分配。

客户端需要调用connect()连接服务器,connectbind的参数形式一致,区别在于bind的参数是

自己的地址,connect的参数是对方的地址。connect()成功返回0,出错返回-1



write(sockfd,str,strlen(str));


n= read(sockfd,buf,MAXLINE);

printf("Responsefrom server:\n");

// write(STDOUT_FILENO,buf, n);

if(n!=write(STDOUT_FILENO,buf,n))//buf写到标准输出中

perror("writeerror");

close(sockfd);

return0;


}






参考linux一站式学习


socket_wrap.h


externvoid perr_exit(const char *s);


externint Accept(int fd,struct sockaddr *sa,socklen_t *salenptr);


externvoid Bind( int fd,const struct sockaddr * sa,socklen_t salen);



externvoid Connect(int fd,const struct sockaddr *sa,socklen_t salen);


externvoid Listen (int fd,int backflag);



externint Socket (int family,int type,int protocol);


externsize_t Read (int fd,void * ptr,size_t nbytes);



externsize_t Write (int fd,const *ptr,size_t nbytes);


externvoid Close (int fd);

/*

TCP协议是面向流的,readwrite调用的返回值往往小于参数指定的字节数。对于read调用,

如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用,如果请求

100个字节,而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节

全部交给发送缓冲区才返回,但如果socket文件描述符有O_NONBLOCK标志,write不阻

,直接返回20

确保读写我们所请求的字节数


*/


externsize_t Readn(int fd, char *vptr,size_t n);


externssize_t Writen(int fd, const void *vptr, size_t n);



socket_wrap.c



#include<stdlib.h>

#include<errno.h>

#include<sys/socket.h>


voidperr_exit(const char *s)

{


perror(s);

exit(1);


}


intAccept(int fd,struct sockaddr *sa,socklen_t *salenptr)

{


intn;

intflag=1;

while(flag) {


if( (n = accept(fd,sa,salenptr))<0) {

if((errno == ECONNABORTED) || (errno == EINTR))

continue;

else

{

flag= 0;

perr_exit("accepterror");

}


}else {

flag= 0;

}


}

returnn;

}



voidBind( int fd,const struct sockaddr * sa,socklen_t salen)

{

if( bind(fd,sa,salen) <0)

perr_exit("binderror");

}


voidConnect(int fd,const struct sockaddr *sa,socklen_t salen)

{

if(connect(fd,sa,salen)<0)

perr_exit("connecterror");


}


voidListen (int fd,int backflag)

{

if(listen(fd,backflag)<0)

perr_exit("listenerror");


}


intSocket (int family,int type,int protocol)

{

int n;

if( (n = socket(family,type,protocol))<0)

perr_exit("socketerror");

returnn;

}


size_tRead (int fd,void * ptr,size_t nbytes)

{

size_tn;


intflag=1;


while(flag) {


if( (n = read(fd,ptr,nbytes))==-1) {

if( errno == EINTR )

continue;

else

flag=0;

}

else

flag= 0;

}

returnn;


}



size_tWrite (int fd,const *ptr,size_t nbytes)

{

size_tn;


intflag=1;


while(flag) {

if((n = write(fd,ptr,nbytes))==-1) {

if(errno == EINTR)

continue;

else

flag=0;

}

else

flag=0;

}

returnn;


}


voidClose (int fd)

{

if(close(fd)==-1)

perr_exit("closeerror");


}


/*

TCP协议是面向流的,readwrite调用的返回值往往小于参数指定的字节数。对于read调用,

如果接收缓冲区中有20字节,请求读100个字节,就会返回20。对于write调用,如果请求

100个字节,而发送缓冲区中只有20个字节的空闲位置,那么write会阻塞,直到把100个字节

全部交给发送缓冲区才返回,但如果socket文件描述符有O_NONBLOCK标志,write不阻

,直接返回20

确保读写我们所请求的字节数


*/


size_tReadn(int fd, char *vptr,size_t n)

{

size_tnleft;

size_tnread;

char*ptr;

ptr= vptr;


nleft= n;


while(nleft >0) {

if ( (nread = read(fd,ptr,nleft))<0 ) {

if(errno ==EINTR)

nread = 0;

else

return -1;

}else if (nread == 0)

break;

nleft -= nread;

ptr += nread;


}

returnn - nleft;


}



size_tWriten(int fd, const void *vptr, size_t n)

{

size_tnleft;

size_tnwritten;

constchar *ptr;

ptr= vptr;

nleft= n;


while(nleft > 0) {

if( (nwritten = write(fd, ptr, nleft)) <= 0) {

if(nwritten < 0 && errno == EINTR)

nwritten= 0;

elsereturn -1;

}

nleft-= nwritten;

ptr+= nwritten;

}

returnn;

}




server.c



#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include"socket_wrap.h"


#defineMAXLINE 80

#defineSERV_PORT 8010


intmain()

{

structsockaddr_in servaddr,cliaddr;

socklen_tcliaddr_len;

intlistenfd,connfd;

charbuf[MAXLINE];

charstr[INET_ADDRSTRLEN];

inti,n;


listenfd= Socket(AF_INET,SOCK_STREAM,0);

bzero(&servaddr,sizeof(servaddr));

servaddr.sin_family= AF_INET;

servaddr.sin_addr.s_addr= htonl(INADDR_ANY);

servaddr.sin_port=htons(SERV_PORT);


Bind(listenfd,(structsockaddr *)&servaddr,sizeof(servaddr));


Listen(listenfd,20);


printf("acceptingconnect.....\n");


while(1) {

cliaddr_len= sizeof(cliaddr);

connfd= Accept (listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);

while(1) {

n= Read(connfd,buf,MAXLINE);

if( n ==0) {

printf("theother side has beenclosed.\n"); break;

}

printf("receviedfrom %s at port %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));

for( i = 0; i< n ;i++) {

buf[i]= toupper(buf[i]);

}

Write(connfd,buf, n);

puts(buf);

}

close(connfd);

}


}



client.c


#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<unistd.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include"socket_wrap.h"


#defineMAXLINE 80

#defineSERV_PORT 8010



intmain(int argc ,char * argv[])

{


structsockaddr_in servaddr;

charbuf[MAXLINE];

intsockfd,n;

char*str;

if(argc != 2) {

fputs("usage:./client message\n", stderr);

exit(1);

}

str= argv[1];


sockfd= Socket(AF_INET,SOCK_STREAM,0);


servaddr.sin_family= AF_INET;

inet_pton(AF_INET,"127.0.0.1",&servaddr.sin_addr);

servaddr.sin_port= htons(SERV_PORT);


Connect(sockfd,(structsockaddr *)&servaddr,sizeof(servaddr));



while(fgets (buf,MAXLINE,stdin)!=NULL) {



Write(sockfd,buf,strlen(buf));


n= Read(sockfd,buf,MAXLINE);


printf("Responsefrom server:\n");

if(n == 0)

printf("theother side has been closed.\n");

else

Write(STDOUT_FILENO,buf, n);

// write(STDOUT_FILENO,buf, n);

// if(n!=Write(STDOUT_FILENO,buf,n))//buf写到标准输出中

// perror("write error");

}

close(sockfd);

return0;


}



编译:

gcc-c socket_wrap.c -o socket_wrap


gcc-c client.c -o client.o

gcc-c server.c -o server.o


gccsocket_wrap server.o -o server

gccsocket_wrap client.o -o client




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值