基于多线程的并发服务器(例子)

1创建一个socket
socket(套接字)实质上提供了进程通信的端点,进程通信之前双方首先必须建立各自的一个端点,否则是没有办法通信的。通过socket将IP地址和端口绑定之后,客户端就可以和服务器通信了。
创建一个套接字可以使用socket()函数
sys/socket。h
int socket(int domain,int type,int protocol)
返回值:成功则返回套接字文件描述符,失败返回-1
参数1:通信域,确定通信特性,包括地址格式
域 描述
AF_INET IPv4因特网域
AF_INET6 IPV6因特网域
AF_UNIX unix域
AF_UNSOEC 未指定
参数2:套接字类型
类型 描述
SOCK_DGRAM 长度固定的、无连接的不可靠报文传输
SOCK_RAW IP协议的数据报接口
SOCK_SEQPACKET 长度固定、有序、可靠的面向连接报文传递
SOCK_STREAM 有序、可靠、双向的面向连接的字节流()
参数3:指定相应的传输协议,也就是诸如TCP或UDP协议等等,系统针对每一个协议簇与类型提供了默认的协议,我们通过把protocol设置为0来使用这个默认值

2绑定IP地址和端口信息
使用bind()函数来把信息绑定到socket
在socket程序设计中,struct sockaddr_in用来记录网络地址
struct sockaddr_in
{
short int sin_family(协议簇)
unsigned short sin_port(端口号)
struct in_addr sin_addr(协议特定地址 IP地址)sockadr_in。sin_addr。s_addr=一个IP 可以使其等于INADDR_ANY这个宏实际为0,可以自动匹配本机IP地址
unsigned char sin_zero【8】;(填0)可以用memset置零
}

unsigned long s_addr;32位 的无符号整数 用这个定义IP
inet_addr是一个计算机函数,功能是将一个点分十进制的IP转换成一个长整数型数(u_long类型)等同于inet_addr()。

IP地址通常由数字加点(192.168.0.1 )的形式表示,而在struct_in_addr中使用的IP地址是由32位的整数表示,为了转换我们可以使用下面两个函数:
int inten_aton(const charcp,struct in_addrinp)
char*iner_ntoa(struct in_addr in)
函数里a代表ascii,n代表network,inte_aton是将 A.B.C.D形式IP转换成32位的IP存储在inp指针中。
inet_pton是一个IP地址转换函数,可以在将IP地址在“点分十进制”和“二进制整数”之间转换,而且inet_pton和inet_ntop这2个函数能够处理ipv4和ipv6。算是比较新的函数了。头文件#include <sys/socket.h>#include <netinet/in.h>#include<arpa/inet.h>
int inet_pton(int af, const char src, void dst);
这个函数转换字符串到网络地址,第一个参数af是地址簇,第二个参数
src是来源地址,第三个参数
dst接收转换后的数据。

字节序转换
不同类型的CPU对变量的字节存储顺序可能不同,有的系统高位在高地址低位在低地址(小端字节序),有的系统是低位在高地址,高位在低地址(大端字节序)而网络传输的数据顺序是一定要是统一的。所以当内部字节存储顺序和网络字节序不同时,就一定要进行转换

htons把unsigned short类型从主机序转换成网络序
htonl把unsigned long类型从主机序转换成网络序
ntohs把unsigned short类型从网络序转换成主机序
ntohl把unsigned long类型从网络序转换成主机序

设置好之后就开始绑定
bind

绑定服务器的地址和端口到socket,这样做就s让客户端来发现用以连接的服务器地址
int bind (int sockfd,const struct sockaddr*addr,socklen_t len)
返回值:成功返回0 失败返回-1
参数1:刚刚创建的socket的返回值
参数2:服务器地址,对于因特网域,如果设置地址为INADDR_ANY,套接字可以绑定到所有的网络端口。这意味着可以收到系统所有网卡的数据包,一般我们使用sockaddr_in类型的结构体代替sockaddr结构体
参数3:addr的长度

设置允许最大连接数
使用listen来宣告可以接受连接请求
int listen(int sockfd,mt backlog)
返回值成功返回0失败返回-1
参数backlog 用于表示服务器能接受的请求量

等待来自客户端的连接请求
一旦服务器调用了listen,套接字就能接收连接请求。使用函数accept函数来接受并建立请求
int accept(int sockfd,struct sockaddrrestrict addr,socklen_tresitrict len)
参数1:服务器socket
参数2:存放客户端的地址(自动回填)
参数3:addr 的长度
如果不关心客户端IP那么addr和len可设为NULL
返回值 成功返回套接字描述符,失败返回-1
注意
accept 返回一个 新得socket关联到客户端,他与原始的socket有相同的套接字类型和协议族,传递给accept的元素socket并没有关联客户端,他要继续保持可用状态,接收其他请求
2accept是一个阻塞函数,会一直等到有客户端的请求

收发数据用函数recv()send()/sendto()或者read()、write()
关闭网络连接close

客户端的创建
1、创建一个socket,使用函数socket
2、设置要连接的服务器地址和端口sockaddr_in结构体
3、连接服务器,使用connect()函数
4、收发数据用函数recv()send()/sendto()或者read()、write()
5、关闭网络连接close(socket描述符)

客户端与服务器之间是数据交换是双向的互不影响,因从需要两个单独的线程读写
服务器端的读线程对于客户端的写线程
服务器的写线程对应客户端读线程

发布了33 篇原创文章 · 获赞 4 · 访问量 2615

猜你喜欢

转载自blog.csdn.net/CNMNMSL1/article/details/104502084