一、端口
1、0-1023:预留端口,超级用户使用
2、1024-49151:已经注册的端口号
3、49152-65535:可自由使用的端口或动态端口
二、套接字类型
1、SOCK_STREAM(字节流套接字):面向连接,可靠的全双工字节流。对于AF_INET来说,建立TCP连接
2、SOCK_DGRAM(数据报套接字):不保证数据正确分发,不保证发送数据的训序。对于AF_INET来说,建立UDP连接
3、SOCK_RAW(原始套接字)
三、建立套接字
int fd;
fd=socket(AF_INET,SOCK_STREAM,0);//除使用SOCK_RAW外,最后一个protocol被设置为0,AF_INET为套接口
四、应用
1.server
deepfuture@deepfuture-laptop:~/private/mytest$ ./testtcps
server wait....
............................
server read :myhaspl
server send :hello
.....
server read :myhaspl
server send :hello
2.client
deepfuture@deepfuture-laptop:~/private/mytest$ ./testtcpc
client send....
client send :myhaspl
client read :hello
deepfuture@deepfuture-laptop:~/private/mytest$ ./testtcpc
client send....
client send :myhaspl
client read :hello
deepfuture@deepfuture-laptop:~/private/mytest$
本博客所有内容是原创,如果转载请注明来源
http://blog.csdn.net/myhaspl/
3.source
1)server
C代码
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
//myhaspl
ssize_t readn(int fd,void *ptr,size_t maxcn){//读取n个字符,maxc为读取的数目
size_t noreadcn,readcn;
char *buf=ptr;
noreadcn=maxcn;
while(noreadcn>0){
if ( (readcn=read(fd,buf,noreadcn))<0){//读数据
if (errno==EINTR) {//数据读取前,操作被信号中断
perror("中断错误");
readcn=0;
}
else {return -1;}//无法修复错误,返回读取失败
}
else if(readcn==0) break;//EOF
noreadcn-=readcn;//读取成功,但是如果读取的字符数小于maxc,则继续读,因为可能数据还会继续通过网络送过来
buf+=readcn;
if (*buf==0) break;//如果读到字符串结尾标志则退出,必须有这句,否则会死循环
}
return (maxcn-noreadcn);
}
ssize_t writen(int fd,void *ptr,size_t maxcn){//写入n个字符
size_t nowritecn,writecn;
char *buf=ptr;
nowritecn=maxcn;
while(nowritecn>0){
if((writecn=write(fd,buf,nowritecn))<=0){//写数据
if (errno==EINTR) {//数据写前,操作被信号中断
perror("中断错误");
writecn=0;
}
else {return -1;}//无法修复错误,返回读取失败
}
nowritecn-=writecn;
buf+=writecn;
}
return (maxcn-nowritecn);
}
int main(void){
int fd;
int addresslen;
struct sockaddr_in address;//地址信息结构
int pid;
int rc;
fd_set fdset;
//建立socket
fd=socket(AF_INET,SOCK_STREAM,0);//fd为socket
if (fd==-1){//错误,类型从errno获得
perror("error");//perror先输出参数,后跟":"加空格,然后是errno值对应的错误信息(不是错误代码),最后是一个换行符。
}
//bind 到socket fd
address.sin_family=AF_INET;//IPV4协议,AF_INET6是IPV6
address.sin_addr.s_addr=htonl(INADDR_ANY);//l表示32位,htonl能保证在不同CPU的相同字节序
address.sin_port=htons(1253);//端口号,s表示16位
addresslen=sizeof(address);
bind(fd,(struct sockaddr *)&address,addresslen);//bind
//建立socket队列,指定最大可接受连接数
rc=listen(fd,32);//最多接收32个连接,开始监听
//int listen(int sockfd, int backlog)返回:0──成功, -1──失败
//内核会在自己的进程空间里维护一个队列以跟踪这些完成的连接但服务器进程还没有接手处理或正在进行的连接
if (rc==-1) {
perror("listen error");//监听失败
exit(1);
}
printf("server wait....\n");
while(1){
struct sockaddr_in clientaddress;
int address_len;
int client_sockfd;
char mybuf[100];
char *buf="hello\n";
struct timeval timeout;//超时结构体
//超时为2秒
timeout.tv_sec=1;
timeout.tv_usec=0;
//设置fdset
FD_ZERO(&fdset);//清除fdset
FD_CLR(fd,&fdset);//清除fd的标志
FD_SET(fd,&fdset);//设置标志
//select
if ((select(fd+1,&fdset,NULL,NULL,&timeout))<0){
perror("select error");
fflush(stdout);
}
//等待连接,使用新的进程或线程来处理连接
fflush(stdout);
address_len=sizeof(clientaddress);
if(FD_ISSET(fd,&fdset)){
//如果有连接到来
client_sockfd=accept(fd,(struct sockaddr *)&clientaddress,&address_len);//client_sockfd可理解为一个文件句柄,能用read和write操作。client_address是客户端信息结构 myhaspl
//fork进程来处理每个客户的连接
pid=fork();
if (pid<0){//错误
printf("error:%s\n",strerror(errno));//strerror将errno映射为一个错误信息串 myhaspl
close(client_sockfd);
exit(1);
}
if (pid==0){ //子进程处理每个客户端的数据
close(fd);//子进程关闭不需要它处理的监听资源
//读取数据 myhaspl
bzero(mybuf,100);
readn(client_sockfd,(void *)mybuf,100);
printf("\nserver read :%s",mybuf);
//发送数据
writen(client_sockfd,(void *)buf,strlen(buf)+1);
printf("\nserver send :%s",buf);
close(client_sockfd);
exit(0);
}
else {//父进程
close(client_sockfd);//父进程不处理客户端连接,因此关闭,但并不意味着把子进程的处理句柄关闭,因为子进程继承了父进程的client_sockfd资源 myhaspl
}
}else{
printf(".");
fflush(stdout);
}
}
}
2) client
C代码
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
//myhaspl
ssize_t readn(int fd,void *ptr,size_t maxcn){//读取n个字符,maxc为读取的数目
size_t noreadcn,readcn;
char *buf=ptr;
noreadcn=maxcn;
while(noreadcn>0){
if ( (readcn=read(fd,buf,noreadcn))<0){//读数据
if (errno==EINTR) {//数据读取前,操作被信号中断 myhaspl
perror("中断错误");
readcn=0;
}
else {return -1;}//无法修复错误,返回读取失败
}
else if(readcn==0) break;//EOF myhaspl
noreadcn-=readcn;//读取成功,但是如果读取的字符数小于maxc,则继续读,因为可能数据还会继续通过网络送过来
buf+=readcn;
if (*buf==0) break; //如果读到字符串结尾标志则退出,必须有这句,否则会死循环 myhaspl
}
return (maxcn-noreadcn);
}
ssize_t writen(int fd,void *ptr,size_t maxcn){//写入n个字符
size_t nowritecn,writecn;
char *buf=ptr;
nowritecn=maxcn;
while(nowritecn>0){
if((writecn=write(fd,buf,nowritecn))<=0){//写数据
if (errno==EINTR) {//数据写前,操作被信号中断
perror("中断错误");
writecn=0;
}
else {return -1;}//无法修复错误,返回读取失败
}
nowritecn-=writecn;
buf+=writecn;
}
return (maxcn-nowritecn);
}
int main(void){
int fd;
int addresslen;
struct sockaddr_in address;//地址信息结构 myhaspl
int pid;
char mybuf[100];
char *buf="myhaspl\n";
int rc;
fd=socket(AF_INET,SOCK_STREAM,0);//建立socket
if (fd==-1){//错误,类型从errno获得
perror("error");//perror先输出参数,后跟":"加空格,然后是errno值对应的错误信息(不是错误代码),最后是一个换行符。 myhaspl
}
printf("client send....\n");
fflush(stdout);
//连接
address.sin_family=AF_INET;//IPV4协议,AF_INET6是IPV6 myhaspl
address.sin_addr.s_addr=inet_addr("127.0.0.1");//l表示32位,htonl能保证在不同CPU的相同字节序
address.sin_port=htons(1253);//端口号,s表示16位 myhaspl
addresslen=sizeof(address);
rc=connect(fd,(struct sockaddr *)&address,addresslen);//连接服务器 myhaspl
if (rc==-1){//rc=0成功,rc=-1失败 myhaspl
perror("连接错误");
exit(1);
}
//发送数据
writen(fd,(void *)buf,strlen(buf)+1);
printf("client send :%s\n",buf);
//读取数据
bzero(mybuf,100);
readn(fd,(void *)mybuf,100);
printf("client read :%s\n",mybuf);
close(fd);
exit(0);
}