一、TCP 服务端的编程
1. int socket(int domain,int type ,int protocol)
domain:是说我们网络程序采用的通讯协议族,一般我们填AF_INET
type: 我们程序采用的通讯协议:SOCK_STREAM是TCP, SOCK_DGRAM是UDP,及数据报
protocol:因为我们使用了type,这里填0就可以了
2.int bind(int sockfd,struct sockaddr *my_addr,int addrlen)
sockfd:是由socket调用返回的文件描述符
struct sockaddr{
unsigned short as_family;
char sa_data[14];
};
不过由于系统的兼容性,我们一般不用这个,而用另外一个结构体(struct sockaddr_in)来代替。
struct sockaddr_in{
unsigned short sin_family;
unsigned short int sin_port;
struct in_addr sin_addr;
unsigned char sin_zero[8];
};
struct in_addr{
in_addr_t s_addr; //IPV4地址
};
sin_family一般为AF_INET,sin_addr.s_addr设置为INADDR_ANY,表示可以和任何计算机通信,sin_port是我们要监听的端口。成功返回0
addrlen表示sockaddr结构的长度
bind(sockfd,(struct sockaddr*)&my_addr,sizeof(struct sockaddr))
3.int listen(int sockfd,int backlog)
sockfd,是bind后的sockfd
backlog:设置请求排队的最大长度,支持多少个客户同时连接
4.int accept (int sockfd,struct sockaddr *addr,int *addrlen)
sockfd: 是listen后的文件描述符
struct sockaddr和addrlen是调用此函数中,把客户端的信息填充进去,这样我们就知道是哪个客户端在连接我们。
返回的自然是跟客户端通信的套接字了。newsock
5.收int recv(int sockfd,void*buf,int len,unsigned int flags)
flags一般为0
6.int send(int sockfd,const void*msg,int len,int flags)
flags一般为0
二、TCP客户端编程
1.sockfd= socket(AF_INET,SOCK_STREAM,0)
2.填充将要连接的服务端的地址信息
struct sockaddr_in their_addr;
their_addr.sin_family=AF_INET;
their_addr.sin_port=htons(2323); //对方使用2323进行通信
inet_aton("192.168.40.44",&their_addr.sin_addr); //int inet_aton(const char*string,struct in_addr* addr);
3.建立连接
connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr))
4.int send(int sockfd,const void*msg,int len,int flags)
flags一般为0
5.收int recv(int sockfd,void*buf,int len,unsigned int flags)
flags一般为0
三、UDP服务端编程
1.sockfd= socket(AF_INET,SOCK_DGRAM,0)
2.填充和绑定.int bind(int sockfd,struct sockaddr *my_addr,int addrlen);不懂参照上面的TCP服务端
3.int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr* from,int *fromlen)
flags一般为0
4.int sendto(int sockfd,const void* msg,int len,unsigned int flags,const strcut sockaddr* to,int tolen);
四、UDP客户端编程
1.sockfd= socket(AF_INET,SOCK_DGRAM,0);
2.填充服务端的地址信息struct sockaddr_in* to
3.int sendto(int sockfd,const void* msg,int len,unsigned int flags,const strcut sockaddr* to,int tolen);
4.int recvfrom(int sockfd,void *buf,int len,unsigned int flags,struct sockaddr* from,int *fromlen)
flags一般为0
五.上述是linux常用的TCP和UDP的编程顺序。接下来是对一些常用的函数的使用和解释
#include<netinet/in.h>
1.uint16_t htons(unit16_t host16bit)
uint32_t htonl(unit32_t host32bit)
uinit16_t ntohs(unit16_t net16it)
uint32_t ntohs(unit32_t net32bit);
2.int inet_aton(const char*string,struct in_addr* addr); 把点分十进制的IP转化为网络字节的IP
char* inet_ntoa(struct in_addr);把 网络字节的IP转化为点分十进制的IP
还有 用于IPV6的inet_pton和inet_ntop,当然也可以用于IPV4
3.struct hostent* gethostbyname(const char* hostname)
hostname:主机名
eg.h=gethostbyname(argv[1]))
- struct addrinfo {
- int ai_flags; /* customize behavior */
- int ai_family; /* address family */
- int ai_socktype; /* socket type */
- int ai_protocol; /* protocol */
- socklen_t ai_addrlen; /* length in bytes of address */
- struct sockaddr *ai_addr; /* address */
- char *ai_canonname; /* canonical name of host */
- struct addrinfo *ai_next; /* next in list */
- .
- .
- .
- }
六、高级编程,或者叫实用编程
因为我们以上connect(),recv()和send()等都是阻塞性函数,如果资源没有准备好,进程就会进入睡觉,这样就无法处理I/O多路复用了,或者没法一个线程处理了。
以下有几种方法解决这个问题:
1.setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&i,sizeof(i)) //允许重复使用本地地址与套接字进行绑定
flags = fcntl(sockfd,F_GETFL);
fcntl(sockfd,FSETEL,flags|O_NONBLOCK)
这样调用accept()没有资源可利用也不会卡住了。
2.fd_set inset,tmp_inset;
struct timeval tv;
tv.sec=
tv.usec=
( setsockopt( *fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags) )
( setsockopt( *fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags) )
FD_ZERO(&inset) 先清空集中的描述符
FD_SET(sockfd1,&inset);
FD_SET(sockfd2,&inset);
FD_SET(sockfd3,&inset);
while(1)
count=select(MAX_SOCK_FD+1,&inset,NULL,NULL,&tv);
if(FD_ISSET(sockfd1,&inset)>0)
{
accept()
recv
send
}
if(FD_ISSET(sockfd2,&inset)>0)
{
accept()
recv
send
}
close(sock1)
close(sock2)
close(sock3)