socket代码
2010-04-18 16:34:09
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define MYPORT 1234//服务端端口
#define BACKLOG 5//最大连接数
#define BUF_SIZE 200
void mySocket() {
puts("server start....");
//建立一个socket通信
int sockFd;
if ((sockFd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
puts("建立一个socket通信");
/*int socket(int domain,int type,int protocol);
socket()用来建立一个新的socket,也就是向系统注册,
通知系统建立一通信端口。参数 domain 指定使用何种的地址类型,
完整的定义在/usr/include/bits/socket.h 内
domain:
PF_UNIX/PF_LOCAL/AF_UNIX/AF_LOCAL UNIX 进程通信协议
PF_INET?AF_INET Ipv4网络协议
PF_INET6/AF_INET6 Ipv6 网络协议
PF_IPX/AF_IPX IPX-Novell协议
PF_NETLINK/AF_NETLINK 核心用户接口装置
PF_X25/AF_X25 ITU-T X.25/ISO-8208 协议
PF_AX25/AF_AX25 业余无线AX.25协议
PF_ATMPVC/AF_ATMPVC 存取原始ATM PVCs
PF_APPLETALK/AF_APPLETALK appletalk(DDP)协议
PF_PACKET/AF_PACKET 初级封包接口
type:
SOCK_STREAM 提供双向连续且可信赖的数据流,即TCP。支持
OOB 机制,在所有数据传送前必须使用connect()来建立连线状态。
SOCK_DGRAM 使用不连续不可信赖的数据包连接
SOCK_SEQPACKET 提供连续可信赖的数据包连接
SOCK_RAW 提供原始网络协议存取
SOCK_RDM 提供可信赖的数据包连接
SOCK_PACKET 提供和网络驱动程序直接通信。
protocol:
用来指定socket所使用的传输协议编号,通常此参考不用管它,设为0即可。
返回值
成功则返回socket处理代码,失败返回-1。
*/
//设置套接口的选项
int yes = 1;
if (setsockopt(sockFd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
puts("设置套接口的选项");
/*
int setsockopt(int s,int level,int optname,const void * optval,socklen_toptlen);
s所指定的socket状态。参数level代表欲设置的网络层,一般设成SOL_SOCKET以存取socket层。参数optname代表欲设置的选项,有下列几种数值:
SO_DEBUG 打开或关闭排错模式
SO_REUSEADDR 允许在bind()过程中本地地址可重复使用
SO_TYPE 返回socket形态。
SO_ERROR 返回socket已发生的错误原因
SO_DONTROUTE 送出的数据包不要利用路由设备来传输。
SO_BROADCAST 使用广播方式传送
SO_SNDBUF 设置送出的暂存区大小
SO_RCVBUF 设置接收的暂存区大小
SO_KEEPALIVE 定期确定连线是否已终止。
SO_OOBINLINE 当接收到OOB 数据时会马上送至标准输入设备
SO_LINGER 确保数据安全且可靠的传送出去。
参数
optval代表欲设置的值,参数optlen则为optval的长度。
返回值
成功则返回0,若有错误则返回-1,错误原因存于errno。
setsockopt什么时候可用
如果是默认的简单TCP数据传输或者UDP广播的发送,不使用setsockopt完全没有问题,但是如果要进行更详细的设置(比如指定接收缓冲区的大小,指定传输时是否采用Nagle算法),则必须使用setsockopt。
*/
//Socket配置
struct sockaddr_in serverAddr;
/*
struct sockaddr_in {
short int sin_family;//sin_family指代协议族,在socket编程中只能是AF_INET
unsigned short int sin_port;//sin_port存储端口号(使用网络字节顺序)
struct in_addr sin_addr;//sin_addr存储IP地址,使用in_addr这个数据结构
unsigned char sin_zero[8];//sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
};
typedef struct in_addr {
union {
struct{
unsigned char s_b1,s_b2,s_b3,s_b4;
} S_un_b;
struct {
unsigned short s_w1,s_w2;
} S_un_w;
unsigned long S_addr;//s_addr按照网络字节顺序存储IP地址
} S_un;
} IN_ADDR;
*/
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(MYPORT);//htons函数用来转换u_short了来自主机的TCP / IP网络字节顺序
serverAddr.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或“所有地址”、“任意地址”。 一般来说,在各个系统中均定义成为0值。
bzero(&(serverAddr.sin_zero), 8);
//extern void bzero(void *s, int n);
//memset(server_addr.sin_zero, '/0', sizeof(serverAddr.sin_zero));
//置字节字符串s的前n个字节为零。
//bzero与memset比较,数组小的时候bzero性能高,数组越大memset性能越高
puts("socket配置");
//Socket绑定
if (bind(sockFd, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) {
perror("bind");
exit(1);
}
puts("socket绑定");
/*
bind(对socket定位)
int bind(int sockfd, struct sockaddr * my_addr, int addrlen);
强制转换
参数addrlen为sockaddr的结构长度。
返回值成功则返回0,失败返回-1,错误原因存于errno中。
*/
//监听
if (listen(sockFd, BACKLOG) == -1) {
perror("listen");
exit(1);
}
puts("监听");
/*
int listen(int s,int backlog);
listen()用来等待参数s 的socket连线。
参数backlog指定同时能处理的最大连接要求,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。
Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。
通常listen()会在socket(),bind()之后调用,接着才调用accept()。
返回值 成功则返回0,失败返回-1,错误原因存于errno
附加说明
listen()只适用SOCK_STREAM或SOCK_SEQPACKET的socket类型。如果socket为AF_INET则参数 backlog 最大值可设至128。
*/
socklen_t sinSize;
struct sockaddr_in clientAddr;
sinSize = sizeof(clientAddr);
int maxSock;
maxSock = sockFd;
struct timeval timeOut;
timeOut.tv_sec = 3;//select等待,要非阻塞就置0
timeOut.tv_usec = 0;
fd_set fdsr;
int ret;
int newSockFd = 0;
char buf[BUF_SIZE];
fflush(NULL);//清空缓冲区
while (true) {
FD_ZERO(&fdsr);//每次循环都要清空集合,否则不能检测描述符变化
FD_SET(sockFd, &fdsr);//添加描述符
ret = select(maxSock + 1, &fdsr, &fdsr, NULL, &timeOut);
if (ret < 0) {
perror("select");
break;
} else if (ret == 0) {
puts("timeout/n");
continue;
}
/*
非阻塞方式,select()
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
int maxfdp是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加1,不能错!
fd_set*readfds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的读变化的,
即我们关心是否可以从这些文件中读取数据了,如果这个集合中有一个文件可读,select就会返回一个大于0的值,
表示有文件可读,如果没有可读的文件,则根据 timeout参数再判断是否超时,若超出timeout的时间,select返回0,
若发生错误返回负值。可以传入NULL值,表示不关心任何文件的读变化。
fd_set*writefds是指向fd_set结构的指针,这个集合中应该包括文件描述符,我们是要监视这些文件描述符的写变化的,
即我们关心是否可以向这些文件中写入数据了,如果这个集合中有一个文件可写,select就会返回一个大于0 的值,
表示有文件可写,如果没有可写的文件,则根据timeout参数再判断是否超时,若超出timeout的时间,select返回0,
若发生错误返回负值。可以传入NULL值,表示不关心任何文件的写变化。
fd_set *errorfds同上面两个参数的意图,用来监视文件错误异常。
struct timeval *timeout是select的超时时间,这个参数至关重要,它可以使select处于三种状态,
第一,若将NULL以形参传入,即不传入时间结构,就是将select置于阻塞状态,一定等到监视文件描述符集合中某个文件描述符发生变化为止;
第二,若将时间值设为0秒0毫秒,就变成一个纯粹的非阻塞函数,不管文件描述符是否有变化,都立刻返回继续执行,文件无变化返回0,有变化返回一个正值;
第三,timeout的值大于0,这就是等待的超时时间,即 select在timeout时间内阻塞,超时时间之内有事件到来就返回了,否则在超时后不管怎样一定返回,返回值同上述。
返回值:
负值:select错误
正值:某些文件可读写或出错
0:等待超时,没有可读写或错误的文件
*/
if (FD_ISSET(sockFd, &fdsr)) {
newSockFd = accept(sockFd, (struct sockaddr*)&clientAddr, &sinSize);
if (newSockFd <= 0) {
perror("accept");
continue;
}
printf("一个客户端连接:%s/n", inet_ntoa(clientAddr.sin_addr));
}
/*
int accept(int s,struct sockaddr * addr,int * addrlen);
accept()用来接受参数s的socket连线。
addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。
addrlen:(可选)指针,指向存有addr地址长度的整形数。
返回值
成功则返回新的socket处理代码,失败返回-1,错误原因存于errno中。
*/
//接收数据
int recvRet;
recvRet = recv(newSockFd, buf, sizeof(buf), 0);
printf("客户端数据:%s/n", buf);
/*
int recv(int s,void *buf,int len,unsigned int flags);
recv()用来接收远端主机经指定的socket传来的数据,并把数据存到由参数buf 指向的内存空间,参数len为可接收数据的最大长度。
参数
flags一般设0。其他数值定义如下:
MSG_OOB 接收以out-of-band 送出的数据。
MSG_PEEK 返回来的数据并不会在系统内删除,如果再调用recv()会返回相同的数据内容。
MSG_WAITALL强迫接收到len大小的数据后才能返回,除非有错误或信号产生。
MSG_NOSIGNAL此操作不愿被SIGPIPE信号中断返回值成功则返回接收到的字符数,失败返回-1,错误原因存于errno中。
*/
char* msg = "Hello World";
int len = strlen(msg);
int successLen = send(newSockFd, msg, len, 0);
printf("实际传输的字符数:%i/n", successLen);
fflush(NULL);
/**
int send(int s,const void * msg,int len,unsigned int falgs);
参数s为已建立好连接的socket。
参数msg指向欲连线的数据内容,参数len则为数据长度。
参数flags一般设0,其他数值定义如下
MSG_OOB 传送的数据以out-of-band 送出。
MSG_DONTROUTE 取消路由表查询
MSG_DONTWAIT 设置为不可阻断运作
MSG_NOSIGNAL 此动作不愿被SIGPIPE 信号中断。
返回值
成功则返回实际传送出去的字符数,失败返回-1。错误原因存于errno
*/
}
//关闭Socket
close(sockFd);
}