您的位置:首页 > 理论基础 > 计算机网络

网络编程练习-TCP socket

2015-03-25 17:13 393 查看
一,一个简单的TCP c/s程序:

/*************************************************************
* file: tcp_socket.c
* brief:tcp demo program
* yejing@2015.3.25 1.0 creat
*************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>//struct sockaddr_in
#include <string.h>
#include <netdb.h>//getaddrinfo()
#include <arpa/inet.h>//inet_ntoa()

static void _get_addrinfo(struct addrinfo* paddrinfo){
if(!paddrinfo)
return;
char hostname[100];
gethostname(hostname, 100);
printf("host name is: %s \n", hostname);
getaddrinfo(hostname, NULL, NULL, &paddrinfo);

struct sockaddr_in* psockaddr = (struct sockaddr_in*)(paddrinfo->ai_addr);
char* pIp = inet_ntoa(psockaddr->sin_addr);
printf("host ip is: %s \n", pIp);

return;
}

#if defined(SERVER)
int main(int argc, char* argv[]){
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(!sockfd){
fprintf(stderr, "socket creat error. \n");
return -1;
}

int sockaddr_cli_len;
struct sockaddr_in sockaddr_cli;
memset((char*)&sockaddr_cli, 0, sizeof(struct sockaddr_in));

struct addrinfo _addrinfo;
memset((char*)&_addrinfo, 0, sizeof(_addrinfo));
_get_addrinfo(&_addrinfo);

struct sockaddr_in _sockaddr;
memset((char*)&_sockaddr, 0, sizeof(struct sockaddr_in));
_sockaddr.sin_family = AF_INET;
_sockaddr.sin_port = htons(9527);
struct sockaddr_in* sa_tmp = (struct sockaddr_in*)(_addrinfo.ai_addr);
_sockaddr.sin_addr.s_addr = sa_tmp->sin_addr.s_addr;

if(!(bind(sockfd, (const struct sockaddr*)&_sockaddr, sizeof(_sockaddr)))){
fprintf(stderr, "socket bind error. \n");
return -1;
}

if(listen(sockfd, 10) < 0){
fprintf(stderr, "socket listen error \n");
return -1;
}

sockaddr_cli_len = sizeof(sockaddr_cli);
int connfd = accept(sockfd, (struct sockaddr*)&sockaddr_cli, &sockaddr_cli_len);

while(1){
int n ;
char buf[128];
memset(buf, 0, sizeof(buf));
while((n = read(connfd, buf, 10)) > 0){
printf("read data: %s \n", buf);
break;
}
}

return 1;
}
#else//client
int main(int argc, char* argv[]){
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(!sockfd){
fprintf(stderr, "socket create error. \n");
return -1;
}

struct addrinfo _addrinfo;
memset((char*)&_addrinfo, 0, sizeof(_addrinfo));
_get_addrinfo(&_addrinfo);

struct sockaddr_in _sockaddr;
memset((char*)&_sockaddr, 0, sizeof(struct sockaddr_in));
_sockaddr.sin_family = AF_INET;
_sockaddr.sin_port = htons(9527);
struct sockaddr_in* sa_tmp = (struct sockaddr_in*)(_addrinfo.ai_addr);
_sockaddr.sin_addr.s_addr = sa_tmp->sin_addr.s_addr;

if(!(bind(sockfd, (const struct sockaddr*)&_sockaddr, sizeof(_sockaddr)))){
fprintf(stderr, "socket bind error. \n");
return -1;
}

int connfd = connect(sockfd, (struct sockaddr*)&_sockaddr, sizeof(_sockaddr));
if(!connfd){
fprintf(stderr, "socket connect error. \n");
return -1;
}

char tmp[10] = "asdfghjkl";
int ret = write(connfd, tmp, 10);
if(!ret){
fprintf(stderr, "data send error. \n");
return -1;
}

return 1;
}
#endif

二,TCP的带外数据:

带外数据(out-of-band data)是 很多传输层(UDP没有)都有的概念,也称经加速数据(expedited data)。

其核心作用是在于连接的某端发送了重要的事情,这些信息需要以更高优先级(要比已经排队等待发送的普通数据更快的发送给对端)发送出去。

带外数据并不要求在服务器和客户之间再建立一个连接,而是被映射到已有的连接中。

TCP并没有真正的带外数据,不过提供紧急模式(urgent mode)。
将send函数的最后一个flags参数设置为MSG_OOB,如:send(fd, “a”,1,MSG_OOB)即发送一个字节的带外数据。

实际的带外数据可能因为流量控制等原因无法送出,但是紧急通知还是会随TCP头部发出。

收端对于带外数据的处理如下:

1,当接收到一个设置了URG标志的分节时,接收端TCP检查紧急指针,确认它是否指向新的带外数据,

2,当有新的紧急指针到达时,如果接收task曾调用fcntl和ioctl为这个套接字建立属主的话,内核会给该套接字属主发送SIGURG信号。

      同时,如果接收进程阻塞在select调用中以等待这个套接字描述符出现一个异常条件,select调用返回。

3,当由紧急指针指向的实际数据字节到达接收端TCP时:

      如果SO_OOBINLINE禁止(默认禁止),该数据字节放入一个单独的单字节带外缓冲区。接收进程从这个单字节缓冲区中读取数据的唯一方法就是制定MSG_OOB标志调用recv系列函数,如果新的OOB字节在旧的OOB字节被读取之前到达,旧的被丢弃。

     如果SO_OOBINLINE打开,那么TCP紧急指针指向的实际数据将被留在通常的套接字接收缓冲区,这个时候MSG_OOB无法读到数据。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: